am b73408d1: am fdb708a3: P2P: Validate SSID element length before copying it
* commit 'b73408d1cfdbad09b1d470e8896e2abf7077d702':
P2P: Validate SSID element length before copying it
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS
new file mode 100644
index 0000000..d20a556
--- /dev/null
+++ b/CONTRIBUTIONS
@@ -0,0 +1,143 @@
+Contributions to hostap.git
+---------------------------
+
+This software is distributed under a permissive open source license to
+allow it to be used in any projects, whether open source or proprietary.
+Contributions to the project are welcome and it is important to maintain
+clear record of contributions and terms under which they are licensed.
+To help with this, following procedure is used to allow acceptance and
+recording of the terms.
+
+All contributions are expected to be licensed under the modified BSD
+license (see below). Acknowledgment of the terms is tracked through
+inclusion of Signed-off-by tag in the contributions at the end of the
+commit log message. This tag indicates that the contributor agrees with
+the Developer Certificate of Origin (DCO) version 1.1 terms (see below;
+also available from http://developercertificate.org/).
+
+
+The current requirements for contributions to hostap.git
+--------------------------------------------------------
+
+To indicate your acceptance of Developer's Certificate of Origin 1.1
+terms, please add the following line to the end of the commit message
+for each contribution you make to the project:
+
+Signed-off-by: Your Name <your@email.example.org>
+
+using your real name. Pseudonyms or anonymous contributions cannot
+unfortunately be accepted.
+
+
+History of license and contributions terms
+------------------------------------------
+
+Until February 11, 2012, in case of most files in hostap.git, "under the
+open source license indicated in the file" means that the contribution
+is licensed both under GPL v2 and modified BSD license (see below) and
+the choice between these licenses is given to anyone who redistributes
+or uses the software. As such, the contribution has to be licensed under
+both options to allow this choice.
+
+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 and the contributions are not required to be
+licensed until GPL v2. In case of most files in hostap.git, "under the
+open source license indicated in the file" means that the contribution
+is licensed under the modified BSD license (see below).
+
+Until February 13, 2014, the project used an extended version of the DCO
+that included the identical items (a) through (d) from DCO 1.1 and an
+additional item (e):
+
+(e) The contribution can be licensed under the modified BSD license
+ as shown below even in case of files that are currently licensed
+ under other terms.
+
+This was used during the period when some of the files included the old
+license terms. Acceptance of this extended DCO version was indicated
+with a Signed-hostap tag in the commit message. This additional item (e)
+was used to collect explicit approval to license the contribution with
+only the modified BSD license (see below), i.e., without the GPL v2
+option. This was done to allow simpler licensing terms to be used in the
+future. It should be noted that the modified BSD license is compatible
+with GNU GPL and as such, this possible move to simpler licensing option
+does not prevent use of this software in GPL projects.
+
+
+===[ start quote from http://developercertificate.org/ ]=======================
+
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+660 York Street, Suite 102,
+San Francisco, CA 94110 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
+
+===[ end quote from http://developercertificate.org/ ]=========================
+
+
+The license terms used for hostap.git files
+-------------------------------------------
+
+Modified BSD license (no advertisement clause):
+
+Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors
+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. 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.
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 7ecbc65..dc7d557 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -59,3 +59,6 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/hostapd_intermediates/*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/wpa_supplicant_intermediates/*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/hostapd_intermediates/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/wpa_supplicant_intermediates/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/hostapd_intermediates/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/hostapd_intermediates/*)
diff --git a/README b/README
index 805c6cf..8de14a6 100644
--- a/README
+++ b/README
@@ -1,12 +1,15 @@
wpa_supplicant and hostapd
--------------------------
-Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> 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
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 695e99c..edaf6fc 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -24,16 +24,13 @@
# Set Android log name
L_CFLAGS += -DANDROID_LOG_NAME=\"hostapd\"
-ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
-L_CFLAGS += -DANDROID_P2P
-endif
+# Disable unused parameter warnings
+L_CFLAGS += -Wno-unused-parameter
-ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
+# Set Android extended P2P functionality
L_CFLAGS += -DANDROID_P2P
-endif
-
-ifeq ($(BOARD_WLAN_DEVICE), mrvl)
-L_CFLAGS += -DANDROID_P2P
+ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+L_CFLAGS += -DANDROID_P2P_STUB
endif
# Use Android specific directory for control interface sockets
@@ -45,17 +42,18 @@
L_CFLAGS += -mabi=aapcs-linux
endif
-# To allow non-ASCII characters in SSID
-L_CFLAGS += -DWPA_UNICODE_SSID
-
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
+ifneq ($(wildcard external/libnl),)
+INCLUDES += external/libnl/include
+else
INCLUDES += external/libnl-headers
endif
+endif
ifndef CONFIG_OS
@@ -108,7 +106,6 @@
NEED_AES=y
NEED_MD5=y
NEED_SHA1=y
-NEED_SHA256=y
OBJS += src/drivers/drivers.c
L_CFLAGS += -DHOSTAPD
@@ -141,10 +138,10 @@
ifndef CONFIG_NO_DUMP_STATE
-# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to
-# a file (undefine it, if you want to save in binary size)
+# 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 += dump_state.c
OBJS += src/eapol_auth/eapol_auth_dump.c
endif
@@ -201,6 +198,10 @@
OBJS += src/ap/peerkey_auth.c
endif
+ifdef CONFIG_HS20
+NEED_AES_OMAC1=y
+endif
+
ifdef CONFIG_IEEE80211W
L_CFLAGS += -DCONFIG_IEEE80211W
NEED_SHA256=y
@@ -355,7 +356,7 @@
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_SERVER_GPSK_SHA256
+L_CFLAGS += -DEAP_GPSK_SHA256
endif
NEED_SHA256=y
NEED_AES_OMAC1=y
@@ -389,10 +390,6 @@
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
@@ -531,15 +528,12 @@
ifdef TLS_FUNCS
OBJS += src/crypto/tls_gnutls.c
LIBS += -lgnutls -lgpg-error
-ifdef CONFIG_GNUTLS_EXTRA
-L_CFLAGS += -DCONFIG_GNUTLS_EXTRA
-LIBS += -lgnutls-extra
-endif
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
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
endif
LIBS += -lgcrypt
LIBS_h += -lgcrypt
@@ -566,7 +560,8 @@
endif
OBJS += src/crypto/crypto_nss.c
ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_nss.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
endif
LIBS += -lnss3
LIBS_h += -lnss3
@@ -831,6 +826,7 @@
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
@@ -860,10 +856,20 @@
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
@@ -884,12 +890,6 @@
OBJS_c += src/utils/edit_simple.c
endif
-ifdef CONFIG_ACS
-L_CFLAGS += -DCONFIG_ACS
-OBJS += src/ap/acs.c
-LIBS += -lm
-endif
-
########################
include $(CLEAR_VARS)
@@ -913,8 +913,12 @@
endif
LOCAL_SHARED_LIBRARIES := libc libcutils liblog libcrypto libssl
ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+LOCAL_SHARED_LIBRARIES += libnl
+else
LOCAL_STATIC_LIBRARIES += libnl_2
endif
+endif
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := $(OBJS)
LOCAL_C_INCLUDES := $(INCLUDES)
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index 1a4e566..9de9438 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,8 +1,127 @@
ChangeLog for hostapd
-????-??-?? - v2.1
- * added support for simulataneous authentication of equals (SAE) for
+2014-06-04 - v2.2
+ * fixed SAE confirm-before-commit validation to avoid a potential
+ segmentation fault in an unexpected message sequence that could be
+ triggered remotely
+ * extended VHT support
+ - Operating Mode Notification
+ - Power Constraint element (local_pwr_constraint)
+ - Spectrum management capability (spectrum_mgmt_required=1)
+ - fix VHT80 segment picking in ACS
+ - fix vht_capab 'Maximum A-MPDU Length Exponent' handling
+ - fix VHT20
+ * fixed HT40 co-ex scan for some pri/sec channel switches
+ * extended HT40 co-ex support to allow dynamic channel width changes
+ during the lifetime of the BSS
+ * fixed HT40 co-ex support to check for overlapping 20 MHz BSS
+ * fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding;
+ this fixes password with include UTF-8 characters that use
+ three-byte encoding EAP methods that use NtPasswordHash
+ * reverted TLS certificate validation step change in v2.1 that rejected
+ any AAA server certificate with id-kp-clientAuth even if
+ id-kp-serverAuth EKU was included
+ * fixed STA validation step for WPS ER commands to prevent a potential
+ crash if an ER sends an unexpected PutWLANResponse to a station that
+ is disassociated, but not fully removed
+ * enforce full EAP authentication after RADIUS Disconnect-Request by
+ removing the PMKSA cache entry
+ * added support for NAS-IP-Address, NAS-identifier, and NAS-IPv6-Address
+ in RADIUS Disconnect-Request
+ * added mechanism for removing addresses for MAC ACLs by prefixing an
+ entry with "-"
+ * Interworking/Hotspot 2.0 enhancements
+ - support Hotspot 2.0 Release 2
+ * OSEN network for online signup connection
+ * subscription remediation (based on RADIUS server request or
+ control interface HS20_WNM_NOTIF for testing purposes)
+ * Hotspot 2.0 release number indication in WFA RADIUS VSA
+ * deauthentication request (based on RADIUS server request or
+ control interface WNM_DEAUTH_REQ for testing purposes)
+ * Session Info URL RADIUS AVP to trigger ESS Disassociation Imminent
+ * hs20_icon config parameter to configure icon files for OSU
+ * osu_* config parameters for OSU Providers list
+ - do not use Interworking filtering rules on Probe Request if
+ Interworking is disabled to avoid interop issues
+ * added/fixed nl80211 functionality
+ - AP interface teardown optimization
+ - support vendor specific driver command
+ (VENDOR <vendor id> <sub command id> [<hex formatted data>])
+ * fixed PMF protection of Deauthentication frame when this is triggered
+ by session timeout
+ * internal TLS implementation enhancements/fixes
+ - add SHA256-based cipher suites
+ - add DHE-RSA cipher suites
+ - fix X.509 validation of PKCS#1 signature to check for extra data
+ * RADIUS server functionality
+ - add minimal RADIUS accounting server support (hostapd-as-server);
+ this is mainly to enable testing coverage with hwsim scripts
+ - allow authentication log to be written into SQLite databse
+ - added option for TLS protocol testing of an EAP peer by simulating
+ various misbehaviors/known attacks
+ - MAC ACL support for testing purposes
+ * fixed PTK derivation for CCMP-256 and GCMP-256
+ * extended WPS per-station PSK to support ER case
+ * added option to configure the management group cipher
+ (group_mgmt_cipher=AES-128-CMAC (default), BIP-GMAC-128, BIP-GMAC-256,
+ BIP-CMAC-256)
+ * fixed AP mode default TXOP Limit values for AC_VI and AC_VO (these
+ were rounded incorrectly)
+ * added support for postponing FT response in case PMK-R1 needs to be
+ pulled from R0KH
+ * added option to advertise 40 MHz intolerant HT capability with
+ ht_capab=[40-INTOLERANT]
+ * remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled
+ whenever CONFIG_WPS=y is set
+ * EAP-pwd fixes
+ - fix possible segmentation fault on EAP method deinit if an invalid
+ group is negotiated
+ * fixed RADIUS client retransmit/failover behavior
+ - there was a potential ctash due to freed memory being accessed
+ - failover to a backup server mechanism did not work properly
+ * fixed a possible crash on double DISABLE command when multiple BSSes
+ are enabled
+ * fixed a memory leak in SAE random number generation
+ * fixed GTK rekeying when the station uses FT protocol
+ * fixed off-by-one bounds checking in printf_encode()
+ - this could result in deinial of service in some EAP server cases
+ * various bug fixes
+
+2014-02-04 - v2.1
+ * added support for simultaneous authentication of equals (SAE) for
stronger password-based authentication with WPA2-Personal
+ * added nl80211 functionality
+ - VHT configuration for nl80211
+ - support split wiphy dump
+ - driver-based MAC ACL
+ - QoS Mapping configuration
+ * added fully automated regression testing with mac80211_hwsim
+ * allow ctrl_iface group to be specified on command line (-G<group>)
+ * 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<phyname>:<config file name>
+ * 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
diff --git a/hostapd/Makefile b/hostapd/Makefile
index fda4e4e..ac6373e 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -6,8 +6,8 @@
CFLAGS = -MMD -O2 -Wall -g
endif
-CFLAGS += -I../src
-CFLAGS += -I../src/utils
+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.
@@ -15,6 +15,11 @@
-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
@@ -65,6 +70,11 @@
OBJS += ../src/drivers/drivers.o
CFLAGS += -DHOSTAPD
+ifdef CONFIG_MODULE_TESTS
+CFLAGS += -DCONFIG_MODULE_TESTS
+OBJS += hapd_module_tests.o
+endif
+
ifdef CONFIG_WPA_TRACE
CFLAGS += -DWPA_TRACE
OBJS += ../src/utils/trace.o
@@ -72,10 +82,10 @@
LDFLAGS += -rdynamic
CFLAGS += -funwind-tables
ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DWPA_TRACE_BFD
-LIBS += -lbfd
-LIBS_c += -lbfd
-LIBS_h += -lbfd
+CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD
+LIBS += -lbfd -ldl -liberty -lz
+LIBS_c += -lbfd -ldl -liberty -lz
+LIBS_h += -lbfd -ldl -liberty -lz
endif
endif
@@ -84,6 +94,15 @@
endif
OBJS += ../src/utils/$(CONFIG_ELOOP).o
OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
+
+ifeq ($(CONFIG_ELOOP), eloop)
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+LIBS_c += -lrt
+LIBS_h += -lrt
+LIBS_n += -lrt
+endif
+
OBJS += ../src/utils/common.o
OBJS += ../src/utils/wpa_debug.o
OBJS_c += ../src/utils/wpa_debug.o
@@ -97,11 +116,19 @@
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 SIGUSR1 handler for dumping state to
-# a file (undefine it, if you want to save in binary size)
+# 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 += dump_state.o
OBJS += ../src/eapol_auth/eapol_auth_dump.o
endif
@@ -158,6 +185,10 @@
OBJS += ../src/ap/peerkey_auth.o
endif
+ifdef CONFIG_HS20
+NEED_AES_OMAC1=y
+endif
+
ifdef CONFIG_IEEE80211W
CFLAGS += -DCONFIG_IEEE80211W
NEED_SHA256=y
@@ -311,7 +342,7 @@
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_SERVER_GPSK_SHA256
+CFLAGS += -DEAP_GPSK_SHA256
endif
NEED_SHA256=y
NEED_AES_OMAC1=y
@@ -345,10 +376,6 @@
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
@@ -491,7 +518,8 @@
OBJS += ../src/crypto/crypto_gnutls.o
HOBJS += ../src/crypto/crypto_gnutls.o
ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_gnutls.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
endif
LIBS += -lgcrypt
LIBS_h += -lgcrypt
@@ -518,7 +546,8 @@
endif
OBJS += ../src/crypto/crypto_nss.o
ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_nss.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
endif
LIBS += -lnss3
LIBS_h += -lnss3
@@ -782,6 +811,7 @@
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
@@ -827,6 +857,10 @@
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
@@ -837,10 +871,6 @@
LIBS_h += -lsqlite3
endif
-ifdef CONFIG_TESTING_OPTIONS
-CFLAGS += -DCONFIG_TESTING_OPTIONS
-endif
-
ALL=hostapd hostapd_cli
all: verify_config $(ALL)
@@ -852,9 +882,15 @@
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 \
@@ -923,9 +959,15 @@
$(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
+ rm -f *.d *.gcno *.gcda *.gcov
+ rm -f lcov.info
+ rm -rf lcov-html
-include $(OBJS:%.o=%.d)
diff --git a/hostapd/README b/hostapd/README
index 39b70ca..50868ee 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -2,7 +2,7 @@
Authenticator and RADIUS authentication server
================================================================
-Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
diff --git a/hostapd/README-WPS b/hostapd/README-WPS
index 654b5bc..bb7d35f 100644
--- a/hostapd/README-WPS
+++ b/hostapd/README-WPS
@@ -63,7 +63,6 @@
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:
diff --git a/hostapd/android.config b/hostapd/android.config
index f51a5bf..ad83308 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -52,7 +52,7 @@
# 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
+CONFIG_IEEE80211W=y
# Integrated EAP server
#CONFIG_EAP=y
@@ -108,8 +108,6 @@
# 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
@@ -157,7 +155,7 @@
# Remove support for VLANs
#CONFIG_NO_VLAN=y
-# Remove support for dumping state into a file on SIGUSR1 signal
+# 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
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 0b4fd77..4c0e3f8 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration file parser
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -22,7 +22,12 @@
#include "config_file.h"
-extern struct wpa_driver_ops *wpa_drivers[];
+#ifndef CONFIG_NO_RADIUS
+#ifdef EAP_SERVER
+static struct hostapd_radius_attr *
+hostapd_parse_radius_attr(const char *value);
+#endif /* EAP_SERVER */
+#endif /* CONFIG_NO_RADIUS */
#ifndef CONFIG_NO_VLAN
@@ -132,6 +137,8 @@
}
while (fgets(buf, sizeof(buf), f)) {
+ int i, rem = 0;
+
line++;
if (buf[0] == '#')
@@ -146,14 +153,32 @@
}
if (buf[0] == '\0')
continue;
+ pos = buf;
+ if (buf[0] == '-') {
+ rem = 1;
+ pos++;
+ }
- if (hwaddr_aton(buf, addr)) {
+ if (hwaddr_aton(pos, addr)) {
wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at "
- "line %d in '%s'", buf, line, fname);
+ "line %d in '%s'", pos, line, fname);
fclose(f);
return -1;
}
+ if (rem) {
+ i = 0;
+ while (i < *num) {
+ if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) ==
+ 0) {
+ os_remove_in_array(*acl, *num,
+ sizeof(**acl), i);
+ (*num)--;
+ } else
+ i++;
+ }
+ continue;
+ }
vlan_id = 0;
pos = buf;
while (*pos != '\0' && *pos != ' ' && *pos != '\t')
@@ -191,7 +216,7 @@
FILE *f;
char buf[512], *pos, *start, *pos2;
int line = 0, ret = 0, num_methods;
- struct hostapd_eap_user *user, *tail = NULL;
+ struct hostapd_eap_user *user = NULL, *tail = NULL;
if (!fname)
return 0;
@@ -225,6 +250,28 @@
if (buf[0] == '\0')
continue;
+#ifndef CONFIG_NO_RADIUS
+ if (user && os_strncmp(buf, "radius_accept_attr=", 19) == 0) {
+ struct hostapd_radius_attr *attr, *a;
+ attr = hostapd_parse_radius_attr(buf + 19);
+ if (attr == NULL) {
+ wpa_printf(MSG_ERROR, "Invalid radius_auth_req_attr: %s",
+ buf + 19);
+ user = NULL; /* already in the BSS list */
+ goto failed;
+ }
+ if (user->accept_attr == NULL) {
+ user->accept_attr = attr;
+ } else {
+ a = user->accept_attr;
+ while (a->next)
+ a = a->next;
+ a->next = attr;
+ }
+ continue;
+ }
+#endif /* CONFIG_NO_RADIUS */
+
user = NULL;
if (buf[0] != '"' && buf[0] != '*') {
@@ -319,6 +366,10 @@
EAP_TTLS_AUTH_MSCHAPV2;
goto skip_eap;
}
+ if (os_strcmp(start, "MACACL") == 0) {
+ user->macacl = 1;
+ goto skip_eap;
+ }
wpa_printf(MSG_ERROR, "Unsupported EAP type "
"'%s' on line %d in '%s'",
start, line, fname);
@@ -333,7 +384,7 @@
break;
start = pos3;
}
- if (num_methods == 0 && user->ttls_auth == 0) {
+ if (num_methods == 0 && user->ttls_auth == 0 && !user->macacl) {
wpa_printf(MSG_ERROR, "No EAP types configured on "
"line %d in '%s'", line, fname);
goto failed;
@@ -451,11 +502,8 @@
continue;
failed:
- if (user) {
- os_free(user->password);
- os_free(user->identity);
- os_free(user);
- }
+ if (user)
+ hostapd_config_free_eap_user(user);
ret = -1;
break;
}
@@ -748,30 +796,32 @@
static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname)
{
- struct hostapd_bss_config *bss;
+ struct hostapd_bss_config **all, *bss;
if (*ifname == '\0')
return -1;
- bss = os_realloc_array(conf->bss, conf->num_bss + 1,
- sizeof(struct hostapd_bss_config));
- if (bss == NULL) {
+ 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 = bss;
+ conf->bss = all;
- bss = &(conf->bss[conf->num_bss]);
- os_memset(bss, 0, sizeof(*bss));
+ 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->num_bss++;
+ conf->bss[conf->num_bss++] = bss;
conf->last_bss = bss;
hostapd_config_defaults_bss(bss);
@@ -1019,8 +1069,8 @@
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, "[40-INTOLERANT]"))
+ conf->ht_capab |= HT_CAP_INFO_40MHZ_INTOLERANT;
if (os_strstr(capab, "[LSIG-TXOP-PROT]"))
conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT;
@@ -1041,8 +1091,6 @@
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]"))
@@ -1060,15 +1108,15 @@
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_MU_BEAMFORMER_CAPABLE;
+ conf->vht_capab |= VHT_CAP_SU_BEAMFORMER_CAPABLE;
if (os_strstr(capab, "[SU-BEAMFORMEE]"))
- conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE;
if (os_strstr(capab, "[BF-ANTENNA-2]") &&
- (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
- conf->vht_capab |= VHT_CAP_BEAMFORMER_ANTENNAS_MAX;
+ (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_MU_BEAMFORMER_CAPABLE))
- conf->vht_capab |= VHT_CAP_SOUNDING_DIMENTION_MAX;
+ (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]"))
@@ -1077,8 +1125,20 @@
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, "[MAX-A-MPDU-LEN-EXP7]"))
+ conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX;
+ else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP6]"))
+ conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6;
+ else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP5]"))
+ conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5;
+ else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP4]"))
+ conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4;
+ else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP3]"))
+ conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3;
+ else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP2]"))
+ conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2;
+ else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP1]"))
+ conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1;
if (os_strstr(capab, "[VHT-LINK-ADAPT2]") &&
(conf->vht_capab & VHT_CAP_HTC_VHT))
conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB;
@@ -1094,165 +1154,6 @@
#endif /* CONFIG_IEEE80211AC */
-static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
- struct hostapd_config *conf)
-{
- if (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 (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 (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 (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 (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 (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 (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 (conf->ieee80211n && bss->wpa &&
- !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
- !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))) {
- 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 (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 (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 (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 (bss->hs20 &&
- (!(bss->wpa & 2) ||
- !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
- 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;
-}
-
-
-static int hostapd_config_check(struct hostapd_config *conf)
-{
- size_t i;
-
- if (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 (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))
- return -1;
- }
-
- return 0;
-}
-
-
#ifdef CONFIG_INTERWORKING
static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
int line)
@@ -1289,26 +1190,34 @@
static int parse_lang_string(struct hostapd_lang_string **array,
unsigned int *count, char *pos)
{
- char *sep;
- size_t clen, nlen;
+ 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)
- return -1;
+ goto fail;
*sep++ = '\0';
clen = os_strlen(pos);
- if (clen < 2)
- return -1;
+ if (clen < 2 || clen > sizeof(ls->lang))
+ goto fail;
nlen = os_strlen(sep);
if (nlen > 252)
- return -1;
+ goto fail;
ls = os_realloc_array(*array, *count + 1,
sizeof(struct hostapd_lang_string));
if (ls == NULL)
- return -1;
+ goto fail;
*array = ls;
ls = &(*array)[*count];
@@ -1319,7 +1228,10 @@
ls->name_len = nlen;
os_memcpy(ls->name, sep, nlen);
- return 0;
+ ret = 0;
+fail:
+ os_free(str);
+ return ret;
}
@@ -1346,7 +1258,7 @@
count = 1;
for (pos = buf; *pos; pos++) {
- if ((*pos < '0' && *pos > '9') && *pos != ';' && *pos != ',')
+ if ((*pos < '0' || *pos > '9') && *pos != ';' && *pos != ',')
goto fail;
if (*pos == ';')
count++;
@@ -1550,6 +1462,47 @@
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 */
@@ -1647,7 +1600,7 @@
fail:
wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'",
- line, pos);
+ line, buf);
os_free(wan_metrics);
return -1;
}
@@ -1664,6 +1617,197 @@
}
return 0;
}
+
+
+static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
+{
+ struct hs20_icon *icon;
+ char *end;
+
+ icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
+ sizeof(struct hs20_icon));
+ if (icon == NULL)
+ return -1;
+ bss->hs20_icons = icon;
+ icon = &bss->hs20_icons[bss->hs20_icons_count];
+ os_memset(icon, 0, sizeof(*icon));
+
+ icon->width = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ pos++;
+
+ icon->height = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ pos++;
+
+ end = os_strchr(pos, ':');
+ if (end == NULL || end - pos > 3)
+ return -1;
+ os_memcpy(icon->language, pos, end - pos);
+ pos = end + 1;
+
+ end = os_strchr(pos, ':');
+ if (end == NULL || end - pos > 255)
+ return -1;
+ os_memcpy(icon->type, pos, end - pos);
+ pos = end + 1;
+
+ end = os_strchr(pos, ':');
+ if (end == NULL || end - pos > 255)
+ return -1;
+ os_memcpy(icon->name, pos, end - pos);
+ pos = end + 1;
+
+ if (os_strlen(pos) > 255)
+ return -1;
+ os_memcpy(icon->file, pos, os_strlen(pos));
+
+ bss->hs20_icons_count++;
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ size_t slen;
+ char *str;
+
+ str = wpa_config_parse_string(pos, &slen);
+ if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
+ os_free(str);
+ return -1;
+ }
+
+ os_memcpy(bss->osu_ssid, str, slen);
+ bss->osu_ssid_len = slen;
+ os_free(str);
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ struct hs20_osu_provider *p;
+
+ p = os_realloc_array(bss->hs20_osu_providers,
+ bss->hs20_osu_providers_count + 1, sizeof(*p));
+ if (p == NULL)
+ return -1;
+
+ bss->hs20_osu_providers = p;
+ bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
+ bss->hs20_osu_providers_count++;
+ os_memset(bss->last_osu, 0, sizeof(*p));
+ bss->last_osu->server_uri = os_strdup(pos);
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ if (parse_lang_string(&bss->last_osu->friendly_name,
+ &bss->last_osu->friendly_name_count, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ os_free(bss->last_osu->osu_nai);
+ bss->last_osu->osu_nai = os_strdup(pos);
+ if (bss->last_osu->osu_nai == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
+ int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos,
+ int line)
+{
+ char **n;
+ struct hs20_osu_provider *p = bss->last_osu;
+
+ if (p == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *));
+ if (n == NULL)
+ return -1;
+ p->icons = n;
+ p->icons[p->icons_count] = os_strdup(pos);
+ if (p->icons[p->icons_count] == NULL)
+ return -1;
+ p->icons_count++;
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ if (parse_lang_string(&bss->last_osu->service_desc,
+ &bss->last_osu->service_desc_count, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+
#endif /* CONFIG_HS20 */
@@ -1696,1349 +1840,1333 @@
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 (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) {
- bss->dump_log_name = os_strdup(pos);
- } 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;
- }
+ }
+ if (conf->driver == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid/unknown driver '%s'",
+ line, pos);
+ return 1;
+ }
+ } 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);
+ return 1;
+ }
+ 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);
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);
+ return 1;
+ }
+ 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);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "deny_mac_file") == 0) {
+ if (hostapd_config_read_maclist(pos, &bss->deny_mac,
+ &bss->num_deny_mac)) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to read deny_mac_file '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wds_sta") == 0) {
+ bss->wds_sta = atoi(pos);
+ } else if (os_strcmp(buf, "start_disabled") == 0) {
+ bss->start_disabled = atoi(pos);
+ } else if (os_strcmp(buf, "ap_isolate") == 0) {
+ bss->isolate = atoi(pos);
+ } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
+ bss->ap_max_inactivity = atoi(pos);
+ } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
+ bss->skip_inactivity_poll = atoi(pos);
+ } else if (os_strcmp(buf, "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);
+ return 1;
+ }
+ 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);
+ } else if (os_strcmp(buf, "eap_authenticator") == 0) {
+ bss->eap_server = atoi(pos);
+ wpa_printf(MSG_ERROR, "Line %d: obsolete eap_authenticator used; this has been renamed to eap_server", line);
+ } else if (os_strcmp(buf, "eap_server") == 0) {
+ bss->eap_server = atoi(pos);
+ } else if (os_strcmp(buf, "eap_user_file") == 0) {
+ if (hostapd_config_read_eap_user(pos, bss))
+ return 1;
+ } else if (os_strcmp(buf, "ca_cert") == 0) {
+ os_free(bss->ca_cert);
+ bss->ca_cert = os_strdup(pos);
+ } else if (os_strcmp(buf, "server_cert") == 0) {
+ os_free(bss->server_cert);
+ bss->server_cert = os_strdup(pos);
+ } else if (os_strcmp(buf, "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);
+ } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
+ os_free(bss->pac_opaque_encr_key);
+ bss->pac_opaque_encr_key = os_malloc(16);
+ if (bss->pac_opaque_encr_key == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: No memory for pac_opaque_encr_key",
+ line);
+ return 1;
+ } else if (hexstr2bin(pos, bss->pac_opaque_encr_key, 16)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid pac_opaque_encr_key",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "eap_fast_a_id") == 0) {
+ size_t idlen = os_strlen(pos);
+ if (idlen & 1) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid eap_fast_a_id",
+ line);
+ return 1;
+ }
+ os_free(bss->eap_fast_a_id);
+ bss->eap_fast_a_id = os_malloc(idlen / 2);
+ if (bss->eap_fast_a_id == NULL ||
+ hexstr2bin(pos, bss->eap_fast_a_id, idlen / 2)) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to parse eap_fast_a_id",
+ line);
+ os_free(bss->eap_fast_a_id);
+ bss->eap_fast_a_id = NULL;
+ return 1;
+ } else {
+ bss->eap_fast_a_id_len = idlen / 2;
+ }
+ } else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) {
+ os_free(bss->eap_fast_a_id_info);
+ bss->eap_fast_a_id_info = os_strdup(pos);
+ } else if (os_strcmp(buf, "eap_fast_prov") == 0) {
+ bss->eap_fast_prov = atoi(pos);
+ } else if (os_strcmp(buf, "pac_key_lifetime") == 0) {
+ bss->pac_key_lifetime = atoi(pos);
+ } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
+ bss->pac_key_refresh_time = atoi(pos);
#endif /* EAP_SERVER_FAST */
#ifdef EAP_SERVER_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);
+ } 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);
+ } 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);
+ } 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);
+ } else if (os_strcmp(buf, "eap_message") == 0) {
+ char *term;
+ os_free(bss->eap_req_id_text);
+ bss->eap_req_id_text = os_strdup(pos);
+ if (bss->eap_req_id_text == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to allocate memory for eap_req_id_text",
+ line);
+ return 1;
+ }
+ bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text);
+ term = os_strstr(bss->eap_req_id_text, "\\0");
+ if (term) {
+ *term++ = '\0';
+ os_memmove(term, term + 1,
+ bss->eap_req_id_text_len -
+ (term - bss->eap_req_id_text) - 1);
+ bss->eap_req_id_text_len--;
+ }
+ } else if (os_strcmp(buf, "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);
+ return 1;
+ }
+ } 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);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wep_rekey_period") == 0) {
+ bss->wep_rekeying_period = atoi(pos);
+ if (bss->wep_rekeying_period < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
+ line, bss->wep_rekeying_period);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "eap_reauth_period") == 0) {
+ bss->eap_reauth_period = atoi(pos);
+ if (bss->eap_reauth_period < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
+ line, bss->eap_reauth_period);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) {
+ bss->eapol_key_index_workaround = atoi(pos);
#ifdef CONFIG_IAPP
- } else if (os_strcmp(buf, "iapp_interface") == 0) {
- bss->ieee802_11f = 1;
- os_strlcpy(bss->iapp_iface, pos,
- sizeof(bss->iapp_iface));
+ } 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);
+ } else if (os_strcmp(buf, "own_ip_addr") == 0) {
+ if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid IP address '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "nas_identifier") == 0) {
+ os_free(bss->nas_identifier);
+ bss->nas_identifier = os_strdup(pos);
#ifndef CONFIG_NO_RADIUS
- } else if (os_strcmp(buf, "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);
+ } else if (os_strcmp(buf, "auth_server_addr") == 0) {
+ if (hostapd_config_read_radius_addr(
+ &bss->radius->auth_servers,
+ &bss->radius->num_auth_servers, pos, 1812,
+ &bss->radius->auth_server)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid IP address '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_port") == 0) {
+ bss->radius->auth_server->port = atoi(pos);
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_shared_secret") == 0) {
+ int len = os_strlen(pos);
+ if (len == 0) {
+ /* RFC 2865, Ch. 3 */
+ wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
+ line);
+ return 1;
+ }
+ os_free(bss->radius->auth_server->shared_secret);
+ bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos);
+ bss->radius->auth_server->shared_secret_len = len;
+ } else if (os_strcmp(buf, "acct_server_addr") == 0) {
+ if (hostapd_config_read_radius_addr(
+ &bss->radius->acct_servers,
+ &bss->radius->num_acct_servers, pos, 1813,
+ &bss->radius->acct_server)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid IP address '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_port") == 0) {
+ bss->radius->acct_server->port = atoi(pos);
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_shared_secret") == 0) {
+ int len = os_strlen(pos);
+ if (len == 0) {
+ /* RFC 2865, Ch. 3 */
+ wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
+ line);
+ return 1;
+ }
+ os_free(bss->radius->acct_server->shared_secret);
+ bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos);
+ bss->radius->acct_server->shared_secret_len = len;
+ } else if (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);
+ return 1;
+ } else if (bss->radius_auth_req_attr == NULL) {
+ bss->radius_auth_req_attr = attr;
+ } else {
+ a = bss->radius_auth_req_attr;
+ while (a->next)
+ a = a->next;
+ a->next = attr;
+ }
+ } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
+ struct hostapd_radius_attr *attr, *a;
+ attr = hostapd_parse_radius_attr(pos);
+ if (attr == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid radius_acct_req_attr",
+ line);
+ return 1;
+ } else if (bss->radius_acct_req_attr == NULL) {
+ bss->radius_acct_req_attr = attr;
+ } else {
+ a = bss->radius_acct_req_attr;
+ while (a->next)
+ a = a->next;
+ a->next = attr;
+ }
+ } else if (os_strcmp(buf, "radius_das_port") == 0) {
+ bss->radius_das_port = atoi(pos);
+ } else if (os_strcmp(buf, "radius_das_client") == 0) {
+ if (hostapd_parse_das_client(bss, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid DAS client",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "radius_das_time_window") == 0) {
+ bss->radius_das_time_window = atoi(pos);
+ } else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) {
+ bss->radius_das_require_event_timestamp = atoi(pos);
#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);
- os_free(bss->ssid.wpa_psk);
- bss->ssid.wpa_psk = NULL;
- }
- } else if (os_strcmp(buf, "wpa_psk") == 0) {
+ } else if (os_strcmp(buf, "auth_algs") == 0) {
+ bss->auth_algs = atoi(pos);
+ if (bss->auth_algs == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: no authentication algorithms allowed",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "max_num_sta") == 0) {
+ bss->max_num_sta = atoi(pos);
+ if (bss->max_num_sta < 0 ||
+ bss->max_num_sta > MAX_STA_COUNT) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
+ line, bss->max_num_sta, MAX_STA_COUNT);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wpa") == 0) {
+ bss->wpa = atoi(pos);
+ } else if (os_strcmp(buf, "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);
+ return 1;
+ }
+ 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 =
- 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;
- }
- } 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++;
- }
+ 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)
+ return 1;
+ if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, PMK_LEN) ||
+ pos[PMK_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+ line, pos);
+ os_free(bss->ssid.wpa_psk);
+ bss->ssid.wpa_psk = NULL;
+ return 1;
+ }
+ bss->ssid.wpa_psk->group = 1;
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = NULL;
+ bss->ssid.wpa_psk_set = 1;
+ } else if (os_strcmp(buf, "wpa_psk_file") == 0) {
+ os_free(bss->ssid.wpa_psk_file);
+ bss->ssid.wpa_psk_file = os_strdup(pos);
+ if (!bss->ssid.wpa_psk_file) {
+ wpa_printf(MSG_ERROR, "Line %d: allocation failed",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wpa_key_mgmt") == 0) {
+ bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos);
+ if (bss->wpa_key_mgmt == -1)
+ return 1;
+ } else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
+ bss->wpa_psk_radius = atoi(pos);
+ if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+ bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
+ bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown wpa_psk_radius %d",
+ line, bss->wpa_psk_radius);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wpa_pairwise") == 0) {
+ bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos);
+ if (bss->wpa_pairwise == -1 || bss->wpa_pairwise == 0)
+ return 1;
+ if (bss->wpa_pairwise &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+ bss->wpa_pairwise, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "rsn_pairwise") == 0) {
+ bss->rsn_pairwise = hostapd_config_parse_cipher(line, pos);
+ if (bss->rsn_pairwise == -1 || bss->rsn_pairwise == 0)
+ return 1;
+ if (bss->rsn_pairwise &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+ bss->rsn_pairwise, pos);
+ return 1;
+ }
#ifdef CONFIG_RSN_PREAUTH
- } else if (os_strcmp(buf, "rsn_preauth") == 0) {
- bss->rsn_preauth = atoi(pos);
- } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
- bss->rsn_preauth_interfaces = os_strdup(pos);
+ } else if (os_strcmp(buf, "rsn_preauth") == 0) {
+ bss->rsn_preauth = atoi(pos);
+ } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
+ os_free(bss->rsn_preauth_interfaces);
+ bss->rsn_preauth_interfaces = os_strdup(pos);
#endif /* CONFIG_RSN_PREAUTH */
#ifdef CONFIG_PEERKEY
- } else if (os_strcmp(buf, "peerkey") == 0) {
- bss->peerkey = atoi(pos);
+ } 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);
+ } else if (os_strcmp(buf, "mobility_domain") == 0) {
+ if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+ hexstr2bin(pos, bss->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid mobility_domain '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "r1_key_holder") == 0) {
+ if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
+ hexstr2bin(pos, bss->r1_key_holder, FT_R1KH_ID_LEN) != 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid r1_key_holder '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
+ 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);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "r1kh") == 0) {
+ if (add_r1kh(bss, pos) < 0) {
+ wpa_printf(MSG_DEBUG, "Line %d: Invalid r1kh '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "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) {
+ } 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;
+ 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;
- }
+ 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",
- bss->ctrl_interface_gid);
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d (from group name '%s')",
+ bss->ctrl_interface_gid, group);
+ return 0;
+ }
+
+ /* Group name not found - try to parse this as gid */
+ bss->ctrl_interface_gid = strtol(group, &endp, 10);
+ if (*group == '\0' || *endp != '\0') {
+ wpa_printf(MSG_DEBUG, "Line %d: Invalid group '%s'",
+ line, group);
+ return 1;
+ }
+ bss->ctrl_interface_gid_set = 1;
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+ bss->ctrl_interface_gid);
#endif /* CONFIG_NATIVE_WINDOWS */
#endif /* CONFIG_NO_CTRL_IFACE */
#ifdef RADIUS_SERVER
- } else if (os_strcmp(buf, "radius_server_clients") == 0) {
- os_free(bss->radius_server_clients);
- bss->radius_server_clients = os_strdup(pos);
- } else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
- bss->radius_server_auth_port = atoi(pos);
- } else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
- bss->radius_server_ipv6 = atoi(pos);
+ } else if (os_strcmp(buf, "radius_server_clients") == 0) {
+ os_free(bss->radius_server_clients);
+ bss->radius_server_clients = os_strdup(pos);
+ } else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
+ bss->radius_server_auth_port = atoi(pos);
+ } else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
+ bss->radius_server_acct_port = atoi(pos);
+ } else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
+ bss->radius_server_ipv6 = atoi(pos);
#endif /* RADIUS_SERVER */
- } else if (os_strcmp(buf, "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) {
+ } 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);
+ return 1;
+ }
+ } 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);
+ return 1;
+ }
+ } 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++;
+ wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled",
+ line);
+ return 1;
+#else /* CONFIG_ACS */
+ conf->channel = 0;
#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;
+ } else
+ conf->channel = atoi(pos);
+ } else if (os_strcmp(buf, "chanlist") == 0) {
+ if (hostapd_parse_intlist(&conf->chanlist, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
+ line);
+ return 1;
+ }
+ } 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);
+ return 1;
+ }
+ conf->beacon_int = val;
#ifdef CONFIG_ACS
- } else if (os_strcmp(buf, "acs_num_scans") == 0) {
- int val = atoi(pos);
- if (val <= 0 || val > 100) {
- wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
- line, val);
- errors++;
- } else
- conf->acs_num_scans = val;
+ } else if (os_strcmp(buf, "acs_num_scans") == 0) {
+ int val = atoi(pos);
+ if (val <= 0 || val > 100) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
+ line, val);
+ return 1;
+ }
+ conf->acs_num_scans = val;
#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++;
- }
+ } 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);
+ return 1;
+ }
+ } 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);
+ return 1;
+ }
+ } 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);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "send_probe_response") == 0) {
+ int val = atoi(pos);
+ if (val != 0 && val != 1) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid send_probe_response %d (expected 0 or 1)",
+ line, val);
+ return 1;
+ }
+ 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);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "basic_rates") == 0) {
+ if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "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);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wep_key0") == 0 ||
+ os_strcmp(buf, "wep_key1") == 0 ||
+ os_strcmp(buf, "wep_key2") == 0 ||
+ os_strcmp(buf, "wep_key3") == 0) {
+ if (hostapd_config_read_wep(&bss->ssid.wep,
+ buf[7] - '0', pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WEP key '%s'",
+ line, buf);
+ return 1;
+ }
#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++;
- }
+ } 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);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "vlan_naming") == 0) {
+ bss->ssid.vlan_naming = atoi(pos);
+ if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
+ bss->ssid.vlan_naming < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid naming scheme %d",
+ line, bss->ssid.vlan_naming);
+ return 1;
+ }
#ifdef CONFIG_FULL_DYNAMIC_VLAN
- } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
- bss->ssid.vlan_tagged_interface = os_strdup(pos);
+ } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
+ os_free(bss->ssid.vlan_tagged_interface);
+ bss->ssid.vlan_tagged_interface = os_strdup(pos);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
#endif /* CONFIG_NO_VLAN */
- } else if (os_strcmp(buf, "ap_table_max_size") == 0) {
- conf->ap_table_max_size = atoi(pos);
- } else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
- conf->ap_table_expiration_time = atoi(pos);
- } else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
- if (hostapd_config_tx_queue(conf, 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++;
- }
+ } 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);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wme_enabled") == 0 ||
+ os_strcmp(buf, "wmm_enabled") == 0) {
+ bss->wmm_enabled = atoi(pos);
+ } else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) {
+ bss->wmm_uapsd = atoi(pos);
+ } else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
+ os_strncmp(buf, "wmm_ac_", 7) == 0) {
+ if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WMM ac item",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "bss") == 0) {
+ if (hostapd_config_bss(conf, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid bss item",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "bssid") == 0) {
+ if (hwaddr_aton(pos, bss->bssid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid bssid item",
+ line);
+ return 1;
+ }
#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++;
- }
+ } else if (os_strcmp(buf, "ieee80211w") == 0) {
+ bss->ieee80211w = atoi(pos);
+ } else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
+ if (os_strcmp(pos, "AES-128-CMAC") == 0) {
+ bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+ } else if (os_strcmp(pos, "BIP-GMAC-128") == 0) {
+ bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_128;
+ } else if (os_strcmp(pos, "BIP-GMAC-256") == 0) {
+ bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_256;
+ } else if (os_strcmp(pos, "BIP-CMAC-256") == 0) {
+ bss->group_mgmt_cipher = WPA_CIPHER_BIP_CMAC_256;
+ } else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid group_mgmt_cipher: %s",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
+ bss->assoc_sa_query_max_timeout = atoi(pos);
+ if (bss->assoc_sa_query_max_timeout == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_max_timeout",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0) {
+ bss->assoc_sa_query_retry_timeout = atoi(pos);
+ if (bss->assoc_sa_query_retry_timeout == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_retry_timeout",
+ line);
+ return 1;
+ }
#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, "ieee80211n") == 0) {
+ conf->ieee80211n = atoi(pos);
+ } else if (os_strcmp(buf, "ht_capab") == 0) {
+ if (hostapd_config_ht_capab(conf, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid ht_capab",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "require_ht") == 0) {
+ conf->require_ht = atoi(pos);
+ } else if (os_strcmp(buf, "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);
+ } else if (os_strcmp(buf, "ieee80211ac") == 0) {
+ conf->ieee80211ac = atoi(pos);
+ } else if (os_strcmp(buf, "vht_capab") == 0) {
+ if (hostapd_config_vht_capab(conf, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid vht_capab",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "require_vht") == 0) {
+ conf->require_vht = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
+ conf->vht_oper_chwidth = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) {
+ conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) {
+ conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
#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);
+ } 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);
+ } else if (os_strcmp(buf, "wps_state") == 0) {
+ bss->wps_state = atoi(pos);
+ if (bss->wps_state < 0 || bss->wps_state > 2) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid wps_state",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wps_independent") == 0) {
+ bss->wps_independent = atoi(pos);
+ } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+ bss->ap_setup_locked = atoi(pos);
+ } else if (os_strcmp(buf, "uuid") == 0) {
+ if (uuid_str2bin(pos, bss->uuid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wps_pin_requests") == 0) {
+ os_free(bss->wps_pin_requests);
+ bss->wps_pin_requests = os_strdup(pos);
+ } else if (os_strcmp(buf, "device_name") == 0) {
+ if (os_strlen(pos) > 32) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long "
+ "device_name", line);
+ return 1;
+ }
+ os_free(bss->device_name);
+ bss->device_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "manufacturer") == 0) {
+ if (os_strlen(pos) > 64) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long manufacturer",
+ line);
+ return 1;
+ }
+ os_free(bss->manufacturer);
+ bss->manufacturer = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_name") == 0) {
+ if (os_strlen(pos) > 32) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long model_name",
+ line);
+ return 1;
+ }
+ os_free(bss->model_name);
+ bss->model_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_number") == 0) {
+ if (os_strlen(pos) > 32) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long model_number",
+ line);
+ return 1;
+ }
+ os_free(bss->model_number);
+ bss->model_number = os_strdup(pos);
+ } else if (os_strcmp(buf, "serial_number") == 0) {
+ if (os_strlen(pos) > 32) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long serial_number",
+ line);
+ return 1;
+ }
+ os_free(bss->serial_number);
+ bss->serial_number = os_strdup(pos);
+ } else if (os_strcmp(buf, "device_type") == 0) {
+ if (wps_dev_type_str2bin(pos, bss->device_type))
+ return 1;
+ } else if (os_strcmp(buf, "config_methods") == 0) {
+ os_free(bss->config_methods);
+ bss->config_methods = os_strdup(pos);
+ } else if (os_strcmp(buf, "os_version") == 0) {
+ if (hexstr2bin(pos, bss->os_version, 4)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid os_version",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "ap_pin") == 0) {
+ os_free(bss->ap_pin);
+ bss->ap_pin = os_strdup(pos);
+ } else if (os_strcmp(buf, "skip_cred_build") == 0) {
+ bss->skip_cred_build = atoi(pos);
+ } else if (os_strcmp(buf, "extra_cred") == 0) {
+ os_free(bss->extra_cred);
+ bss->extra_cred = (u8 *) os_readfile(pos, &bss->extra_cred_len);
+ if (bss->extra_cred == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: could not read Credentials from '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "wps_cred_processing") == 0) {
+ bss->wps_cred_processing = atoi(pos);
+ } else if (os_strcmp(buf, "ap_settings") == 0) {
+ os_free(bss->ap_settings);
+ bss->ap_settings =
+ (u8 *) os_readfile(pos, &bss->ap_settings_len);
+ if (bss->ap_settings == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: could not read AP Settings from '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "upnp_iface") == 0) {
+ os_free(bss->upnp_iface);
+ bss->upnp_iface = os_strdup(pos);
+ } else if (os_strcmp(buf, "friendly_name") == 0) {
+ os_free(bss->friendly_name);
+ bss->friendly_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "manufacturer_url") == 0) {
+ os_free(bss->manufacturer_url);
+ bss->manufacturer_url = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_description") == 0) {
+ os_free(bss->model_description);
+ bss->model_description = os_strdup(pos);
+ } else if (os_strcmp(buf, "model_url") == 0) {
+ os_free(bss->model_url);
+ bss->model_url = os_strdup(pos);
+ } else if (os_strcmp(buf, "upc") == 0) {
+ os_free(bss->upc);
+ bss->upc = os_strdup(pos);
+ } else if (os_strcmp(buf, "pbc_in_m1") == 0) {
+ bss->pbc_in_m1 = atoi(pos);
+ } else if (os_strcmp(buf, "server_id") == 0) {
+ os_free(bss->server_id);
+ bss->server_id = os_strdup(pos);
#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;
+ } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
+ bss->wps_nfc_dev_pw_id = atoi(pos);
+ if (bss->wps_nfc_dev_pw_id < 0x10 ||
+ bss->wps_nfc_dev_pw_id > 0xffff) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid wps_nfc_dev_pw_id value",
+ line);
+ return 1;
+ }
+ bss->wps_nfc_pw_from_config = 1;
+ } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
+ wpabuf_free(bss->wps_nfc_dh_pubkey);
+ bss->wps_nfc_dh_pubkey = 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;
+ } else if (os_strcmp(buf, "manage_p2p") == 0) {
+ if (atoi(pos))
+ bss->p2p |= P2P_MANAGE;
+ else
+ bss->p2p &= ~P2P_MANAGE;
+ } else if (os_strcmp(buf, "allow_cross_connection") == 0) {
+ if (atoi(pos))
+ bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
+ else
+ bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
#endif /* CONFIG_P2P_MANAGER */
- } else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
- bss->disassoc_low_ack = atoi(pos);
- } else if (os_strcmp(buf, "tdls_prohibit") == 0) {
- 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;
+ } else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
+ bss->disassoc_low_ack = atoi(pos);
+ } else if (os_strcmp(buf, "tdls_prohibit") == 0) {
+ if (atoi(pos))
+ bss->tdls |= TDLS_PROHIBIT;
+ else
+ bss->tdls &= ~TDLS_PROHIBIT;
+ } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
+ if (atoi(pos))
+ bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
+ else
+ bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
#ifdef CONFIG_RSN_TESTING
- } else if (os_strcmp(buf, "rsn_testing") == 0) {
- extern int rsn_testing;
- rsn_testing = atoi(pos);
+ } 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++;
+ } else if (os_strcmp(buf, "time_advertisement") == 0) {
+ bss->time_advertisement = atoi(pos);
+ } else if (os_strcmp(buf, "time_zone") == 0) {
+ size_t tz_len = os_strlen(pos);
+ if (tz_len < 4 || tz_len > 255) {
+ wpa_printf(MSG_DEBUG, "Line %d: invalid time_zone",
+ line);
+ return 1;
+ }
+ os_free(bss->time_zone);
+ bss->time_zone = os_strdup(pos);
+ if (bss->time_zone == NULL)
+ return 1;
#ifdef CONFIG_WNM
- } 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);
+ } 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;
+ } else if (os_strcmp(buf, "interworking") == 0) {
+ bss->interworking = atoi(pos);
+ } else if (os_strcmp(buf, "access_network_type") == 0) {
+ bss->access_network_type = atoi(pos);
+ if (bss->access_network_type < 0 ||
+ bss->access_network_type > 15) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid access_network_type",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "internet") == 0) {
+ bss->internet = atoi(pos);
+ } else if (os_strcmp(buf, "asra") == 0) {
+ bss->asra = atoi(pos);
+ } else if (os_strcmp(buf, "esr") == 0) {
+ bss->esr = atoi(pos);
+ } else if (os_strcmp(buf, "uesa") == 0) {
+ bss->uesa = atoi(pos);
+ } else if (os_strcmp(buf, "venue_group") == 0) {
+ bss->venue_group = atoi(pos);
+ bss->venue_info_set = 1;
+ } else if (os_strcmp(buf, "venue_type") == 0) {
+ bss->venue_type = atoi(pos);
+ bss->venue_info_set = 1;
+ } else if (os_strcmp(buf, "hessid") == 0) {
+ if (hwaddr_aton(pos, bss->hessid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid hessid", line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "roaming_consortium") == 0) {
+ if (parse_roaming_consortium(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "venue_name") == 0) {
+ if (parse_venue_name(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "network_auth_type") == 0) {
+ u8 auth_type;
+ u16 redirect_url_len;
+ if (hexstr2bin(pos, &auth_type, 1)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid network_auth_type '%s'",
+ line, pos);
+ return 1;
+ }
+ if (auth_type == 0 || auth_type == 2)
+ redirect_url_len = os_strlen(pos + 2);
+ else
+ redirect_url_len = 0;
+ os_free(bss->network_auth_type);
+ bss->network_auth_type = os_malloc(redirect_url_len + 3 + 1);
+ if (bss->network_auth_type == NULL)
+ return 1;
+ *bss->network_auth_type = auth_type;
+ WPA_PUT_LE16(bss->network_auth_type + 1, redirect_url_len);
+ if (redirect_url_len)
+ os_memcpy(bss->network_auth_type + 3, pos + 2,
+ redirect_url_len);
+ bss->network_auth_type_len = 3 + redirect_url_len;
+ } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
+ if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid ipaddr_type_availability '%s'",
+ line, pos);
+ bss->ipaddr_type_configured = 0;
+ return 1;
+ }
+ bss->ipaddr_type_configured = 1;
+ } else if (os_strcmp(buf, "domain_name") == 0) {
+ int j, num_domains, domain_len, domain_list_len = 0;
+ char *tok_start, *tok_prev;
+ u8 *domain_list, *domain_ptr;
- domain_list_len = os_strlen(pos) + 1;
- domain_list = os_malloc(domain_list_len);
- if (domain_list == NULL) {
- errors++;
- return errors;
- }
+ domain_list_len = os_strlen(pos) + 1;
+ domain_list = os_malloc(domain_list_len);
+ if (domain_list == NULL)
+ return 1;
- domain_ptr = domain_list;
- tok_prev = pos;
- num_domains = 1;
- while ((tok_prev = os_strchr(tok_prev, ','))) {
- num_domains++;
- tok_prev++;
+ 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;
}
- 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);
+ os_free(bss->domain_name);
+ bss->domain_name = domain_list;
+ bss->domain_name_len = domain_list_len;
+ } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
+ if (parse_3gpp_cell_net(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "nai_realm") == 0) {
+ if (parse_nai_realm(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "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)
+ return 1;
#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);
+ } 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;
+ } 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, "osen") == 0) {
+ bss->osen = atoi(pos);
+ } else if (os_strcmp(buf, "anqp_domain_id") == 0) {
+ bss->anqp_domain_id = atoi(pos);
+ } else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
+ bss->hs20_deauth_req_timeout = atoi(pos);
+ } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
+ if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
+ if (hs20_parse_wan_metrics(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
+ if (hs20_parse_conn_capab(bss, pos, line) < 0) {
+ return 1;
+ }
+ } else if (os_strcmp(buf, "hs20_operating_class") == 0) {
+ u8 *oper_class;
+ size_t oper_class_len;
+ oper_class_len = os_strlen(pos);
+ if (oper_class_len < 2 || (oper_class_len & 0x01)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid hs20_operating_class '%s'",
+ line, pos);
+ return 1;
+ }
+ oper_class_len /= 2;
+ oper_class = os_malloc(oper_class_len);
+ if (oper_class == NULL)
+ return 1;
+ if (hexstr2bin(pos, oper_class, oper_class_len)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid hs20_operating_class '%s'",
+ line, pos);
+ os_free(oper_class);
+ return 1;
+ }
+ os_free(bss->hs20_operating_class);
+ bss->hs20_operating_class = oper_class;
+ bss->hs20_operating_class_len = oper_class_len;
+ } else if (os_strcmp(buf, "hs20_icon") == 0) {
+ if (hs20_parse_icon(bss, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "osu_ssid") == 0) {
+ if (hs20_parse_osu_ssid(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_server_uri") == 0) {
+ if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_friendly_name") == 0) {
+ if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_nai") == 0) {
+ if (hs20_parse_osu_nai(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_method_list") == 0) {
+ if (hs20_parse_osu_method_list(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_icon") == 0) {
+ if (hs20_parse_osu_icon(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "osu_service_desc") == 0) {
+ if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
+ os_free(bss->subscr_remediation_url);
+ bss->subscr_remediation_url = os_strdup(pos);
+ } else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
+ bss->subscr_remediation_method = atoi(pos);
#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)
-#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++;
+#define PARSE_TEST_PROBABILITY(_val) \
+ } else if (os_strcmp(buf, #_val) == 0) { \
+ char *end; \
+ \
+ conf->_val = strtod(pos, &end); \
+ if (*end || conf->_val < 0.0 || \
+ conf->_val > 1.0) { \
+ wpa_printf(MSG_ERROR, \
+ "Line %d: Invalid value '%s'", \
+ line, pos); \
+ return 1; \
}
- }
+ PARSE_TEST_PROBABILITY(ignore_probe_probability)
+ PARSE_TEST_PROBABILITY(ignore_auth_probability)
+ PARSE_TEST_PROBABILITY(ignore_assoc_probability)
+ PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
+ PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
+ } else if (os_strcmp(buf, "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;
+ }
- return errors;
-}
+ 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;
+ }
-static 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;
+ 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 if (os_strcmp(buf, "local_pwr_constraint") == 0) {
+ int val = atoi(pos);
+ if (val < 0 || val > 255) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid local_pwr_constraint %d (expected 0..255)",
+ line, val);
+ return 1;
+ }
+ conf->local_pwr_constraint = val;
+ } else if (os_strcmp(buf, "spectrum_mgmt_required") == 0) {
+ conf->spectrum_mgmt_required = atoi(pos);
+ } else if (os_strcmp(buf, "wowlan_triggers") == 0) {
+ os_free(bss->wowlan_triggers);
+ bss->wowlan_triggers = os_strdup(pos);
} 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_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+ line, buf);
+ return 1;
}
+
+ return 0;
}
@@ -3079,7 +3207,7 @@
return NULL;
}
- bss = conf->last_bss = conf->bss;
+ bss = conf->last_bss = conf->bss[0];
while (fgets(buf, sizeof(buf), f)) {
bss = conf->last_bss;
@@ -3113,9 +3241,9 @@
fclose(f);
for (i = 0; i < conf->num_bss; i++)
- hostapd_set_security_params(&conf->bss[i]);
+ hostapd_set_security_params(conf->bss[i], 1);
- if (hostapd_config_check(conf))
+ if (hostapd_config_check(conf, 1))
errors++;
#ifndef WPA_IGNORE_CONFIG_ERRORS
@@ -3145,9 +3273,9 @@
}
for (i = 0; i < conf->num_bss; i++)
- hostapd_set_security_params(&conf->bss[i]);
+ hostapd_set_security_params(conf->bss[i], 0);
- if (hostapd_config_check(conf)) {
+ if (hostapd_config_check(conf, 0)) {
wpa_printf(MSG_ERROR, "Configuration check failed");
return -1;
}
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index be941c4..9ce7829 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1,6 +1,6 @@
/*
* hostapd / UNIX domain socket -based control interface
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -20,6 +20,7 @@
#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"
@@ -29,6 +30,8 @@
#include "ap/wps_hostapd.h"
#include "ap/ctrl_iface_ap.h"
#include "ap/ap_drv_ops.h"
+#include "ap/hs20.h"
+#include "ap/wnm_ap.h"
#include "ap/wpa_auth.h"
#include "wps/wps_defs.h"
#include "wps/wps.h"
@@ -82,15 +85,15 @@
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);
- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
- (u8 *) from->sun_path,
- fromlen -
- offsetof(struct sockaddr_un, sun_path));
return 0;
}
prev = dst;
@@ -397,13 +400,70 @@
static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
char *cmd)
{
- /*
- * Since NFC connection handover provided full WPS Credential, there is
- * no need for additional operations within hostapd. Just report this in
- * debug log.
- */
- wpa_printf(MSG_DEBUG, "NFC: Connection handover reported: %s", cmd);
- return 0;
+ 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 */
@@ -558,6 +618,182 @@
#endif /* CONFIG_WPS */
+#ifdef CONFIG_HS20
+
+static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ const char *url;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+ url = cmd + 17;
+ if (*url == '\0') {
+ url = NULL;
+ } else {
+ if (*url != ' ')
+ return -1;
+ url++;
+ if (*url == '\0')
+ url = NULL;
+ }
+
+ return hs20_send_wnm_notification(hapd, addr, 1, url);
+}
+
+
+static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ int code, reauth_delay, ret;
+ const char *pos;
+ size_t url_len;
+ struct wpabuf *req;
+
+ /* <STA MAC Addr> <Code(0/1)> <Re-auth-Delay(sec)> [URL] */
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ code = atoi(pos);
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ reauth_delay = atoi(pos);
+
+ url_len = 0;
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ pos++;
+ url_len = os_strlen(pos);
+ }
+
+ req = wpabuf_alloc(4 + url_len);
+ if (req == NULL)
+ return -1;
+ wpabuf_put_u8(req, code);
+ wpabuf_put_le16(req, reauth_delay);
+ wpabuf_put_u8(req, url_len);
+ if (pos)
+ wpabuf_put_data(req, pos, url_len);
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR
+ " to indicate imminent deauthentication (code=%d "
+ "reauth_delay=%d)", MAC2STR(addr), code, reauth_delay);
+ ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req);
+ wpabuf_free(req);
+ return ret;
+}
+
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_INTERWORKING
+
+static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 qos_map_set[16 + 2 * 21], count = 0;
+ const char *pos = cmd;
+ int val, ret;
+
+ for (;;) {
+ if (count == sizeof(qos_map_set)) {
+ wpa_printf(MSG_ERROR, "Too many qos_map_set parameters");
+ return -1;
+ }
+
+ val = atoi(pos);
+ if (val < 0 || val > 255) {
+ wpa_printf(MSG_INFO, "Invalid QoS Map Set");
+ return -1;
+ }
+
+ qos_map_set[count++] = val;
+ pos = os_strchr(pos, ',');
+ if (!pos)
+ break;
+ pos++;
+ }
+
+ if (count < 16 || count & 1) {
+ wpa_printf(MSG_INFO, "Invalid QoS Map Set");
+ return -1;
+ }
+
+ ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count);
+ if (ret) {
+ wpa_printf(MSG_INFO, "Failed to set QoS Map Set");
+ return -1;
+ }
+
+ os_memcpy(hapd->conf->qos_map_set, qos_map_set, count);
+ hapd->conf->qos_map_set_len = count;
+
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ struct wpabuf *buf;
+ u8 *qos_map_set = hapd->conf->qos_map_set;
+ u8 qos_map_set_len = hapd->conf->qos_map_set_len;
+ int ret;
+
+ if (!qos_map_set_len) {
+ wpa_printf(MSG_INFO, "QoS Map Set is not set");
+ return -1;
+ }
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
+ "for QoS Map Configuration message",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ if (!sta->qos_map_enabled) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate "
+ "support for QoS Map", MAC2STR(addr));
+ return -1;
+ }
+
+ buf = wpabuf_alloc(2 + 2 + qos_map_set_len);
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_QOS);
+ wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG);
+
+ /* QoS Map Set Element */
+ wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET);
+ wpabuf_put_u8(buf, qos_map_set_len);
+ wpabuf_put_data(buf, qos_map_set, qos_map_set_len);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+#endif /* CONFIG_INTERWORKING */
+
#ifdef CONFIG_WNM
@@ -565,9 +801,8 @@
const char *cmd)
{
u8 addr[ETH_ALEN];
- u8 buf[1000], *pos;
- struct ieee80211_mgmt *mgmt;
int disassoc_timer;
+ struct sta_info *sta;
if (hwaddr_aton(cmd, addr))
return -1;
@@ -575,31 +810,15 @@
return -1;
disassoc_timer = atoi(cmd + 17);
- 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, 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;
-
- if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
- wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
- "Management Request frame");
+ 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 0;
+ return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
}
@@ -608,14 +827,20 @@
{
u8 addr[ETH_ALEN];
const char *url, *timerstr;
- u8 buf[1000], *pos;
- struct ieee80211_mgmt *mgmt;
- size_t url_len;
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;
@@ -628,75 +853,8 @@
if (url == NULL)
return -1;
url++;
- url_len = os_strlen(url);
- if (url_len > 255)
- return -1;
- os_memset(buf, 0, sizeof(buf));
- mgmt = (struct ieee80211_mgmt *) buf;
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(mgmt->da, 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_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 */
- *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) {
- struct sta_info *sta;
- 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, addr);
-
- 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;
- }
-
- 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(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;
+ return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
}
#endif /* CONFIG_WNM */
@@ -782,6 +940,14 @@
return pos - buf;
pos += ret;
}
+#ifdef CONFIG_SAE
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+ ret = os_snprintf(pos, end - pos, "FT-SAE ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SAE */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
@@ -797,6 +963,14 @@
pos += ret;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+ ret = os_snprintf(pos, end - pos, "SAE ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SAE */
ret = os_snprintf(pos, end - pos, "\n");
if (ret < 0 || ret >= end - pos)
@@ -884,6 +1058,10 @@
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) {
@@ -893,8 +1071,42 @@
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 {
+ struct sta_info *sta;
+ int vlan_id;
+
ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
+ if (ret)
+ return ret;
+
+ if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (hostapd_maclist_found(
+ hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, sta->addr,
+ &vlan_id) &&
+ (!vlan_id || vlan_id == sta->vlan_id))
+ ap_sta_disconnect(
+ hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+ } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED &&
+ os_strcasecmp(cmd, "accept_mac_file") == 0) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!hostapd_maclist_found(
+ hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac,
+ sta->addr, &vlan_id) ||
+ (vlan_id && vlan_id != sta->vlan_id))
+ ap_sta_disconnect(
+ hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+ }
}
return ret;
@@ -949,11 +1161,200 @@
}
+#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_iface *iface,
+ char *pos)
+{
+#ifdef NEED_AP_MLME
+ struct csa_settings settings;
+ int ret;
+ unsigned int i;
+
+ ret = hostapd_parse_csa_settings(pos, &settings);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ ret = hostapd_switch_channel(iface->bss[i], &settings);
+ if (ret) {
+ /* FIX: What do we do if CSA fails in the middle of
+ * submitting multi-BSS CSA requests? */
+ return ret;
+ }
+ }
+
+ return 0;
+#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 int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos;
+ u8 *data = NULL;
+ unsigned int vendor_id, subcmd;
+ struct wpabuf *reply;
+ size_t data_len = 0;
+
+ /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+ vendor_id = strtoul(cmd, &pos, 16);
+ if (!isblank(*pos))
+ return -EINVAL;
+
+ subcmd = strtoul(pos, &pos, 10);
+
+ if (*pos != '\0') {
+ if (!isblank(*pos++))
+ return -EINVAL;
+ data_len = os_strlen(pos);
+ }
+
+ if (data_len) {
+ data_len /= 2;
+ data = os_malloc(data_len);
+ if (!data)
+ return -ENOBUFS;
+
+ if (hexstr2bin(pos, data, data_len)) {
+ wpa_printf(MSG_DEBUG,
+ "Vendor command: wrong parameter format");
+ os_free(data);
+ return -EINVAL;
+ }
+ }
+
+ reply = wpabuf_alloc((buflen - 1) / 2);
+ if (!reply) {
+ os_free(data);
+ return -ENOBUFS;
+ }
+
+ ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
+ reply);
+
+ if (ret == 0)
+ ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
+ wpabuf_len(reply));
+
+ wpabuf_free(reply);
+ os_free(data);
+
+ return ret;
+}
+
+
static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
- char buf[256];
+ char buf[4096];
int res;
struct sockaddr_un from;
socklen_t fromlen = sizeof(from);
@@ -989,6 +1390,11 @@
} 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) {
@@ -1018,6 +1424,9 @@
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);
@@ -1093,6 +1502,22 @@
reply_len = -1;
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
+#ifdef CONFIG_INTERWORKING
+ } else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) {
+ if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) {
+ if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18))
+ reply_len = -1;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ } else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
+ if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
+ if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
+ reply_len = -1;
+#endif /* CONFIG_HS20 */
#ifdef CONFIG_WNM
} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
@@ -1119,6 +1544,21 @@
} 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->iface, buf + 12))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
+ reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
+ reply_size);
+
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -1367,6 +1807,16 @@
}
+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)
{
@@ -1385,6 +1835,7 @@
return;
}
buf[res] = '\0';
+ wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
os_memcpy(reply, "OK\n", 3);
reply_len = 3;
@@ -1392,12 +1843,23 @@
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;
+#ifdef CONFIG_MODULE_TESTS
+ } else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
+ int hapd_module_tests(void);
+ if (hapd_module_tests() < 0)
+ reply_len = -1;
+#endif /* CONFIG_MODULE_TESTS */
} else {
wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
"ignored");
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 2dd6fc8..5b74b64 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -22,6 +22,19 @@
# 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$<path to libnl include files>
+#LIBS += -L$<path to libnl library files>
+
+# 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
@@ -42,10 +55,7 @@
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
+CONFIG_IEEE80211W=y
# Integrated EAP server
CONFIG_EAP=y
@@ -96,16 +106,13 @@
#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.
+# 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
@@ -157,6 +164,12 @@
# 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
@@ -174,7 +187,7 @@
# Note: This requires libnl 3.1 or newer.
#CONFIG_VLAN_NETLINK=y
-# Remove support for dumping state into a file on SIGUSR1 signal
+# 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
diff --git a/hostapd/dump_state.c b/hostapd/dump_state.c
deleted file mode 100644
index fcd9890..0000000
--- a/hostapd/dump_state.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * hostapd / State dump
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "utils/includes.h"
-#include <time.h>
-
-#include "utils/common.h"
-#include "radius/radius_client.h"
-#include "radius/radius_server.h"
-#include "eapol_auth/eapol_auth_sm.h"
-#include "eapol_auth/eapol_auth_sm_i.h"
-#include "eap_server/eap.h"
-#include "ap/hostapd.h"
-#include "ap/ap_config.h"
-#include "ap/sta_info.h"
-#include "dump_state.h"
-#include "ap/ap_drv_ops.h"
-
-
-static void fprint_char(FILE *f, char c)
-{
- if (c >= 32 && c < 127)
- fprintf(f, "%c", c);
- else
- fprintf(f, "<%02x>", c);
-}
-
-
-static void ieee802_1x_dump_state(FILE *f, const char *prefix,
- struct sta_info *sta)
-{
- struct eapol_state_machine *sm = sta->eapol_sm;
- if (sm == NULL)
- return;
-
- fprintf(f, "%sIEEE 802.1X:\n", prefix);
-
- if (sm->identity) {
- size_t i;
- fprintf(f, "%sidentity=", prefix);
- for (i = 0; i < sm->identity_len; i++)
- fprint_char(f, sm->identity[i]);
- fprintf(f, "\n");
- }
-
- fprintf(f, "%slast EAP type: Authentication Server: %d (%s) "
- "Supplicant: %d (%s)\n", prefix,
- 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));
-
- fprintf(f, "%scached_packets=%s\n", prefix,
- sm->last_recv_radius ? "[RX RADIUS]" : "");
-
- eapol_auth_dump_state(f, prefix, sm);
-}
-
-
-/**
- * hostapd_dump_state - SIGUSR1 handler to dump hostapd state to a text file
- */
-static void hostapd_dump_state(struct hostapd_data *hapd)
-{
- FILE *f;
- time_t now;
- struct sta_info *sta;
- int i;
-#ifndef CONFIG_NO_RADIUS
- char *buf;
-#endif /* CONFIG_NO_RADIUS */
- struct hostap_sta_driver_data data;
-
- if (!hapd->conf->dump_log_name) {
- wpa_printf(MSG_DEBUG, "Dump file not defined - ignoring dump "
- "request");
- return;
- }
-
- wpa_printf(MSG_DEBUG, "Dumping hostapd state to '%s'",
- hapd->conf->dump_log_name);
- f = fopen(hapd->conf->dump_log_name, "w");
- if (f == NULL) {
- wpa_printf(MSG_WARNING, "Could not open dump file '%s' for "
- "writing.", hapd->conf->dump_log_name);
- return;
- }
-
- time(&now);
- fprintf(f, "hostapd state dump - %s", ctime(&now));
- fprintf(f, "num_sta=%d num_sta_non_erp=%d "
- "num_sta_no_short_slot_time=%d\n"
- "num_sta_no_short_preamble=%d\n",
- hapd->num_sta, hapd->iface->num_sta_non_erp,
- hapd->iface->num_sta_no_short_slot_time,
- hapd->iface->num_sta_no_short_preamble);
-
- for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
- fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr));
-
- fprintf(f,
- " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
- "\n"
- " capability=0x%x listen_interval=%d\n",
- sta->aid,
- sta->flags,
- (sta->flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
- (sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
- (sta->flags & WLAN_STA_PS ? "[PS]" : ""),
- (sta->flags & WLAN_STA_TIM ? "[TIM]" : ""),
- (sta->flags & WLAN_STA_PERM ? "[PERM]" : ""),
- (ap_sta_is_authorized(sta) ? "[AUTHORIZED]" : ""),
- (sta->flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" :
- ""),
- (sta->flags & WLAN_STA_SHORT_PREAMBLE ?
- "[SHORT_PREAMBLE]" : ""),
- (sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""),
- (sta->flags & WLAN_STA_WMM ? "[WMM]" : ""),
- (sta->flags & WLAN_STA_MFP ? "[MFP]" : ""),
- (sta->flags & WLAN_STA_WPS ? "[WPS]" : ""),
- (sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
- (sta->flags & WLAN_STA_WDS ? "[WDS]" : ""),
- (sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
- (sta->flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
- sta->capability,
- sta->listen_interval);
-
- fprintf(f, " supported_rates=");
- for (i = 0; i < sta->supported_rates_len; i++)
- fprintf(f, "%02x ", sta->supported_rates[i]);
- fprintf(f, "\n");
-
- fprintf(f,
- " timeout_next=%s\n",
- (sta->timeout_next == STA_NULLFUNC ? "NULLFUNC POLL" :
- (sta->timeout_next == STA_DISASSOC ? "DISASSOC" :
- "DEAUTH")));
-
- ieee802_1x_dump_state(f, " ", sta);
-
- if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) == 0) {
- fprintf(f, " rx_pkt=%lu tx_pkt=%lu\n"
- " rx_byte=%lu tx_byte=%lu\n",
- data.rx_packets, data.tx_packets,
- data.rx_bytes, data.tx_bytes);
- }
- }
-
-#ifndef CONFIG_NO_RADIUS
- buf = os_malloc(4096);
- if (buf) {
- int count = radius_client_get_mib(hapd->radius, buf, 4096);
- if (count < 0)
- count = 0;
- else if (count > 4095)
- count = 4095;
- buf[count] = '\0';
- fprintf(f, "%s", buf);
-
-#ifdef RADIUS_SERVER
- count = radius_server_get_mib(hapd->radius_srv, buf, 4096);
- if (count < 0)
- count = 0;
- else if (count > 4095)
- count = 4095;
- buf[count] = '\0';
- fprintf(f, "%s", buf);
-#endif /* RADIUS_SERVER */
-
- os_free(buf);
- }
-#endif /* CONFIG_NO_RADIUS */
- fclose(f);
-}
-
-
-int handle_dump_state_iface(struct hostapd_iface *iface, void *ctx)
-{
- size_t i;
-
- for (i = 0; i < iface->num_bss; i++)
- hostapd_dump_state(iface->bss[i]);
-
- return 0;
-}
diff --git a/hostapd/dump_state.h b/hostapd/dump_state.h
deleted file mode 100644
index a209d65..0000000
--- a/hostapd/dump_state.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * hostapd / State dump
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef DUMP_STATE_H
-#define DUMP_STATE_H
-
-int handle_dump_state_iface(struct hostapd_iface *iface, void *ctx);
-
-#endif /* DUMP_STATE_H */
diff --git a/hostapd/eap_register.c b/hostapd/eap_register.c
index 981e539..8477c21 100644
--- a/hostapd/eap_register.c
+++ b/hostapd/eap_register.c
@@ -44,6 +44,13 @@
ret = eap_server_unauth_tls_register();
#endif /* EAP_SERVER_TLS */
+#ifdef EAP_SERVER_TLS
+#ifdef CONFIG_HS20
+ if (ret == 0)
+ ret = eap_server_wfa_unauth_tls_register();
+#endif /* CONFIG_HS20 */
+#endif /* EAP_SERVER_TLS */
+
#ifdef EAP_SERVER_MSCHAPV2
if (ret == 0)
ret = eap_server_mschapv2_register();
diff --git a/hostapd/hapd_module_tests.c b/hostapd/hapd_module_tests.c
new file mode 100644
index 0000000..f7887eb
--- /dev/null
+++ b/hostapd/hapd_module_tests.c
@@ -0,0 +1,17 @@
+/*
+ * hostapd module tests
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+
+int hapd_module_tests(void)
+{
+ wpa_printf(MSG_INFO, "hostapd module tests");
+ return 0;
+}
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index e04e2e9..c041887 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
/*
* HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -18,6 +18,9 @@
* SIM-REQ-AUTH <IMSI> <max_chal>
* SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3]
* SIM-RESP-AUTH <IMSI> FAILURE
+ * GSM-AUTH-REQ <IMSI> RAND1:RAND2[:RAND3]
+ * GSM-AUTH-RESP <IMSI> Kc1:SRES1:Kc2:SRES2[:Kc3:SRES3]
+ * GSM-AUTH-RESP <IMSI> FAILURE
*
* EAP-AKA / UMTS query/response:
* AKA-REQ-AUTH <IMSI>
@@ -30,12 +33,16 @@
* IMSI and max_chal are sent as an ASCII string,
* Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings.
*
- * The example implementation here reads GSM authentication triplets from a
+ * 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.
@@ -58,6 +65,7 @@
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 {
@@ -214,6 +222,9 @@
{
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';
@@ -611,31 +622,30 @@
}
-static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
- char *imsi)
+static int sim_req_auth(char *imsi, char *resp, size_t resp_len)
{
int count, max_chal, ret;
char *pos;
- char reply[1000], *rpos, *rend;
+ char *rpos, *rend;
struct milenage_parameters *m;
struct gsm_triplet *g;
- reply[0] = '\0';
+ resp[0] = '\0';
pos = strchr(imsi, ' ');
if (pos) {
*pos++ = '\0';
max_chal = atoi(pos);
- if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL)
+ if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL)
max_chal = EAP_SIM_MAX_CHAL;
} else
max_chal = EAP_SIM_MAX_CHAL;
- rend = &reply[sizeof(reply)];
- rpos = reply;
+ rend = resp + resp_len;
+ rpos = resp;
ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
if (ret < 0 || ret >= rend - rpos)
- return;
+ return -1;
rpos += ret;
m = get_milenage(imsi);
@@ -643,7 +653,7 @@
u8 _rand[16], sres[4], kc[8];
for (count = 0; count < max_chal; count++) {
if (random_get_bytes(_rand, 16) < 0)
- return;
+ return -1;
gsm_milenage(m->opc, m->ki, _rand, sres, kc);
*rpos++ = ' ';
rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
@@ -653,7 +663,7 @@
rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
}
*rpos = '\0';
- goto send;
+ return 0;
}
count = 0;
@@ -677,15 +687,61 @@
printf("No GSM triplets found for %s\n", imsi);
ret = snprintf(rpos, rend - rpos, " FAILURE");
if (ret < 0 || ret >= rend - rpos)
- return;
+ return -1;
rpos += ret;
}
-send:
- printf("Send: %s\n", reply);
- if (sendto(s, reply, rpos - reply, 0,
- (struct sockaddr *) from, fromlen) < 0)
- perror("send");
+ 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;
}
@@ -711,11 +767,10 @@
}
-static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
- char *imsi)
+static int aka_req_auth(char *imsi, char *resp, size_t resp_len)
{
/* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
- char reply[1000], *pos, *end;
+ char *pos, *end;
u8 _rand[EAP_AKA_RAND_LEN];
u8 autn[EAP_AKA_AUTN_LEN];
u8 ik[EAP_AKA_IK_LEN];
@@ -729,16 +784,18 @@
m = get_milenage(imsi);
if (m) {
if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
- return;
+ 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;
- 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]);
+ 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 {
@@ -756,18 +813,18 @@
#endif /* AKA_USE_FIXED_TEST_VALUES */
}
- pos = reply;
- end = &reply[sizeof(reply)];
+ pos = resp;
+ end = resp + resp_len;
ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
if (ret < 0 || ret >= end - pos)
- return;
+ return -1;
pos += ret;
if (failed) {
ret = snprintf(pos, end - pos, "FAILURE");
if (ret < 0 || ret >= end - pos)
- return;
+ return -1;
pos += ret;
- goto done;
+ return 0;
}
pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
*pos++ = ' ';
@@ -779,65 +836,87 @@
*pos++ = ' ';
pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
-done:
- printf("Send: %s\n", reply);
-
- if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
- fromlen) < 0)
- perror("send");
+ return 0;
}
-static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
- char *imsi)
+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 <IMSI> <AUTS> <RAND> */
auts = strchr(imsi, ' ');
if (auts == NULL)
- return;
+ return -1;
*auts++ = '\0';
__rand = strchr(auts, ' ');
if (__rand == NULL)
- return;
+ return -1;
*__rand++ = '\0';
- printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand);
+ 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;
+ return -1;
}
m = get_milenage(imsi);
if (m == NULL) {
printf("Unknown IMSI: %s\n", imsi);
- return;
+ 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);
- 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]);
+ 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];
+ char buf[1000], resp[1000];
struct sockaddr_un from;
socklen_t fromlen;
ssize_t res;
@@ -859,14 +938,21 @@
printf("Received: %s\n", buf);
- if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0)
- sim_req_auth(s, &from, fromlen, buf + 13);
- else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0)
- aka_req_auth(s, &from, fromlen, buf + 13);
- else if (strncmp(buf, "AKA-AUTS ", 9) == 0)
- aka_auts(s, &from, fromlen, buf + 9);
- else
- printf("Unknown request: %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;
}
@@ -894,8 +980,10 @@
os_free(prev);
}
- close(serv_sock);
- unlink(socket_path);
+ if (serv_sock >= 0)
+ close(serv_sock);
+ if (socket_path)
+ unlink(socket_path);
#ifdef CONFIG_SQLITE
if (sqlite_db) {
@@ -917,12 +1005,12 @@
{
printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
"database/authenticator\n"
- "Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>\n"
+ "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>\n"
"\n"
"usage:\n"
"hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
"[-m<milenage file>] \\\n"
- " [-D<DB file>] [-i<IND len in bits>]\n"
+ " [-D<DB file>] [-i<IND len in bits>] [command]\n"
"\n"
"options:\n"
" -h = show this usage help\n"
@@ -932,7 +1020,15 @@
" -g<triplet file> = path for GSM authentication triplets\n"
" -m<milenage file> = path for Milenage keys\n"
" -D<DB file> = path to SQLite database\n"
- " -i<IND len in bits> = IND length for SQN (default: 5)\n",
+ " -i<IND len in bits> = IND length for SQN (default: 5)\n"
+ "\n"
+ "If the optional command argument, like "
+ "\"AKA-REQ-AUTH <IMSI>\" 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);
}
@@ -942,6 +1038,7 @@
int c;
char *gsm_triplet_file = NULL;
char *sqlite_db_file = NULL;
+ int ret = 0;
if (os_program_init())
return -1;
@@ -1005,18 +1102,31 @@
if (milenage_file && read_milenage(milenage_file) < 0)
return -1;
- serv_sock = open_socket(socket_path);
- if (serv_sock < 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);
+ printf("Listening for requests on %s\n", socket_path);
- atexit(cleanup);
- signal(SIGTERM, handle_term);
- signal(SIGINT, handle_term);
+ atexit(cleanup);
+ signal(SIGTERM, handle_term);
+ signal(SIGINT, handle_term);
- for (;;)
- process(serv_sock);
+ 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) {
@@ -1027,5 +1137,5 @@
os_program_deinit();
- return 0;
+ return ret;
}
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 45897ed..a7ab0f6 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -51,9 +51,6 @@
logger_stdout=-1
logger_stdout_level=2
-# Dump file for state information (on SIGUSR1)
-dump_file=/tmp/hostapd.dump
-
# 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
@@ -111,6 +108,20 @@
# (default: 0 = disabled)
#ieee80211h=1
+# Add Power Constraint element to Beacon and Probe Response frames
+# This config option adds Power Constraint element when applicable and Country
+# element is added. Power Constraint element is required by Transmit Power
+# Control. This can be used only with ieee80211d=1.
+# Valid values are 0..255.
+#local_pwr_constraint=3
+
+# Set Spectrum Management subfield in the Capability Information field.
+# This config option forces the Spectrum Management bit to be set. When this
+# option is not set, the value of the Spectrum Management bit depends on whether
+# DFS or TPC is required by regulatory authorities. This can be used only with
+# ieee80211d=1 and local_pwr_constraint configured.
+#spectrum_mgmt_required=1
+
# Operation mode (a = IEEE 802.11a, 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)
@@ -143,6 +154,14 @@
# Defaults:
#acs_num_scans=5
+# Channel list restriction. This option allows hostapd to select one of the
+# provided channels when a channel should be automatically selected. This
+# is currently only used for DFS when the current channels becomes unavailable
+# due to radar interference, and is currently only useful when ieee80211h=1 is
+# set.
+# Default: not set (allow any enabled channel to be selected)
+#chanlist=100 104 108 112 116
+
# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
beacon_int=100
@@ -416,6 +435,12 @@
# 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
+# <station count>:<channel utilization>:<available admission capacity>
+#bss_load_test=12:80:20000
+
##### IEEE 802.11n related configuration ######################################
# ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -428,7 +453,7 @@
# 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
+# with secondary channel above the primary channel
# (20 MHz only if neither is set)
# Note: There are limits on which channels can be used with HT40- and
# HT40+. Following table shows the channels that may be available for
@@ -455,13 +480,20 @@
# 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)
+# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set)
# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
#ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40]
# Require stations to support HT PHY (reject association if they do not)
#require_ht=1
+# If set non-zero, require stations to perform scans of overlapping
+# channels to test for stations which would be affected by 40 MHz traffic.
+# This parameter sets the interval in seconds between these scans. 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
@@ -961,6 +993,11 @@
# The UDP port number for the RADIUS authentication server
#radius_server_auth_port=1812
+# The UDP port number for the RADIUS accounting server
+# Commenting this out or setting this to 0 can be used to disable RADIUS
+# accounting while still enabling RADIUS authentication.
+#radius_server_acct_port=1813
+
# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API)
#radius_server_ipv6=1
@@ -1067,6 +1104,17 @@
# 2 = required
#ieee80211w=0
+# Group management cipher suite
+# Default: AES-128-CMAC (BIP)
+# Other options (depending on driver support):
+# BIP-GMAC-128
+# BIP-GMAC-256
+# BIP-CMAC-256
+# Note: All the stations connecting to the BSS will also need to support the
+# selected cipher. The default AES-128-CMAC is the only option that is commonly
+# available in deployed devices.
+#group_mgmt_cipher=AES-128-CMAC
+
# Association SA Query maximum timeout (in TU = 1.024 ms; for MFP)
# (maximum time to wait for a SA Query response)
# dot11AssociationSAQueryMaximumTimeout, 1...4294967295
@@ -1457,6 +1505,9 @@
# 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
@@ -1508,6 +1559,8 @@
# accordance with IETF RFC 4282
# NAI Realm(s): Semi-colon delimited NAI Realm(s)
# EAP Method: <EAP Method>[:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...]
+# EAP Method types, see:
+# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4
# AuthParam (Table 8-188 in IEEE Std 802.11-2012):
# ID 2 = Non-EAP Inner Authentication Type
# 1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2
@@ -1521,6 +1574,23 @@
# 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:
+# [<DSCP Exceptions[DSCP,UP]>,]<UP 0 range[low,high]>,...<UP 7 range[low,high]>
+#
+# 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
@@ -1533,6 +1603,21 @@
# forging such frames to other stations in the BSS.
#disable_dgaf=1
+# OSU Server-Only Authenticated L2 Encryption Network
+#osen=1
+
+# ANQP Domain ID (0..65535)
+# An identifier for a set of APs in an ESS that share the same common ANQP
+# information. 0 = Some of the ANQP information is unique to this AP (default).
+#anqp_domain_id=1234
+
+# Deauthentication request timeout
+# If the RADIUS server indicates that the station is not allowed to connect to
+# the BSS/ESS, the AP can allow the station some time to download a
+# notification page (URL included in the message). This parameter sets that
+# timeout in seconds.
+#hs20_deauth_req_timeout=60
+
# Operator Friendly Name
# This parameter can be used to configure one or more Operator Friendly Name
# Duples. Each entry has a two or three character language code (ISO-639)
@@ -1576,6 +1661,32 @@
# channels 36-48):
#hs20_operating_class=5173
+# OSU icons
+# <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path>
+#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
+#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
+
+# OSU SSID (see ssid2 for format description)
+# This is the SSID used for all OSU connections to all the listed OSU Providers.
+#osu_ssid="example"
+
+# OSU Providers
+# One or more sets of following parameter. Each OSU provider is started by the
+# mandatory osu_server_uri item. The other parameters add information for the
+# last added OSU provider.
+#
+#osu_server_uri=https://example.com/osu/
+#osu_friendly_name=eng:Example operator
+#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
+#osu_nai=anonymous@example.com
+#osu_method_list=1 0
+#osu_icon=icon32
+#osu_icon=icon64
+#osu_service_desc=eng:Example services
+#osu_service_desc=fin:Esimerkkipalveluja
+#
+#osu_server_uri=...
+
##### TESTING OPTIONS #########################################################
#
# The options in this section are only available when the build configuration
@@ -1619,6 +1730,11 @@
# - is not the same as the MAC address of the radio
# - is not the same as any other explicitly specified BSSID
#
+# Not all drivers support multiple BSSes. The exact mechanism for determining
+# the driver capabilities is driver specific. With the current (i.e., a recent
+# kernel) drivers using nl80211, this information can be checked with "iw list"
+# (search for "valid interface combinations").
+#
# Please note that hostapd uses some of the values configured for the first BSS
# as the defaults for the following BSSes. However, it is recommended that all
# BSSes include explicit configuration of all relevant configuration items.
diff --git a/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user
index 12a2c61..00edc95 100644
--- a/hostapd/hostapd.eap_user
+++ b/hostapd/hostapd.eap_user
@@ -48,6 +48,12 @@
# TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a
# plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password
# hash.
+#
+# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly
+# to the way radius_auth_req_attr is used for Access-Request packet in
+# hostapd.conf. For EAP server, this is configured separately for each user
+# entry with radius_accept_attr=<value> line(s) following the main user entry
+# line.
# Phase 1 users
"user" MD5 "password"
diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite
index f688327..826db34 100644
--- a/hostapd/hostapd.eap_user_sqlite
+++ b/hostapd/hostapd.eap_user_sqlite
@@ -2,6 +2,7 @@
identity TEXT PRIMARY KEY,
methods TEXT,
password TEXT,
+ remediation TEXT,
phase2 INTEGER
);
@@ -15,3 +16,11 @@
INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS');
INSERT INTO wildcards(identity,methods) VALUES ('0','AKA');
+
+CREATE TABLE authlog(
+ timestamp TEXT,
+ session TEXT,
+ nas_ip TEXT,
+ username TEXT,
+ note TEXT
+);
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 7187abc..95f28d3 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1,6 +1,6 @@
/*
* hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -18,7 +18,7 @@
static const char *hostapd_cli_version =
"hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> and contributors";
static const char *hostapd_cli_license =
@@ -216,8 +216,21 @@
}
+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");
}
@@ -225,28 +238,19 @@
static int hostapd_cli_exec(const char *program, const char *arg1,
const char *arg2)
{
- char *cmd;
+ char *arg;
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)
+ len = os_strlen(arg1) + os_strlen(arg2) + 2;
+ arg = os_malloc(len);
+ if (arg == 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);
+ os_snprintf(arg, len, "%s %s", arg1, arg2);
+ res = os_exec(program, arg, 1);
+ os_free(arg);
- return ret;
+ return res;
}
@@ -270,12 +274,15 @@
static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char buf[64];
- if (argc != 1) {
- printf("Invalid 'sta' command - exactly one argument, STA "
+ if (argc < 1) {
+ printf("Invalid 'sta' command - at least one argument, STA "
"address, is required.\n");
return -1;
}
- snprintf(buf, sizeof(buf), "STA %s", argv[0]);
+ 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);
}
@@ -688,6 +695,90 @@
}
+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_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[300];
+ int res;
+
+ if (argc < 2) {
+ printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
+ "addr and URL) are needed\n");
+ return -1;
+ }
+
+ res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || res >= (int) sizeof(buf))
+ return -1;
+ return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[300];
+ int res;
+
+ if (argc < 3) {
+ printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
+ return -1;
+ }
+
+ if (argc > 3)
+ res = os_snprintf(buf, sizeof(buf),
+ "HS20_DEAUTH_REQ %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ else
+ res = os_snprintf(buf, sizeof(buf),
+ "HS20_DEAUTH_REQ %s %s %s",
+ argv[0], argv[1], argv[2]);
+ if (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;
@@ -742,8 +833,8 @@
}
hostapd_cli_close_connection();
- free(ctrl_ifname);
- ctrl_ifname = strdup(argv[0]);
+ os_free(ctrl_ifname);
+ ctrl_ifname = os_strdup(argv[0]);
if (hostapd_cli_open_connection(ctrl_ifname)) {
printf("Connected to interface '%s.\n", ctrl_ifname);
@@ -801,6 +892,66 @@
}
+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: <cs_count> <freq> [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);
+}
+
+
+static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc < 2 || argc > 3) {
+ printf("Invalid vendor command\n"
+ "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
+ argc == 3 ? argv[2] : "");
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long VENDOR command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -810,6 +961,7 @@
{ "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 },
@@ -843,6 +995,12 @@
{ "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 },
+ { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
+ { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
+ { "vendor", hostapd_cli_cmd_vendor },
{ NULL, NULL }
};
diff --git a/hostapd/main.c b/hostapd/main.c
index 90e5966..af4d85d 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -14,6 +14,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
+#include "utils/uuid.h"
#include "crypto/random.h"
#include "crypto/tls.h"
#include "common/version.h"
@@ -25,16 +26,10 @@
#include "ap/ap_drv_ops.h"
#include "config_file.h"
#include "eap_register.h"
-#include "dump_state.h"
#include "ctrl_iface.h"
-
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
-extern struct wpa_driver_ops *wpa_drivers[];
-
+struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
+ struct wpa_driver_capa *capa);
struct hapd_global {
void **drv_priv;
@@ -99,7 +94,8 @@
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);
+ module_str ? " " : "", module_str ? module_str : "",
+ txt);
else if (hapd && hapd->conf)
os_snprintf(format, maxlen, "%s:%s%s %s",
hapd->conf->iface, module_str ? " " : "",
@@ -114,7 +110,7 @@
if ((conf_stdout & module) && level >= conf_stdout_level) {
wpa_debug_print_timestamp();
- printf("%s\n", format);
+ wpa_printf(MSG_INFO, "%s", format);
}
#ifndef CONFIG_NATIVE_WINDOWS
@@ -148,65 +144,8 @@
/**
- * 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().
+ * hostapd_driver_init - Preparate driver interface
*/
-static struct hostapd_iface * hostapd_init(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 = hostapd_config_read(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);
- os_free(hapd_iface);
- }
- return NULL;
-}
-
-
static int hostapd_driver_init(struct hostapd_iface *iface)
{
struct wpa_init_params params;
@@ -274,18 +213,34 @@
if (hapd->driver->get_capa &&
hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
+ struct wowlan_triggers *triggs;
+
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;
+
+ triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
+ if (triggs && hapd->driver->set_wowlan) {
+ if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
+ wpa_printf(MSG_ERROR, "set_wowlan failed");
+ }
+ os_free(triggs);
}
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)
@@ -294,7 +249,7 @@
int k;
wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
- iface = hostapd_init(config_fname);
+ iface = hostapd_init(interfaces, config_fname);
if (!iface)
return NULL;
iface->interfaces = interfaces;
@@ -304,7 +259,7 @@
iface->bss[0]->conf->logger_stdout_level--;
}
- if (iface->conf->bss[0].iface[0] == '\0' &&
+ 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);
@@ -312,12 +267,6 @@
return NULL;
}
- if (hostapd_driver_init(iface) ||
- hostapd_setup_interface(iface)) {
- hostapd_interface_deinit_free(iface);
- return NULL;
- }
-
return iface;
}
@@ -358,10 +307,7 @@
static void handle_dump_state(int sig, void *signal_ctx)
{
-#ifdef HOSTAPD_DUMP_STATE
- struct hapd_interfaces *interfaces = signal_ctx;
- hostapd_for_each_interface(interfaces, handle_dump_state_iface, NULL);
-#endif /* HOSTAPD_DUMP_STATE */
+ /* Not used anymore - ignore signal */
}
#endif /* CONFIG_NATIVE_WINDOWS */
@@ -480,7 +426,7 @@
"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-2013, Jouni Malinen <j@w1.fi> "
+ "Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> "
"and contributors\n");
}
@@ -507,6 +453,10 @@
#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");
@@ -517,8 +467,9 @@
static const char * hostapd_msg_ifname_cb(void *ctx)
{
struct hostapd_data *hapd = ctx;
- if (hapd && hapd->iconf && hapd->iconf->bss)
- return hapd->iconf->bss->iface;
+ if (hapd && hapd->iconf && hapd->iconf->bss &&
+ hapd->iconf->num_bss > 0 && hapd->iconf->bss[0])
+ return hapd->iconf->bss[0]->iface;
return NULL;
}
@@ -563,15 +514,41 @@
}
+#ifdef CONFIG_WPS
+static int gen_uuid(const char *txt_addr)
+{
+ u8 addr[ETH_ALEN];
+ u8 uuid[UUID_LEN];
+ char buf[100];
+
+ if (hwaddr_aton(txt_addr, addr) < 0)
+ return -1;
+
+ uuid_gen_mac_addr(addr, uuid);
+ if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0)
+ return -1;
+
+ printf("%s\n", buf);
+
+ return 0;
+}
+#endif /* CONFIG_WPS */
+
+
int main(int argc, char *argv[])
{
struct hapd_interfaces interfaces;
int ret = 1;
- size_t i;
+ 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;
@@ -588,7 +565,7 @@
interfaces.global_ctrl_sock = -1;
for (;;) {
- c = getopt(argc, argv, "Bde:f:hKP:tvg:G:");
+ c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
if (c < 0)
break;
switch (c) {
@@ -619,6 +596,11 @@
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);
@@ -631,23 +613,46 @@
if (hostapd_get_ctrl_iface_group(&interfaces, optarg))
return -1;
break;
+ case 'b':
+ tmp_bss = os_realloc_array(bss_config,
+ num_bss_configs + 1,
+ sizeof(char *));
+ if (tmp_bss == NULL)
+ goto out;
+ bss_config = tmp_bss;
+ bss_config[num_bss_configs++] = optarg;
+ break;
+#ifdef CONFIG_WPS
+ case 'u':
+ return gen_uuid(optarg);
+#endif /* CONFIG_WPS */
default:
usage();
break;
}
}
- if (optind == argc && interfaces.global_iface_path == NULL)
+ 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) {
- interfaces.iface = os_calloc(interfaces.count,
+ 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");
@@ -660,7 +665,7 @@
return -1;
}
- /* Initialize interfaces */
+ /* 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],
@@ -671,6 +676,57 @@
}
}
+ /* 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)) {
@@ -683,8 +739,14 @@
out:
hostapd_global_ctrl_iface_deinit(&interfaces);
/* Deinitialize all interfaces */
- for (i = 0; i < interfaces.count; i++)
+ for (i = 0; i < interfaces.count; i++) {
+ if (!interfaces.iface[i])
+ continue;
+ interfaces.iface[i]->driver_ap_teardown =
+ !!(interfaces.iface[i]->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
hostapd_interface_deinit_free(interfaces.iface[i]);
+ }
os_free(interfaces.iface);
hostapd_global_deinit(pid_file);
@@ -692,6 +754,9 @@
if (log_file)
wpa_debug_close_file();
+ wpa_debug_close_linux_tracing();
+
+ os_free(bss_config);
os_program_deinit();
diff --git a/hostapd/wps-ap-nfc.py b/hostapd/wps-ap-nfc.py
index 61b5519..2fc3012 100755
--- a/hostapd/wps-ap-nfc.py
+++ b/hostapd/wps-ap-nfc.py
@@ -9,6 +9,7 @@
import os
import sys
import time
+import argparse
import nfc
import nfc.ndef
@@ -16,11 +17,25 @@
import nfc.handover
import logging
-logging.basicConfig()
import wpaspy
wpas_ctrl = '/var/run/hostapd'
+continue_loop = True
+summary_file = None
+success_file = None
+
+def summary(txt):
+ print txt
+ if summary_file:
+ with open(summary_file, 'a') as f:
+ f.write(txt + "\n")
+
+def success_report(txt):
+ summary(txt)
+ if success_file:
+ with open(success_file, 'a') as f:
+ f.write(txt + "\n")
def wpas_connect():
ifaces = []
@@ -47,29 +62,40 @@
def wpas_tag_read(message):
wpas = wpas_connect()
if (wpas == None):
- return
- print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex"))
+ return False
+ if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
+ return False
+ return True
def wpas_get_config_token():
wpas = wpas_connect()
if (wpas == None):
return None
- return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex")
+ ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
+ if "FAIL" in ret:
+ return None
+ return ret.rstrip().decode("hex")
def wpas_get_password_token():
wpas = wpas_connect()
if (wpas == None):
return None
- return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
+ ret = wpas.request("WPS_NFC_TOKEN NDEF")
+ if "FAIL" in ret:
+ return None
+ return ret.rstrip().decode("hex")
def wpas_get_handover_sel():
wpas = wpas_connect()
if (wpas == None):
return None
- return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
+ ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR")
+ if "FAIL" in ret:
+ return None
+ return ret.rstrip().decode("hex")
def wpas_report_handover(req, sel):
@@ -82,185 +108,228 @@
class HandoverServer(nfc.handover.HandoverServer):
- def __init__(self):
- super(HandoverServer, self).__init__()
+ def __init__(self, llc):
+ super(HandoverServer, self).__init__(llc)
+ self.ho_server_processing = False
+ self.success = False
+
+ # override to avoid parser error in request/response.pretty() in nfcpy
+ # due to new WSC handover format
+ def _process_request(self, request):
+ summary("received handover request {}".format(request.type))
+ response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+ if not request.type == 'urn:nfc:wkt:Hr':
+ summary("not a handover request")
+ else:
+ try:
+ request = nfc.ndef.HandoverRequestMessage(request)
+ except nfc.ndef.DecodeError as e:
+ summary("error decoding 'Hr' message: {}".format(e))
+ else:
+ response = self.process_request(request)
+ summary("send handover response {}".format(response.type))
+ return response
def process_request(self, request):
- print "HandoverServer - request received"
- print "Parsed handover request: " + request.pretty()
+ summary("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"
- self.received_carrier = carrier.record
+ summary("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"
+ summary("Could not get handover select carrier record from hostapd")
continue
print "Handover select carrier record from hostapd:"
print data.encode("hex")
- self.sent_carrier = data
+ if "OK" in wpas_report_handover(carrier.record, data):
+ success_report("Handover reported successfully")
+ else:
+ summary("Handover report rejected")
message = nfc.ndef.Message(data);
sel.add_carrier(message[0], "active", message[1:])
print "Handover select:"
- print sel.pretty()
+ try:
+ print sel.pretty()
+ except Exception, e:
+ print e
print str(sel).encode("hex")
- print "Sending handover select"
+ summary("Sending handover select")
+ self.success = True
return sel
-def wps_handover_resp(peer):
- print "Trying to handle WPS handover"
-
- srv = HandoverServer()
- srv.sent_carrier = None
-
- nfc.llcp.activate(peer);
-
- try:
- print "Trying handover";
- srv.start()
- print "Wait for disconnect"
- while nfc.llcp.connected():
- time.sleep(0.1)
- print "Disconnected after handover"
- except nfc.llcp.ConnectRefused:
- print "Handover connection refused"
- nfc.llcp.shutdown()
- return
-
- if srv.sent_carrier:
- wpas_report_handover(srv.received_carrier, srv.sent_carrier)
-
- print "Remove peer"
- nfc.llcp.shutdown()
- print "Done with handover"
-
-
def wps_tag_read(tag):
+ success = False
if len(tag.ndef.message):
- message = nfc.ndef.Message(tag.ndef.message)
- print "message type " + message.type
-
- for record in 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"
- wpas_tag_read(tag.ndef.message)
+ summary("WPS tag - send to hostapd")
+ success = wpas_tag_read(tag.ndef.message)
break
else:
- print "Empty tag"
+ summary("Empty tag")
- print "Remove tag"
- while tag.is_present:
+ if success:
+ success_report("Tag read succeeded")
+
+ return success
+
+
+def rdwr_connected_write(tag):
+ summary("Tag found - writing - " + str(tag))
+ global write_data
+ tag.ndef.message = str(write_data)
+ success_report("Tag write succeeded")
+ print "Done - remove tag"
+ global only_one
+ if only_one:
+ global continue_loop
+ continue_loop = False
+ global write_wait_remove
+ while write_wait_remove and tag.is_present:
time.sleep(0.1)
-
-def wps_write_config_tag(clf):
- print "Write WPS config token"
- data = wpas_get_config_token()
- if (data == None):
- print "Could not get WPS config token from hostapd"
+def wps_write_config_tag(clf, wait_remove=True):
+ summary("Write WPS config token")
+ global write_data, write_wait_remove
+ write_wait_remove = wait_remove
+ write_data = wpas_get_config_token()
+ if write_data == None:
+ summary("Could not get WPS config token from hostapd")
return
print "Touch an NFC tag"
- while True:
- tag = clf.poll()
- if tag == None:
- time.sleep(0.1)
- continue
- break
-
- print "Tag found - writing"
- tag.ndef.message = data
- print "Done - remove tag"
- while tag.is_present:
- time.sleep(0.1)
+ clf.connect(rdwr={'on-connect': rdwr_connected_write})
-def wps_write_password_tag(clf):
- print "Write WPS password token"
- data = wpas_get_password_token()
- if (data == None):
- print "Could not get WPS password token from hostapd"
+def wps_write_password_tag(clf, wait_remove=True):
+ summary("Write WPS password token")
+ global write_data, write_wait_remove
+ write_wait_remove = wait_remove
+ write_data = wpas_get_password_token()
+ if write_data == None:
+ summary("Could not get WPS password token from hostapd")
return
print "Touch an NFC tag"
- while True:
- tag = clf.poll()
- if tag == None:
- time.sleep(0.1)
- continue
- break
-
- print "Tag found - writing"
- tag.ndef.message = data
- print "Done - remove tag"
- while tag.is_present:
- time.sleep(0.1)
+ clf.connect(rdwr={'on-connect': rdwr_connected_write})
-def find_peer(clf):
- while True:
- if nfc.llcp.connected():
- print "LLCP connected"
- general_bytes = nfc.llcp.startup({})
- peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
- if isinstance(peer, nfc.DEP):
- print "listen -> DEP";
- if peer.general_bytes.startswith("Ffm"):
- print "Found DEP"
- return peer
- print "mismatch in general_bytes"
- print peer.general_bytes
+def rdwr_connected(tag):
+ global only_one, no_wait
+ summary("Tag connected: " + str(tag))
- peer = clf.poll(general_bytes)
- if isinstance(peer, nfc.DEP):
- print "poll -> DEP";
- if peer.general_bytes.startswith("Ffm"):
- print "Found DEP"
- return peer
- print "mismatch in general_bytes"
- print peer.general_bytes
+ 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:
+ summary("Not an NDEF tag - remove tag")
+ return True
- if peer:
- print "Found tag"
- return peer
+ return not no_wait
+
+
+def llcp_startup(clf, llc):
+ print "Start LLCP server"
+ global srv
+ srv = HandoverServer(llc)
+ return llc
+
+def llcp_connected(llc):
+ print "P2P LLCP connected"
+ global wait_connection
+ wait_connection = False
+ global srv
+ srv.start()
+ return True
def main():
clf = nfc.ContactlessFrontend()
+ parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
+ parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+ action='store_const', dest='loglevel',
+ help='verbose debug output')
+ parser.add_argument('-q', const=logging.WARNING, action='store_const',
+ dest='loglevel', help='be quiet')
+ parser.add_argument('--only-one', '-1', action='store_true',
+ help='run only one operation and exit')
+ parser.add_argument('--no-wait', action='store_true',
+ help='do not wait for tag to be removed before exiting')
+ parser.add_argument('--summary',
+ help='summary file for writing status updates')
+ parser.add_argument('--success',
+ help='success file for writing success update')
+ parser.add_argument('command', choices=['write-config',
+ 'write-password'],
+ nargs='?')
+ args = parser.parse_args()
+
+ global only_one
+ only_one = args.only_one
+
+ global no_wait
+ no_wait = args.no_wait
+
+ if args.summary:
+ global summary_file
+ summary_file = args.summary
+
+ if args.success:
+ global success_file
+ success_file = args.success
+
+ logging.basicConfig(level=args.loglevel)
+
try:
- if len(sys.argv) > 1 and sys.argv[1] == "write-config":
- wps_write_config_tag(clf)
+ if not clf.open("usb"):
+ print "Could not open connection with an NFC device"
raise SystemExit
- if len(sys.argv) > 1 and sys.argv[1] == "write-password":
- wps_write_password_tag(clf)
+ if args.command == "write-config":
+ wps_write_config_tag(clf, wait_remove=not args.no_wait)
raise SystemExit
- while True:
+ 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"
- tag = find_peer(clf)
- if isinstance(tag, nfc.DEP):
- wps_handover_resp(tag)
- continue
-
- if tag.ndef:
- wps_tag_read(tag)
- continue
-
- print "Not an NDEF tag - remove tag"
- while tag.is_present:
- time.sleep(0.1)
+ global srv
+ if only_one and srv and srv.success:
+ raise SystemExit
except KeyboardInterrupt:
raise SystemExit
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
new file mode 100644
index 0000000..63cbc6f
--- /dev/null
+++ b/hs20/client/Android.mk
@@ -0,0 +1,77 @@
+LOCAL_PATH := $(call my-dir)
+
+INCLUDES = $(LOCAL_PATH)
+INCLUDES += $(LOCAL_PATH)/../../src/utils
+INCLUDES += $(LOCAL_PATH)/../../src/common
+INCLUDES += $(LOCAL_PATH)/../../src
+INCLUDES += external/openssl/include
+INCLUDES += external/libxml2/include
+INCLUDES += external/curl/include
+INCLUDES += external/webkit/Source/WebKit/gtk
+ifneq ($(wildcard external/icu),)
+INCLUDES += external/icu/icu4c/source/common
+else
+INCLUDES += external/icu4c/common
+endif
+
+
+#GTKCFLAGS := $(shell pkg-config --cflags gtk+-2.0 webkit-1.0)
+#GTKLIBS := $(shell pkg-config --libs gtk+-2.0 webkit-1.0)
+#CFLAGS += $(GTKCFLAGS)
+#LIBS += $(GTKLIBS)
+
+L_CFLAGS += -DCONFIG_CTRL_IFACE
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+L_CFLAGS += -DLIBXML_SCHEMAS_ENABLED
+L_CFLAGS += -DLIBXML_REGEXP_ENABLED
+
+OBJS = spp_client.c
+OBJS += oma_dm_client.c
+OBJS += osu_client.c
+OBJS += est.c
+OBJS += ../../src/common/wpa_ctrl.c
+OBJS += ../../src/common/wpa_helpers.c
+OBJS += ../../src/utils/xml-utils.c
+#OBJS += ../../src/utils/browser-android.c
+OBJS += ../../src/utils/browser-wpadebug.c
+OBJS += ../../src/utils/wpabuf.c
+OBJS += ../../src/utils/eloop.c
+OBJS += ../../src/wps/httpread.c
+OBJS += ../../src/wps/http_server.c
+OBJS += ../../src/utils/xml_libxml2.c
+OBJS += ../../src/utils/http_curl.c
+OBJS += ../../src/utils/base64.c
+OBJS += ../../src/utils/os_unix.c
+L_CFLAGS += -DCONFIG_DEBUG_FILE
+OBJS += ../../src/utils/wpa_debug.c
+OBJS += ../../src/utils/common.c
+OBJS += ../../src/crypto/crypto_internal.c
+OBJS += ../../src/crypto/md5-internal.c
+OBJS += ../../src/crypto/sha1-internal.c
+OBJS += ../../src/crypto/sha256-internal.c
+
+L_CFLAGS += -DEAP_TLS_OPENSSL
+
+#CFLAGS += $(shell xml2-config --cflags)
+#LIBS += $(shell xml2-config --libs)
+
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := hs20-osu-client
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_SHARED_LIBRARIES += libcrypto libssl
+#LOCAL_SHARED_LIBRARIES += libxml2
+LOCAL_STATIC_LIBRARIES += libxml2
+LOCAL_SHARED_LIBRARIES += libicuuc
+LOCAL_SHARED_LIBRARIES += libcurl
+
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
new file mode 100644
index 0000000..ca67b54
--- /dev/null
+++ b/hs20/client/Makefile
@@ -0,0 +1,94 @@
+all: hs20-osu-client
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I../../src/utils
+CFLAGS += -I../../src/common
+CFLAGS += -I../../src
+
+ifndef CONFIG_NO_BROWSER
+ifndef CONFIG_BROWSER_SYSTEM
+GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkitgtk-3.0)
+GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkitgtk-3.0)
+CFLAGS += $(GTKCFLAGS)
+LIBS += $(GTKLIBS)
+endif
+endif
+
+OBJS=spp_client.o
+OBJS += oma_dm_client.o
+OBJS += osu_client.o
+OBJS += est.o
+OBJS += ../../src/utils/xml-utils.o
+CFLAGS += -DCONFIG_CTRL_IFACE
+CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+OBJS += ../../src/common/wpa_ctrl.o ../../src/common/wpa_helpers.o
+ifdef CONFIG_NO_BROWSER
+CFLAGS += -DCONFIG_NO_BROWSER
+else
+ifdef CONFIG_BROWSER_SYSTEM
+OBJS += ../../src/utils/eloop.o
+OBJS += ../../src/utils/wpabuf.o
+OBJS += ../../src/wps/httpread.o
+OBJS += ../../src/wps/http_server.o
+OBJS += ../../src/utils/browser-system.o
+else
+OBJS += ../../src/utils/browser.o
+endif
+endif
+OBJS += ../../src/utils/xml_libxml2.o
+OBJS += ../../src/utils/http_curl.o
+OBJS += ../../src/utils/base64.o
+OBJS += ../../src/utils/os_unix.o
+CFLAGS += -DCONFIG_DEBUG_FILE
+OBJS += ../../src/utils/wpa_debug.o
+OBJS += ../../src/utils/common.o
+OBJS += ../../src/crypto/crypto_internal.o
+OBJS += ../../src/crypto/md5-internal.o
+OBJS += ../../src/crypto/sha1-internal.o
+OBJS += ../../src/crypto/sha256-internal.o
+
+CFLAGS += $(shell xml2-config --cflags)
+LIBS += $(shell xml2-config --libs)
+LIBS += -lcurl
+
+CFLAGS += -DEAP_TLS_OPENSSL
+LIBS += -lssl -lcrypto
+
+hs20-osu-client: $(OBJS)
+ $(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
+ @$(E) " LD " $@
+
+%.o: %.c
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+
+clean:
+ rm -f core *~ *.o *.d hs20-osu-client
+ rm -f ../../src/utils/*.o
+ rm -f ../../src/utils/*.d
+ rm -f ../../src/common/*.o
+ rm -f ../../src/common/*.d
+ rm -f ../../src/crypto/*.o
+ rm -f ../../src/crypto/*.d
+ rm -f ../../src/wps/*.o
+ rm -f ../../src/wps/*.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hs20/client/devdetail.xml b/hs20/client/devdetail.xml
new file mode 100644
index 0000000..6d0389e
--- /dev/null
+++ b/hs20/client/devdetail.xml
@@ -0,0 +1,47 @@
+<DevDetail xmlns="urn:oma:mo:oma-dm-devdetail:1.0">
+ <Ext>
+ <org.wi-fi>
+ <Wi-Fi>
+ <EAPMethodList>
+ <EAPMethod1>
+ <EAPType>13</EAPType>
+ </EAPMethod1>
+ <EAPMethod2>
+ <EAPType>21</EAPType>
+ <InnerMethod>MS-CHAP-V2</InnerMethod>
+ </EAPMethod2>
+ <EAPMethod3>
+ <EAPType>18</EAPType>
+ </EAPMethod3>
+ <EAPMethod4>
+ <EAPType>23</EAPType>
+ </EAPMethod4>
+ <EAPMethod5>
+ <EAPType>50</EAPType>
+ </EAPMethod5>
+ </EAPMethodList>
+ <ManufacturingCertificate>false</ManufacturingCertificate>
+ <Wi-FiMACAddress>020102030405</Wi-FiMACAddress>
+ <IMSI>310026000000000</IMSI>
+ <IMEI_MEID>imei:490123456789012</IMEI_MEID>
+ <ClientTriggerRedirectURI>http://localhost:12345/</ClientTriggerRedirectURI>
+ <Ops>
+ <launchBrowserToURI></launchBrowserToURI>
+ <negotiateClientCertTLS></negotiateClientCertTLS>
+ <getCertificate></getCertificate>
+ </Ops>
+ </Wi-Fi>
+ </org.wi-fi>
+ </Ext>
+ <URI>
+ <MaxDepth>0</MaxDepth>
+ <MaxTotLen>0</MaxTotLen>
+ <MaxSegLen>0</MaxSegLen>
+ </URI>
+ <DevType>MobilePhone</DevType>
+ <OEM>Manufacturer</OEM>
+ <FwV>1.0</FwV>
+ <SwV>1.0</SwV>
+ <HwV>1.0</HwV>
+ <LrgObj>false</LrgObj>
+</DevDetail>
diff --git a/hs20/client/devinfo.xml b/hs20/client/devinfo.xml
new file mode 100644
index 0000000..d48a520
--- /dev/null
+++ b/hs20/client/devinfo.xml
@@ -0,0 +1,7 @@
+<DevInfo xmlns="urn:oma:mo:oma-dm-devinfo:1.0">
+ <DevId>urn:Example:HS20-station:123456</DevId>
+ <Man>Manufacturer</Man>
+ <Mod>HS20-station</Mod>
+ <DmV>1.2</DmV>
+ <Lang>en</Lang>
+</DevInfo>
diff --git a/hs20/client/est.c b/hs20/client/est.c
new file mode 100644
index 0000000..ec05bc4
--- /dev/null
+++ b/hs20/client/est.c
@@ -0,0 +1,715 @@
+/*
+ * Hotspot 2.0 OSU client - EST client
+ * Copyright (c) 2012-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 <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rsa.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "common.h"
+#include "utils/base64.h"
+#include "utils/xml-utils.h"
+#include "utils/http-utils.h"
+#include "osu_client.h"
+
+
+static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
+ size_t len, char *pem_file, char *der_file)
+{
+ PKCS7 *p7 = NULL;
+ const unsigned char *p = pkcs7;
+ STACK_OF(X509) *certs;
+ int i, num, ret = -1;
+ BIO *out = NULL;
+
+ p7 = d2i_PKCS7(NULL, &p, len);
+ if (p7 == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ write_result(ctx, "Could not parse PKCS#7 object from EST");
+ goto fail;
+ }
+
+ switch (OBJ_obj2nid(p7->type)) {
+ case NID_pkcs7_signed:
+ certs = p7->d.sign->cert;
+ break;
+ case NID_pkcs7_signedAndEnveloped:
+ certs = p7->d.signed_and_enveloped->cert;
+ break;
+ default:
+ certs = NULL;
+ break;
+ }
+
+ if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+ wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
+ write_result(ctx, "No certificates found in PKCS#7 object");
+ goto fail;
+ }
+
+ if (der_file) {
+ FILE *f = fopen(der_file, "wb");
+ if (f == NULL)
+ goto fail;
+ i2d_X509_fp(f, sk_X509_value(certs, 0));
+ fclose(f);
+ }
+
+ if (pem_file) {
+ out = BIO_new(BIO_s_file());
+ if (out == NULL ||
+ BIO_write_filename(out, pem_file) <= 0)
+ goto fail;
+
+ for (i = 0; i < num; i++) {
+ X509 *cert = sk_X509_value(certs, i);
+ X509_print(out, cert);
+ PEM_write_bio_X509(out, cert);
+ BIO_puts(out, "\n");
+ }
+ }
+
+ ret = 0;
+
+fail:
+ PKCS7_free(p7);
+ if (out)
+ BIO_free_all(out);
+
+ return ret;
+}
+
+
+int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
+{
+ char *buf, *resp;
+ size_t buflen;
+ unsigned char *pkcs7;
+ size_t pkcs7_len, resp_len;
+ int res;
+
+ buflen = os_strlen(url) + 100;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return -1;
+
+ os_snprintf(buf, buflen, "%s/cacerts", url);
+ wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
+ write_summary(ctx, "Download EST cacerts from %s", buf);
+ ctx->no_osu_cert_validation = 1;
+ http_ocsp_set(ctx->http, 1);
+ res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
+ ctx->ca_fname);
+ http_ocsp_set(ctx->http,
+ (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+ ctx->no_osu_cert_validation = 0;
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
+ buf);
+ write_result(ctx, "Failed to download EST cacerts from %s",
+ buf);
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+
+ resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
+ write_result(ctx, "Could not read EST cacerts");
+ return -1;
+ }
+
+ pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+ if (pkcs7 && pkcs7_len < resp_len / 2) {
+ wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
+ (unsigned int) pkcs7_len, (unsigned int) resp_len);
+ os_free(pkcs7);
+ pkcs7 = NULL;
+ }
+ if (pkcs7 == NULL) {
+ wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
+ pkcs7 = os_malloc(resp_len);
+ if (pkcs7) {
+ os_memcpy(pkcs7, resp, resp_len);
+ pkcs7_len = resp_len;
+ }
+ }
+ os_free(resp);
+
+ if (pkcs7 == NULL) {
+ wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
+ write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
+ return -1;
+ }
+
+ res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
+ NULL);
+ os_free(pkcs7);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
+ write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
+ return -1;
+ }
+ unlink("Cert/est-cacerts.txt");
+
+ return 0;
+}
+
+
+/*
+ * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
+ *
+ * AttrOrOID ::= CHOICE {
+ * oid OBJECT IDENTIFIER,
+ * attribute Attribute }
+ *
+ * Attribute ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
+ */
+
+typedef struct {
+ ASN1_OBJECT *type;
+ STACK_OF(ASN1_OBJECT) *values;
+} Attribute;
+
+typedef struct {
+ int type;
+ union {
+ ASN1_OBJECT *oid;
+ Attribute *attribute;
+ } d;
+} AttrOrOID;
+
+typedef struct {
+ int type;
+ STACK_OF(AttrOrOID) *attrs;
+} CsrAttrs;
+
+ASN1_SEQUENCE(Attribute) = {
+ ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
+ ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
+} ASN1_SEQUENCE_END(Attribute);
+
+ASN1_CHOICE(AttrOrOID) = {
+ ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
+ ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
+} ASN1_CHOICE_END(AttrOrOID);
+
+ASN1_CHOICE(CsrAttrs) = {
+ ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
+} ASN1_CHOICE_END(CsrAttrs);
+
+IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
+
+
+static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
+ STACK_OF(X509_EXTENSION) *exts)
+{
+ char txt[100];
+ int res;
+
+ if (!oid)
+ return;
+
+ res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ return;
+
+ if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
+ wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
+ } else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
+ wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
+ } else {
+ wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
+ }
+}
+
+
+static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
+ STACK_OF(ASN1_OBJECT) *values,
+ STACK_OF(X509_EXTENSION) *exts)
+{
+ char txt[100];
+ int i, num, res;
+
+ num = sk_ASN1_OBJECT_num(values);
+ for (i = 0; i < num; i++) {
+ ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
+
+ res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ continue;
+
+ if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
+ wpa_printf(MSG_INFO, "TODO: extReq macAddress");
+ } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
+ wpa_printf(MSG_INFO, "TODO: extReq imei");
+ } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
+ wpa_printf(MSG_INFO, "TODO: extReq meid");
+ } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
+ wpa_printf(MSG_INFO, "TODO: extReq DevId");
+ } else {
+ wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
+ txt);
+ }
+ }
+}
+
+
+static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
+ STACK_OF(X509_EXTENSION) *exts)
+{
+ char txt[100], txt2[100];
+ int i, num, res;
+
+ if (!attr || !attr->type || !attr->values)
+ return;
+
+ res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ return;
+
+ if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
+ add_csrattrs_ext_req(ctx, attr->values, exts);
+ return;
+ }
+
+ num = sk_ASN1_OBJECT_num(attr->values);
+ for (i = 0; i < num; i++) {
+ ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
+
+ res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
+ if (res < 0 || res >= (int) sizeof(txt2))
+ continue;
+
+ wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
+ txt, txt2);
+ }
+}
+
+
+static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
+ STACK_OF(X509_EXTENSION) *exts)
+{
+ int i, num;
+
+ if (!csrattrs || ! csrattrs->attrs)
+ return;
+
+ num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
+ for (i = 0; i < num; i++) {
+ AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
+ switch (ao->type) {
+ case 0:
+ add_csrattrs_oid(ctx, ao->d.oid, exts);
+ break;
+ case 1:
+ add_csrattrs_attr(ctx, ao->d.attribute, exts);
+ break;
+ }
+ }
+}
+
+
+static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
+ char *csr_pem, char *est_req, char *old_cert,
+ CsrAttrs *csrattrs)
+{
+ EVP_PKEY_CTX *pctx = NULL;
+ EVP_PKEY *pkey = NULL;
+ RSA *rsa;
+ X509_REQ *req = NULL;
+ int ret = -1;
+ unsigned int val;
+ X509_NAME *subj = NULL;
+ char name[100];
+ STACK_OF(X509_EXTENSION) *exts = NULL;
+ X509_EXTENSION *ex;
+ BIO *out;
+
+ wpa_printf(MSG_INFO, "Generate RSA private key");
+ write_summary(ctx, "Generate RSA private key");
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+ if (!pctx)
+ return -1;
+
+ if (EVP_PKEY_keygen_init(pctx) <= 0)
+ goto fail;
+
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
+ goto fail;
+
+ if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
+ goto fail;
+ EVP_PKEY_CTX_free(pctx);
+ pctx = NULL;
+
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL)
+ goto fail;
+
+ if (key_pem) {
+ FILE *f = fopen(key_pem, "wb");
+ if (f == NULL)
+ goto fail;
+ if (!PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL,
+ NULL)) {
+ wpa_printf(MSG_INFO, "Could not write private key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ fclose(f);
+ goto fail;
+ }
+ fclose(f);
+ }
+
+ wpa_printf(MSG_INFO, "Generate CSR");
+ write_summary(ctx, "Generate CSR");
+ req = X509_REQ_new();
+ if (req == NULL)
+ goto fail;
+
+ if (old_cert) {
+ FILE *f;
+ X509 *cert;
+ int res;
+
+ f = fopen(old_cert, "r");
+ if (f == NULL)
+ goto fail;
+ cert = PEM_read_X509(f, NULL, NULL, NULL);
+ fclose(f);
+
+ if (cert == NULL)
+ goto fail;
+ res = X509_REQ_set_subject_name(req,
+ X509_get_subject_name(cert));
+ X509_free(cert);
+ if (!res)
+ goto fail;
+ } else {
+ os_get_random((u8 *) &val, sizeof(val));
+ os_snprintf(name, sizeof(name), "cert-user-%u", val);
+ subj = X509_NAME_new();
+ if (subj == NULL ||
+ !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
+ (unsigned char *) name,
+ -1, -1, 0) ||
+ !X509_REQ_set_subject_name(req, subj))
+ goto fail;
+ X509_NAME_free(subj);
+ subj = NULL;
+ }
+
+ if (!X509_REQ_set_pubkey(req, pkey))
+ goto fail;
+
+ exts = sk_X509_EXTENSION_new_null();
+ if (!exts)
+ goto fail;
+
+ ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
+ "CA:FALSE");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+
+ ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
+ "nonRepudiation,digitalSignature,keyEncipherment");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+
+ ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage,
+ "1.3.6.1.4.1.40808.1.1.2");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+
+ add_csrattrs(ctx, csrattrs, exts);
+
+ if (!X509_REQ_add_extensions(req, exts))
+ goto fail;
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ exts = NULL;
+
+ if (!X509_REQ_sign(req, pkey, EVP_sha256()))
+ goto fail;
+
+ out = BIO_new(BIO_s_mem());
+ if (out) {
+ char *txt;
+ size_t rlen;
+
+ X509_REQ_print(out, req);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ int res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
+ txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+ }
+
+ if (csr_pem) {
+ FILE *f = fopen(csr_pem, "w");
+ if (f == NULL)
+ goto fail;
+ X509_REQ_print_fp(f, req);
+ if (!PEM_write_X509_REQ(f, req)) {
+ fclose(f);
+ goto fail;
+ }
+ fclose(f);
+ }
+
+ if (est_req) {
+ BIO *mem = BIO_new(BIO_s_mem());
+ BUF_MEM *ptr;
+ char *pos, *end, *buf_end;
+ FILE *f;
+
+ if (mem == NULL)
+ goto fail;
+ if (!PEM_write_bio_X509_REQ(mem, req)) {
+ BIO_free(mem);
+ goto fail;
+ }
+
+ BIO_get_mem_ptr(mem, &ptr);
+ pos = ptr->data;
+ buf_end = pos + ptr->length;
+
+ /* Remove START/END lines */
+ while (pos < buf_end && *pos != '\n')
+ pos++;
+ if (pos == buf_end) {
+ BIO_free(mem);
+ goto fail;
+ }
+ pos++;
+
+ end = pos;
+ while (end < buf_end && *end != '-')
+ end++;
+
+ f = fopen(est_req, "w");
+ if (f == NULL) {
+ BIO_free(mem);
+ goto fail;
+ }
+ fwrite(pos, end - pos, 1, f);
+ fclose(f);
+
+ BIO_free(mem);
+ }
+
+ ret = 0;
+fail:
+ if (exts)
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ if (subj)
+ X509_NAME_free(subj);
+ if (req)
+ X509_REQ_free(req);
+ if (pkey)
+ EVP_PKEY_free(pkey);
+ if (pctx)
+ EVP_PKEY_CTX_free(pctx);
+ return ret;
+}
+
+
+int est_build_csr(struct hs20_osu_client *ctx, const char *url)
+{
+ char *buf;
+ size_t buflen;
+ int res;
+ char old_cert_buf[200];
+ char *old_cert = NULL;
+ CsrAttrs *csrattrs = NULL;
+
+ buflen = os_strlen(url) + 100;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return -1;
+
+ os_snprintf(buf, buflen, "%s/csrattrs", url);
+ wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
+ write_summary(ctx, "Download EST csrattrs from %s", buf);
+ ctx->no_osu_cert_validation = 1;
+ http_ocsp_set(ctx->http, 1);
+ res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
+ ctx->ca_fname);
+ http_ocsp_set(ctx->http,
+ (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+ ctx->no_osu_cert_validation = 0;
+ os_free(buf);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
+ } else {
+ size_t resp_len;
+ char *resp;
+ unsigned char *attrs;
+ const unsigned char *pos;
+ size_t attrs_len;
+
+ resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Could not read csrattrs");
+ return -1;
+ }
+
+ attrs = base64_decode((unsigned char *) resp, resp_len,
+ &attrs_len);
+ os_free(resp);
+
+ if (attrs == NULL) {
+ wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
+ return -1;
+ }
+ unlink("Cert/est-csrattrs.txt");
+
+ pos = attrs;
+ csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
+ os_free(attrs);
+ if (csrattrs == NULL) {
+ wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
+ /* Continue assuming no additional requirements */
+ }
+ }
+
+ if (ctx->client_cert_present) {
+ os_snprintf(old_cert_buf, sizeof(old_cert_buf),
+ "SP/%s/client-cert.pem", ctx->fqdn);
+ old_cert = old_cert_buf;
+ }
+
+ res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
+ "Cert/est-req.b64", old_cert, csrattrs);
+ if (csrattrs)
+ CsrAttrs_free(csrattrs);
+
+ return res;
+}
+
+
+int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
+ const char *user, const char *pw)
+{
+ char *buf, *resp, *req, *req2;
+ size_t buflen, resp_len, len, pkcs7_len;
+ unsigned char *pkcs7;
+ FILE *f;
+ char client_cert_buf[200];
+ char client_key_buf[200];
+ const char *client_cert = NULL, *client_key = NULL;
+ int res;
+
+ req = os_readfile("Cert/est-req.b64", &len);
+ if (req == NULL) {
+ wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
+ return -1;
+ }
+ req2 = os_realloc(req, len + 1);
+ if (req2 == NULL) {
+ os_free(req);
+ return -1;
+ }
+ req2[len] = '\0';
+ req = req2;
+ wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
+
+ buflen = os_strlen(url) + 100;
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ os_free(req);
+ return -1;
+ }
+
+ if (ctx->client_cert_present) {
+ os_snprintf(buf, buflen, "%s/simplereenroll", url);
+ os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+ "SP/%s/client-cert.pem", ctx->fqdn);
+ client_cert = client_cert_buf;
+ os_snprintf(client_key_buf, sizeof(client_key_buf),
+ "SP/%s/client-key.pem", ctx->fqdn);
+ client_key = client_key_buf;
+ } else
+ os_snprintf(buf, buflen, "%s/simpleenroll", url);
+ wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
+ write_summary(ctx, "EST simpleenroll URL: %s", buf);
+ ctx->no_osu_cert_validation = 1;
+ http_ocsp_set(ctx->http, 1);
+ resp = http_post(ctx->http, buf, req, "application/pkcs10",
+ "Content-Transfer-Encoding: base64",
+ ctx->ca_fname, user, pw, client_cert, client_key,
+ &resp_len);
+ http_ocsp_set(ctx->http,
+ (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+ ctx->no_osu_cert_validation = 0;
+ os_free(buf);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "EST certificate enrollment failed");
+ write_result(ctx, "EST certificate enrollment failed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
+ f = fopen("Cert/est-resp.raw", "w");
+ if (f) {
+ fwrite(resp, resp_len, 1, f);
+ fclose(f);
+ }
+
+ pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+ if (pkcs7 == NULL) {
+ wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
+ pkcs7 = os_malloc(resp_len);
+ if (pkcs7) {
+ os_memcpy(pkcs7, resp, resp_len);
+ pkcs7_len = resp_len;
+ }
+ }
+ os_free(resp);
+
+ if (pkcs7 == NULL) {
+ wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
+ write_result(ctx, "Failed to parse EST simpleenroll base64 response");
+ return -1;
+ }
+
+ res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
+ "Cert/est_cert.der");
+ os_free(pkcs7);
+
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
+ write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
+ ctx->client_cert_present ? "re" : "");
+ write_summary(ctx, "EST simple%senroll completed successfully",
+ ctx->client_cert_present ? "re" : "");
+
+ return 0;
+}
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
new file mode 100644
index 0000000..82e9106
--- /dev/null
+++ b/hs20/client/oma_dm_client.c
@@ -0,0 +1,1370 @@
+/*
+ * Hotspot 2.0 - OMA DM client
+ * Copyright (c) 2013-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 "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/browser.h"
+#include "osu_client.h"
+
+
+#define DM_SERVER_INITIATED_MGMT 1200
+#define DM_CLIENT_INITIATED_MGMT 1201
+#define DM_GENERIC_ALERT 1226
+
+/* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
+#define DM_RESP_OK 200
+#define DM_RESP_AUTH_ACCEPTED 212
+#define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
+#define DM_RESP_NOT_EXECUTED 215
+#define DM_RESP_ATOMIC_ROLL_BACK_OK 216
+#define DM_RESP_NOT_MODIFIED 304
+#define DM_RESP_BAD_REQUEST 400
+#define DM_RESP_UNAUTHORIZED 401
+#define DM_RESP_FORBIDDEN 403
+#define DM_RESP_NOT_FOUND 404
+#define DM_RESP_COMMAND_NOT_ALLOWED 405
+#define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
+#define DM_RESP_MISSING_CREDENTIALS 407
+#define DM_RESP_CONFLICT 409
+#define DM_RESP_GONE 410
+#define DM_RESP_INCOMPLETE_COMMAND 412
+#define DM_RESP_REQ_ENTITY_TOO_LARGE 413
+#define DM_RESP_URI_TOO_LONG 414
+#define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
+#define DM_RESP_REQ_TOO_BIG 416
+#define DM_RESP_ALREADY_EXISTS 418
+#define DM_RESP_DEVICE_FULL 420
+#define DM_RESP_SIZE_MISMATCH 424
+#define DM_RESP_PERMISSION_DENIED 425
+#define DM_RESP_COMMAND_FAILED 500
+#define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
+#define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
+
+#define DM_HS20_SUBSCRIPTION_CREATION \
+ "org.wi-fi.hotspot2dot0.SubscriptionCreation"
+#define DM_HS20_SUBSCRIPTION_PROVISIONING \
+ "org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
+#define DM_HS20_SUBSCRIPTION_REMEDIATION \
+ "org.wi-fi.hotspot2dot0.SubscriptionRemediation"
+#define DM_HS20_POLICY_UPDATE \
+ "org.wi-fi.hotspot2dot0.PolicyUpdate"
+
+#define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
+#define DM_URI_LAUNCH_BROWSER \
+ "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
+
+
+static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
+ const char *locuri, const char *data);
+
+
+static const char * int2str(int val)
+{
+ static char buf[20];
+ snprintf(buf, sizeof(buf), "%d", val);
+ return buf;
+}
+
+
+static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
+ xml_node_t *node)
+{
+ xml_node_t *locuri;
+ char *uri, *ret = NULL;
+
+ locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
+ if (locuri == NULL)
+ return NULL;
+
+ uri = xml_node_get_text(ctx->xml, locuri);
+ if (uri)
+ ret = os_strdup(uri);
+ xml_node_get_text_free(ctx->xml, uri);
+ return ret;
+}
+
+
+static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
+ const char *element, const char *uri)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, element);
+ if (node == NULL)
+ return;
+ xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
+}
+
+
+static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml, *synchdr;
+ xml_namespace_t *ns;
+
+ syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
+ "SyncML");
+
+ synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
+ xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
+ xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
+ xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
+ xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
+
+ oma_dm_add_locuri(ctx, synchdr, "Target", url);
+ oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
+
+ return syncml;
+}
+
+
+static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int cmdid)
+{
+ xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
+}
+
+
+static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int cmdid, int data)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "Alert");
+ if (node == NULL)
+ return NULL;
+ oma_dm_add_cmdid(ctx, node, cmdid);
+ xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
+
+ return node;
+}
+
+
+static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int msgref, int cmdref, int cmdid,
+ const char *cmd, int data, const char *targetref)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "Status");
+ if (node == NULL)
+ return NULL;
+ oma_dm_add_cmdid(ctx, node, cmdid);
+ xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
+ if (cmdref)
+ xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
+ int2str(cmdref));
+ xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
+ xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
+ if (targetref) {
+ xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
+ targetref);
+ }
+
+ return node;
+}
+
+
+static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int msgref, int cmdref, int cmdid,
+ const char *locuri, const char *data)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "Results");
+ if (node == NULL)
+ return NULL;
+
+ oma_dm_add_cmdid(ctx, node, cmdid);
+ xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
+ xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
+ add_item(ctx, node, locuri, data);
+
+ return node;
+}
+
+
+static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
+ const char *fname)
+{
+ xml_node_t *fnode, *tnds;
+ char *str;
+
+ fnode = node_from_file(ctx->xml, fname);
+ if (!fnode)
+ return NULL;
+ tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
+ xml_node_free(ctx->xml, fnode);
+ if (!tnds)
+ return NULL;
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL)
+ return NULL;
+ wpa_printf(MSG_INFO, "MgmtTree: %s", str);
+
+ return str;
+}
+
+
+static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
+ const char *locuri, const char *data)
+{
+ xml_node_t *item, *node;
+
+ item = xml_node_create(ctx->xml, parent, NULL, "Item");
+ oma_dm_add_locuri(ctx, item, "Source", locuri);
+ node = xml_node_create(ctx->xml, item, NULL, "Meta");
+ xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
+ "Chr");
+ xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
+ "text/plain");
+ xml_node_create_text(ctx->xml, item, NULL, "Data", data);
+}
+
+
+static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
+ int cmdid)
+{
+ xml_node_t *info, *child, *replace;
+ const char *name;
+ char locuri[200], *txt;
+
+ info = node_from_file(ctx->xml, "devinfo.xml");
+ if (info == NULL) {
+ wpa_printf(MSG_INFO, "Could not read devinfo.xml");
+ return;
+ }
+
+ replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
+ if (replace == NULL) {
+ xml_node_free(ctx->xml, info);
+ return;
+ }
+ oma_dm_add_cmdid(ctx, replace, cmdid);
+
+ xml_node_for_each_child(ctx->xml, child, info) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
+ txt = xml_node_get_text(ctx->xml, child);
+ if (txt) {
+ add_item(ctx, replace, locuri, txt);
+ xml_node_get_text_free(ctx->xml, txt);
+ }
+ }
+
+ xml_node_free(ctx->xml, info);
+}
+
+
+static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
+ xml_node_t *syncbody,
+ int cmdid, const char *oper,
+ const char *data)
+{
+ xml_node_t *node, *item;
+ char buf[200];
+
+ node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
+
+ item = xml_node_create(ctx->xml, node, NULL, "Item");
+ oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
+ node = xml_node_create(ctx->xml, item, NULL, "Meta");
+ snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
+ xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
+ xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
+ "xml");
+ xml_node_create_text(ctx->xml, item, NULL, "Data", data);
+}
+
+
+static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
+ const char *url, int msgid, const char *oper)
+{
+ xml_node_t *syncml, *syncbody;
+ char *str;
+ int cmdid = 0;
+
+ syncml = oma_dm_build_hdr(ctx, url, msgid);
+ if (syncml == NULL)
+ return NULL;
+
+ syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+ if (syncbody == NULL) {
+ xml_node_free(ctx->xml, syncml);
+ return NULL;
+ }
+
+ cmdid++;
+ add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
+
+ str = mo_str(ctx, NULL, "devdetail.xml");
+ if (str == NULL) {
+ xml_node_free(ctx->xml, syncml);
+ return NULL;
+ }
+ cmdid++;
+ oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
+ os_free(str);
+
+ cmdid++;
+ add_replace_devinfo(ctx, syncbody, cmdid);
+
+ xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+ return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml;
+
+ syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
+ if (syncml)
+ debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
+
+ return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml;
+
+ syncml = build_oma_dm_1(ctx, url, msgid,
+ DM_HS20_SUBSCRIPTION_PROVISIONING);
+ if (syncml)
+ debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
+
+ return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml;
+
+ syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
+ if (syncml)
+ debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
+
+ return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
+ const char *url, int msgid)
+{
+ xml_node_t *syncml;
+
+ syncml = build_oma_dm_1(ctx, url, msgid,
+ DM_HS20_SUBSCRIPTION_REMEDIATION);
+ if (syncml)
+ debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
+
+ return syncml;
+}
+
+
+static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+ xml_node_t *node;
+ char *data;
+ int res;
+
+ node = get_node(ctx->xml, exec, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Data node found");
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ data = xml_node_get_text(ctx->xml, node);
+ wpa_printf(MSG_INFO, "Data: %s", data);
+ wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
+ write_summary(ctx, "Launch browser to URI '%s'", data);
+ res = hs20_web_browser(data);
+ xml_node_get_text_free(ctx->xml, data);
+ if (res > 0) {
+ wpa_printf(MSG_INFO, "User response in browser completed successfully");
+ write_summary(ctx, "User response in browser completed successfully");
+ return DM_RESP_OK;
+ } else {
+ wpa_printf(MSG_INFO, "Failed to receive user response");
+ write_summary(ctx, "Failed to receive user response");
+ return DM_RESP_COMMAND_FAILED;
+ }
+}
+
+
+static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+ xml_node_t *node, *getcert;
+ char *data;
+ const char *name;
+ int res;
+
+ wpa_printf(MSG_INFO, "Client certificate enrollment");
+ write_summary(ctx, "Client certificate enrollment");
+
+ node = get_node(ctx->xml, exec, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Data node found");
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ data = xml_node_get_text(ctx->xml, node);
+ wpa_printf(MSG_INFO, "Data: %s", data);
+ getcert = xml_node_from_buf(ctx->xml, data);
+ xml_node_get_text_free(ctx->xml, data);
+
+ if (getcert == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
+
+ name = xml_node_get_localname(ctx->xml, getcert);
+ if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
+ wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
+ name);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ res = osu_get_certificate(ctx, getcert);
+
+ xml_node_free(ctx->xml, getcert);
+
+ return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
+}
+
+
+static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+ char *locuri;
+ int ret;
+
+ locuri = oma_dm_get_target_locuri(ctx, exec);
+ if (locuri == NULL) {
+ wpa_printf(MSG_INFO, "No Target LocURI node found");
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+
+ if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+ "launchBrowserToURI") == 0) {
+ ret = oma_dm_exec_browser(ctx, exec);
+ } else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+ "getCertificate") == 0) {
+ ret = oma_dm_exec_get_cert(ctx, exec);
+ } else {
+ wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
+ ret = DM_RESP_NOT_FOUND;
+ }
+ os_free(locuri);
+
+ return ret;
+}
+
+
+static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
+ xml_node_t *add, xml_node_t *pps,
+ const char *pps_fname)
+{
+ const char *pos;
+ size_t fqdn_len;
+ xml_node_t *node, *tnds, *unode, *pps_node;
+ char *data, *uri, *upos, *end;
+ int use_tnds = 0;
+ size_t uri_len;
+
+ wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
+
+ if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos = locuri + 8;
+
+ if (ctx->fqdn == NULL)
+ return DM_RESP_COMMAND_FAILED;
+ fqdn_len = os_strlen(ctx->fqdn);
+ if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+ pos[fqdn_len] != '/') {
+ wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
+ ctx->fqdn);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += fqdn_len + 1;
+
+ if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+ wpa_printf(MSG_INFO,
+ "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
+ ctx->fqdn);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += 24;
+
+ wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
+
+ pps_node = get_node(ctx->xml, pps, pos);
+ if (pps_node) {
+ wpa_printf(MSG_INFO, "Specified PPS node exists already");
+ return DM_RESP_ALREADY_EXISTS;
+ }
+
+ uri = os_strdup(pos);
+ if (uri == NULL)
+ return DM_RESP_COMMAND_FAILED;
+ while (!pps_node) {
+ upos = os_strrchr(uri, '/');
+ if (!upos)
+ break;
+ upos[0] = '\0';
+ pps_node = get_node(ctx->xml, pps, uri);
+ wpa_printf(MSG_INFO, "Node %s %s", uri,
+ pps_node ? "exists" : "does not exist");
+ }
+
+ wpa_printf(MSG_INFO, "Parent URI: %s", uri);
+
+ if (!pps_node) {
+ /* Add at root of PPS MO */
+ pps_node = pps;
+ }
+
+ uri_len = os_strlen(uri);
+ os_strlcpy(uri, pos + uri_len, os_strlen(pos));
+ upos = uri;
+ while (*upos == '/')
+ upos++;
+ wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
+
+ for (;;) {
+ end = os_strchr(upos, '/');
+ if (!end)
+ break;
+ *end = '\0';
+ wpa_printf(MSG_INFO, "Adding interim node %s", upos);
+ pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
+ if (pps_node == NULL) {
+ os_free(uri);
+ return DM_RESP_COMMAND_FAILED;
+ }
+ upos = end + 1;
+ }
+
+ wpa_printf(MSG_INFO, "Adding node %s", upos);
+
+ node = get_node(ctx->xml, add, "Item/Meta/Type");
+ if (node) {
+ char *type;
+ type = xml_node_get_text(ctx->xml, node);
+ use_tnds = node &&
+ os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+ }
+
+ node = get_node(ctx->xml, add, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Add/Item/Data found");
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ data = xml_node_get_text(ctx->xml, node);
+ if (data == NULL) {
+ wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
+
+ if (use_tnds) {
+ tnds = xml_node_from_buf(ctx->xml, data);
+ xml_node_get_text_free(ctx->xml, data);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO,
+ "Could not parse Add/Item/Data text");
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ unode = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (unode == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse TNDS text");
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ debug_dump_node(ctx, "Parsed TNDS", unode);
+
+ xml_node_add_child(ctx->xml, pps_node, unode);
+ } else {
+ /* TODO: What to do here? */
+ os_free(uri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ os_free(uri);
+
+ if (update_pps_file(ctx, pps_fname, pps) < 0)
+ return DM_RESP_COMMAND_FAILED;
+
+ ctx->pps_updated = 1;
+
+ return DM_RESP_OK;
+}
+
+
+static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
+ xml_node_t *pps, const char *pps_fname)
+{
+ xml_node_t *node;
+ char *locuri;
+ char fname[300];
+ int ret;
+
+ node = get_node(ctx->xml, add, "Item/Target/LocURI");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Target LocURI node found");
+ return DM_RESP_BAD_REQUEST;
+ }
+ locuri = xml_node_get_text(ctx->xml, node);
+ wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+ if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
+ xml_node_get_text_free(ctx->xml, locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+
+ node = get_node(ctx->xml, add, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Data node found");
+ xml_node_get_text_free(ctx->xml, locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ if (pps_fname && os_file_exists(pps_fname)) {
+ ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
+ if (ret != DM_RESP_OK) {
+ xml_node_get_text_free(ctx->xml, locuri);
+ return ret;
+ }
+ ret = 0;
+ os_strlcpy(fname, pps_fname, sizeof(fname));
+ } else
+ ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
+ xml_node_get_text_free(ctx->xml, locuri);
+ if (ret < 0)
+ return ret == -2 ? DM_RESP_ALREADY_EXISTS :
+ DM_RESP_COMMAND_FAILED;
+
+ if (ctx->no_reconnect == 2) {
+ os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
+ fname);
+ ctx->pps_cred_set = 1;
+ return DM_RESP_OK;
+ }
+
+ wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+ cmd_set_pps(ctx, fname);
+
+ if (ctx->no_reconnect)
+ return DM_RESP_OK;
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+ wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+
+ return DM_RESP_OK;
+}
+
+
+static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
+ xml_node_t *pps, const char *pps_fname)
+{
+ char *locuri, *pos;
+ size_t fqdn_len;
+ xml_node_t *node, *tnds, *unode, *pps_node, *parent;
+ char *data;
+ int use_tnds = 0;
+
+ locuri = oma_dm_get_target_locuri(ctx, replace);
+ if (locuri == NULL)
+ return DM_RESP_BAD_REQUEST;
+
+ wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
+ if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos = locuri + 8;
+
+ if (ctx->fqdn == NULL) {
+ os_free(locuri);
+ return DM_RESP_COMMAND_FAILED;
+ }
+ fqdn_len = os_strlen(ctx->fqdn);
+ if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+ pos[fqdn_len] != '/') {
+ wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
+ ctx->fqdn);
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += fqdn_len + 1;
+
+ if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+ wpa_printf(MSG_INFO,
+ "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
+ ctx->fqdn);
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += 24;
+
+ wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
+
+ pps_node = get_node(ctx->xml, pps, pos);
+ if (pps_node == NULL) {
+ wpa_printf(MSG_INFO, "Specified PPS node not found");
+ os_free(locuri);
+ return DM_RESP_NOT_FOUND;
+ }
+
+ node = get_node(ctx->xml, replace, "Item/Meta/Type");
+ if (node) {
+ char *type;
+ type = xml_node_get_text(ctx->xml, node);
+ use_tnds = node &&
+ os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+ }
+
+ node = get_node(ctx->xml, replace, "Item/Data");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Replace/Item/Data found");
+ os_free(locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ data = xml_node_get_text(ctx->xml, node);
+ if (data == NULL) {
+ wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
+ os_free(locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
+
+ if (use_tnds) {
+ tnds = xml_node_from_buf(ctx->xml, data);
+ xml_node_get_text_free(ctx->xml, data);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO,
+ "Could not parse Replace/Item/Data text");
+ os_free(locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ unode = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (unode == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse TNDS text");
+ os_free(locuri);
+ return DM_RESP_BAD_REQUEST;
+ }
+
+ debug_dump_node(ctx, "Parsed TNDS", unode);
+
+ parent = xml_node_get_parent(ctx->xml, pps_node);
+ xml_node_detach(ctx->xml, pps_node);
+ xml_node_add_child(ctx->xml, parent, unode);
+ } else {
+ xml_node_set_text(ctx->xml, pps_node, data);
+ xml_node_get_text_free(ctx->xml, data);
+ }
+
+ os_free(locuri);
+
+ if (update_pps_file(ctx, pps_fname, pps) < 0)
+ return DM_RESP_COMMAND_FAILED;
+
+ ctx->pps_updated = 1;
+
+ return DM_RESP_OK;
+}
+
+
+static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
+ xml_node_t *pps, const char *pps_fname, char **value)
+{
+ char *locuri, *pos;
+ size_t fqdn_len;
+ xml_node_t *pps_node;
+ const char *name;
+
+ *value = NULL;
+
+ locuri = oma_dm_get_target_locuri(ctx, get);
+ if (locuri == NULL)
+ return DM_RESP_BAD_REQUEST;
+
+ wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
+ if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos = locuri + 8;
+
+ if (ctx->fqdn == NULL)
+ return DM_RESP_COMMAND_FAILED;
+ fqdn_len = os_strlen(ctx->fqdn);
+ if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+ pos[fqdn_len] != '/') {
+ wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
+ ctx->fqdn);
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += fqdn_len + 1;
+
+ if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+ wpa_printf(MSG_INFO,
+ "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
+ ctx->fqdn);
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+ pos += 24;
+
+ wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
+
+ pps_node = get_node(ctx->xml, pps, pos);
+ if (pps_node == NULL) {
+ wpa_printf(MSG_INFO, "Specified PPS node not found");
+ os_free(locuri);
+ return DM_RESP_NOT_FOUND;
+ }
+
+ name = xml_node_get_localname(ctx->xml, pps_node);
+ wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
+ if (os_strcasecmp(name, "Password") == 0) {
+ wpa_printf(MSG_INFO, "Do not allow Get for Password node");
+ os_free(locuri);
+ return DM_RESP_PERMISSION_DENIED;
+ }
+
+ /*
+ * TODO: No support for DMTNDS, so if interior node, reply with a
+ * list of children node names in Results element. The child list type is
+ * defined in [DMTND].
+ */
+
+ *value = xml_node_get_text(ctx->xml, pps_node);
+ if (*value == NULL)
+ return DM_RESP_COMMAND_FAILED;
+
+ return DM_RESP_OK;
+}
+
+
+static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
+{
+ xml_node_t *cnode;
+ char *str;
+ int ret;
+
+ cnode = get_node(ctx->xml, node, "CmdID");
+ if (cnode == NULL)
+ return 0;
+
+ str = xml_node_get_text(ctx->xml, cnode);
+ if (str == NULL)
+ return 0;
+ ret = atoi(str);
+ xml_node_get_text_free(ctx->xml, str);
+ return ret;
+}
+
+
+static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
+ const char *url, xml_node_t *syncml,
+ const char *ext_hdr,
+ const char *username, const char *password,
+ const char *client_cert,
+ const char *client_key)
+{
+ xml_node_t *resp;
+ char *str, *res;
+ char *resp_uri = NULL;
+
+ str = xml_node_to_str(ctx->xml, syncml);
+ xml_node_free(ctx->xml, syncml);
+ if (str == NULL)
+ return NULL;
+
+ wpa_printf(MSG_INFO, "Send OMA DM Package");
+ write_summary(ctx, "Send OMA DM Package");
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(url);
+ res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
+ ext_hdr, ctx->ca_fname, username, password,
+ client_cert, client_key, NULL);
+ os_free(str);
+ os_free(resp_uri);
+ resp_uri = NULL;
+
+ if (res == NULL) {
+ const char *err = http_get_err(ctx->http);
+ if (err) {
+ wpa_printf(MSG_INFO, "HTTP error: %s", err);
+ write_result(ctx, "HTTP error: %s", err);
+ } else {
+ write_summary(ctx, "Failed to send OMA DM Package");
+ }
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "Server response: %s", res);
+
+ wpa_printf(MSG_INFO, "Process OMA DM Package");
+ write_summary(ctx, "Process received OMA DM Package");
+ resp = xml_node_from_buf(ctx->xml, res);
+ os_free(res);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
+ return NULL;
+ }
+
+ debug_dump_node(ctx, "OMA DM Package", resp);
+
+ return resp;
+}
+
+
+static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
+ xml_node_t *resp, int msgid,
+ char **ret_resp_uri,
+ xml_node_t *pps, const char *pps_fname)
+{
+ xml_node_t *syncml, *syncbody, *hdr, *body, *child;
+ const char *name;
+ char *resp_uri = NULL;
+ int server_msgid = 0;
+ int cmdid = 0;
+ int server_cmdid;
+ int resp_needed = 0;
+ char *tmp;
+ int final = 0;
+ char *locuri;
+
+ *ret_resp_uri = NULL;
+
+ name = xml_node_get_localname(ctx->xml, resp);
+ if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
+ wpa_printf(MSG_INFO, "SyncML node not found");
+ return NULL;
+ }
+
+ hdr = get_node(ctx->xml, resp, "SyncHdr");
+ body = get_node(ctx->xml, resp, "SyncBody");
+ if (hdr == NULL || body == NULL) {
+ wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
+ return NULL;
+ }
+
+ xml_node_for_each_child(ctx->xml, child, hdr) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ wpa_printf(MSG_INFO, "SyncHdr %s", name);
+ if (os_strcasecmp(name, "RespURI") == 0) {
+ tmp = xml_node_get_text(ctx->xml, child);
+ if (tmp)
+ resp_uri = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ } else if (os_strcasecmp(name, "MsgID") == 0) {
+ tmp = xml_node_get_text(ctx->xml, child);
+ if (tmp)
+ server_msgid = atoi(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ }
+ }
+
+ wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
+ if (resp_uri)
+ wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
+
+ syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
+ if (syncml == NULL) {
+ os_free(resp_uri);
+ return NULL;
+ }
+
+ syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+ cmdid++;
+ add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
+ DM_RESP_AUTH_ACCEPTED, NULL);
+
+ xml_node_for_each_child(ctx->xml, child, body) {
+ xml_node_for_each_check(ctx->xml, child);
+ server_cmdid = oma_dm_get_cmdid(ctx, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
+ server_cmdid, name);
+ if (os_strcasecmp(name, "Exec") == 0) {
+ int res = oma_dm_exec(ctx, child);
+ cmdid++;
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ if (locuri == NULL)
+ res = DM_RESP_BAD_REQUEST;
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, res, locuri);
+ os_free(locuri);
+ resp_needed = 1;
+ } else if (os_strcasecmp(name, "Add") == 0) {
+ int res = oma_dm_add(ctx, child, pps, pps_fname);
+ cmdid++;
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ if (locuri == NULL)
+ res = DM_RESP_BAD_REQUEST;
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, res, locuri);
+ os_free(locuri);
+ resp_needed = 1;
+ } else if (os_strcasecmp(name, "Replace") == 0) {
+ int res;
+ res = oma_dm_replace(ctx, child, pps, pps_fname);
+ cmdid++;
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ if (locuri == NULL)
+ res = DM_RESP_BAD_REQUEST;
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, res, locuri);
+ os_free(locuri);
+ resp_needed = 1;
+ } else if (os_strcasecmp(name, "Status") == 0) {
+ /* TODO: Verify success */
+ } else if (os_strcasecmp(name, "Get") == 0) {
+ int res;
+ char *value;
+ res = oma_dm_get(ctx, child, pps, pps_fname, &value);
+ cmdid++;
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ if (locuri == NULL)
+ res = DM_RESP_BAD_REQUEST;
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, res, locuri);
+ if (res == DM_RESP_OK && value) {
+ cmdid++;
+ add_results(ctx, syncbody, server_msgid,
+ server_cmdid, cmdid, locuri, value);
+ }
+ os_free(locuri);
+ xml_node_get_text_free(ctx->xml, value);
+ resp_needed = 1;
+#if 0 /* TODO: MUST support */
+ } else if (os_strcasecmp(name, "Delete") == 0) {
+#endif
+#if 0 /* TODO: MUST support */
+ } else if (os_strcasecmp(name, "Sequence") == 0) {
+#endif
+ } else if (os_strcasecmp(name, "Final") == 0) {
+ final = 1;
+ break;
+ } else {
+ locuri = oma_dm_get_target_locuri(ctx, child);
+ add_status(ctx, syncbody, server_msgid, server_cmdid,
+ cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
+ locuri);
+ os_free(locuri);
+ resp_needed = 1;
+ }
+ }
+
+ if (!final) {
+ wpa_printf(MSG_INFO, "Final node not found");
+ xml_node_free(ctx->xml, syncml);
+ os_free(resp_uri);
+ return NULL;
+ }
+
+ if (!resp_needed) {
+ wpa_printf(MSG_INFO, "Exchange completed - no response needed");
+ xml_node_free(ctx->xml, syncml);
+ os_free(resp_uri);
+ return NULL;
+ }
+
+ xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+ debug_dump_node(ctx, "OMA-DM Package 3", syncml);
+
+ *ret_resp_uri = resp_uri;
+ return syncml;
+}
+
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
+{
+ xml_node_t *syncml, *resp;
+ char *resp_uri = NULL;
+ int msgid = 0;
+
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
+ write_summary(ctx, "OMA-DM credential provisioning");
+
+ msgid++;
+ syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
+ if (syncml == NULL)
+ return -1;
+
+ while (syncml) {
+ resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+ syncml, NULL, NULL, NULL, NULL, NULL);
+ if (resp == NULL)
+ return -1;
+
+ msgid++;
+ syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+ NULL, NULL);
+ xml_node_free(ctx->xml, resp);
+ }
+
+ os_free(resp_uri);
+
+ return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
+{
+ xml_node_t *syncml, *resp;
+ char *resp_uri = NULL;
+ int msgid = 0;
+
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
+ ctx->no_reconnect = 2;
+
+ wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+ write_summary(ctx, "Wait for IP address before starting SIM provisioning");
+
+ if (wait_ip_addr(ctx->ifname, 15) < 0) {
+ wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ }
+ write_summary(ctx, "OMA-DM SIM provisioning");
+
+ msgid++;
+ syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
+ if (syncml == NULL)
+ return -1;
+
+ while (syncml) {
+ resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+ syncml, NULL, NULL, NULL, NULL, NULL);
+ if (resp == NULL)
+ return -1;
+
+ msgid++;
+ syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+ NULL, NULL);
+ xml_node_free(ctx->xml, resp);
+ }
+
+ os_free(resp_uri);
+
+ if (ctx->pps_cred_set) {
+ wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+ cmd_set_pps(ctx, ctx->pps_fname);
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ write_summary(ctx, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+ wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+ write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+ return -1;
+ }
+ }
+
+ return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps)
+{
+ xml_node_t *syncml, *resp;
+ char *resp_uri = NULL;
+ int msgid = 0;
+
+ wpa_printf(MSG_INFO, "OMA-DM policy update");
+ write_summary(ctx, "OMA-DM policy update");
+
+ msgid++;
+ syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
+ if (syncml == NULL)
+ return;
+
+ while (syncml) {
+ resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+ syncml, NULL, cred_username,
+ cred_password, client_cert, client_key);
+ if (resp == NULL)
+ return;
+
+ msgid++;
+ syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+ pps, pps_fname);
+ xml_node_free(ctx->xml, resp);
+ }
+
+ os_free(resp_uri);
+
+ if (ctx->pps_updated) {
+ wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
+ write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
+ cmd_set_pps(ctx, pps_fname);
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to request wpa_supplicant to reconnect");
+ write_summary(ctx,
+ "Failed to request wpa_supplicant to reconnect");
+ }
+ }
+}
+
+
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps)
+{
+ xml_node_t *syncml, *resp;
+ char *resp_uri = NULL;
+ int msgid = 0;
+
+ wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
+ write_summary(ctx, "OMA-DM subscription remediation");
+
+ msgid++;
+ syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
+ if (syncml == NULL)
+ return;
+
+ while (syncml) {
+ resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+ syncml, NULL, cred_username,
+ cred_password, client_cert, client_key);
+ if (resp == NULL)
+ return;
+
+ msgid++;
+ syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+ pps, pps_fname);
+ xml_node_free(ctx->xml, resp);
+ }
+
+ os_free(resp_uri);
+
+ wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+ write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+ cmd_set_pps(ctx, pps_fname);
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+ wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+ write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+ }
+}
+
+
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *add_fname)
+{
+ xml_node_t *pps, *add;
+ int res;
+
+ ctx->fqdn = os_strdup("wi-fi.org");
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+ pps_fname);
+ return;
+ }
+
+ add = node_from_file(ctx->xml, add_fname);
+ if (add == NULL) {
+ wpa_printf(MSG_INFO, "Add file %s could not be parsed",
+ add_fname);
+ xml_node_free(ctx->xml, pps);
+ return;
+ }
+
+ res = oma_dm_add(ctx, add, pps, pps_fname);
+ wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
+
+ xml_node_free(ctx->xml, pps);
+ xml_node_free(ctx->xml, add);
+}
+
+
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *replace_fname)
+{
+ xml_node_t *pps, *replace;
+ int res;
+
+ ctx->fqdn = os_strdup("wi-fi.org");
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+ pps_fname);
+ return;
+ }
+
+ replace = node_from_file(ctx->xml, replace_fname);
+ if (replace == NULL) {
+ wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
+ replace_fname);
+ xml_node_free(ctx->xml, pps);
+ return;
+ }
+
+ res = oma_dm_replace(ctx, replace, pps, pps_fname);
+ wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
+
+ xml_node_free(ctx->xml, pps);
+ xml_node_free(ctx->xml, replace);
+}
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
new file mode 100644
index 0000000..a439bde
--- /dev/null
+++ b/hs20/client/osu_client.c
@@ -0,0 +1,3203 @@
+/*
+ * Hotspot 2.0 OSU client
+ * Copyright (c) 2012-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 <time.h>
+#include <sys/stat.h>
+
+#include "common.h"
+#include "utils/browser.h"
+#include "utils/base64.h"
+#include "utils/xml-utils.h"
+#include "utils/http-utils.h"
+#include "common/wpa_ctrl.h"
+#include "common/wpa_helpers.h"
+#include "eap_common/eap_defs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ FILE *f;
+ char buf[500];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ write_summary(ctx, "%s", buf);
+
+ if (!ctx->result_file)
+ return;
+
+ f = fopen(ctx->result_file, "w");
+ if (f == NULL)
+ return;
+
+ va_start(ap, fmt);
+ vfprintf(f, fmt, ap);
+ va_end(ap);
+ fprintf(f, "\n");
+ fclose(f);
+}
+
+
+void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ FILE *f;
+
+ if (!ctx->summary_file)
+ return;
+
+ f = fopen(ctx->summary_file, "a");
+ if (f == NULL)
+ return;
+
+ va_start(ap, fmt);
+ vfprintf(f, fmt, ap);
+ va_end(ap);
+ fprintf(f, "\n");
+ fclose(f);
+}
+
+
+void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
+ xml_node_t *node)
+{
+ char *str = xml_node_to_str(ctx->xml, node);
+ wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str);
+ free(str);
+}
+
+
+static int valid_fqdn(const char *fqdn)
+{
+ const char *pos;
+
+ /* TODO: could make this more complete.. */
+ if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255)
+ return 0;
+ for (pos = fqdn; *pos; pos++) {
+ if (*pos >= 'a' && *pos <= 'z')
+ continue;
+ if (*pos >= 'A' && *pos <= 'Z')
+ continue;
+ if (*pos >= '0' && *pos <= '9')
+ continue;
+ if (*pos == '-' || *pos == '.' || *pos == '_')
+ continue;
+ return 0;
+ }
+ return 1;
+}
+
+
+int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
+{
+ xml_node_t *node;
+ char *url, *user = NULL, *pw = NULL;
+ char *proto;
+ int ret = -1;
+
+ proto = xml_node_get_attr_value(ctx->xml, getcert,
+ "enrollmentProtocol");
+ if (!proto)
+ return -1;
+ wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto);
+ write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto);
+ if (os_strcasecmp(proto, "EST") != 0) {
+ wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol");
+ xml_node_get_attr_value_free(ctx->xml, proto);
+ return -1;
+ }
+ xml_node_get_attr_value_free(ctx->xml, proto);
+
+ node = get_node(ctx->xml, getcert, "enrollmentServerURI");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node");
+ xml_node_get_attr_value_free(ctx->xml, proto);
+ return -1;
+ }
+ url = xml_node_get_text(ctx->xml, node);
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Could not get URL text");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url);
+ write_summary(ctx, "enrollmentServerURI: %s", url);
+
+ node = get_node(ctx->xml, getcert, "estUserID");
+ if (node == NULL && !ctx->client_cert_present) {
+ wpa_printf(MSG_INFO, "Could not find estUserID node");
+ goto fail;
+ }
+ if (node) {
+ user = xml_node_get_text(ctx->xml, node);
+ if (user == NULL) {
+ wpa_printf(MSG_INFO, "Could not get estUserID text");
+ goto fail;
+ }
+ wpa_printf(MSG_INFO, "estUserID: %s", user);
+ write_summary(ctx, "estUserID: %s", user);
+ }
+
+ node = get_node(ctx->xml, getcert, "estPassword");
+ if (node == NULL && !ctx->client_cert_present) {
+ wpa_printf(MSG_INFO, "Could not find estPassword node");
+ goto fail;
+ }
+ if (node) {
+ pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+ if (pw == NULL) {
+ wpa_printf(MSG_INFO, "Could not get estPassword text");
+ goto fail;
+ }
+ wpa_printf(MSG_INFO, "estPassword: %s", pw);
+ }
+
+ mkdir("Cert", S_IRWXU);
+ if (est_load_cacerts(ctx, url) < 0 ||
+ est_build_csr(ctx, url) < 0 ||
+ est_simple_enroll(ctx, url, user, pw) < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ xml_node_get_text_free(ctx->xml, url);
+ xml_node_get_text_free(ctx->xml, user);
+ xml_node_get_text_free(ctx->xml, pw);
+
+ return ret;
+}
+
+
+static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
+ const char *fqdn)
+{
+ u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
+ char *der, *pem;
+ size_t der_len, pem_len;
+ char *fingerprint;
+ char buf[200];
+
+ wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn);
+
+ fingerprint = xml_node_get_text(ctx->xml, cert);
+ if (fingerprint == NULL)
+ return -1;
+ if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) {
+ wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
+ write_result(ctx, "Invalid client certificate SHA256 hash value in PPS");
+ xml_node_get_text_free(ctx->xml, fingerprint);
+ return -1;
+ }
+ xml_node_get_text_free(ctx->xml, fingerprint);
+
+ der = os_readfile("Cert/est_cert.der", &der_len);
+ if (der == NULL) {
+ wpa_printf(MSG_INFO, "Could not find client certificate from EST");
+ write_result(ctx, "Could not find client certificate from EST");
+ return -1;
+ }
+
+ if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) {
+ os_free(der);
+ return -1;
+ }
+ os_free(der);
+
+ if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
+ wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO");
+ write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO");
+ unlink("Cert/est_cert.der");
+
+ os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn);
+ if (rename("Cert/est-cacerts.pem", buf) < 0) {
+ wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s",
+ strerror(errno));
+ return -1;
+ }
+ pem = os_readfile(buf, &pem_len);
+
+ os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn);
+ if (rename("Cert/est_cert.pem", buf) < 0) {
+ wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s",
+ strerror(errno));
+ os_free(pem);
+ return -1;
+ }
+
+ if (pem) {
+ FILE *f = fopen(buf, "a");
+ if (f) {
+ fwrite(pem, pem_len, 1, f);
+ fclose(f);
+ }
+ os_free(pem);
+ }
+
+ os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn);
+ if (rename("Cert/privkey-plain.pem", buf) < 0) {
+ wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ unlink("Cert/est-req.b64");
+ unlink("Cert/est-req.pem");
+ unlink("Cert/est-resp.raw");
+ rmdir("Cert");
+
+ return 0;
+}
+
+
+#define TMP_CERT_DL_FILE "tmp-cert-download"
+
+static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
+ const char *fname)
+{
+ xml_node_t *url_node, *hash_node;
+ char *url, *hash;
+ char *cert;
+ size_t len;
+ u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
+ int res;
+ unsigned char *b64;
+ FILE *f;
+
+ url_node = get_node(ctx->xml, params, "CertURL");
+ hash_node = get_node(ctx->xml, params, "CertSHA256Fingerprint");
+ if (url_node == NULL || hash_node == NULL)
+ return -1;
+ url = xml_node_get_text(ctx->xml, url_node);
+ hash = xml_node_get_text(ctx->xml, hash_node);
+ if (url == NULL || hash == NULL) {
+ xml_node_get_text_free(ctx->xml, url);
+ xml_node_get_text_free(ctx->xml, hash);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "CertURL: %s", url);
+ wpa_printf(MSG_INFO, "SHA256 hash: %s", hash);
+
+ if (hexstr2bin(hash, digest1, SHA256_MAC_LEN) < 0) {
+ wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
+ write_result(ctx, "Invalid SHA256 hash value for downloaded certificate");
+ xml_node_get_text_free(ctx->xml, hash);
+ return -1;
+ }
+ xml_node_get_text_free(ctx->xml, hash);
+
+ write_summary(ctx, "Download certificate from %s", url);
+ ctx->no_osu_cert_validation = 1;
+ http_ocsp_set(ctx->http, 1);
+ res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
+ http_ocsp_set(ctx->http,
+ (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+ ctx->no_osu_cert_validation = 0;
+ xml_node_get_text_free(ctx->xml, url);
+ if (res < 0)
+ return -1;
+
+ cert = os_readfile(TMP_CERT_DL_FILE, &len);
+ remove(TMP_CERT_DL_FILE);
+ if (cert == NULL)
+ return -1;
+
+ if (sha256_vector(1, (const u8 **) &cert, &len, digest2) < 0) {
+ os_free(cert);
+ return -1;
+ }
+
+ if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
+ wpa_printf(MSG_INFO, "Downloaded certificate fingerprint did not match");
+ write_result(ctx, "Downloaded certificate fingerprint did not match");
+ os_free(cert);
+ return -1;
+ }
+
+ b64 = base64_encode((unsigned char *) cert, len, NULL);
+ os_free(cert);
+ if (b64 == NULL)
+ return -1;
+
+ f = fopen(fname, "wb");
+ if (f == NULL) {
+ os_free(b64);
+ return -1;
+ }
+
+ fprintf(f, "-----BEGIN CERTIFICATE-----\n"
+ "%s"
+ "-----END CERTIFICATE-----\n",
+ b64);
+
+ os_free(b64);
+ fclose(f);
+
+ wpa_printf(MSG_INFO, "Downloaded certificate into %s and validated fingerprint",
+ fname);
+ write_summary(ctx, "Downloaded certificate into %s and validated fingerprint",
+ fname);
+
+ return 0;
+}
+
+
+static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *ca_fname)
+{
+ xml_node_t *pps, *node;
+ int ret;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return -1;
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "SubscriptionUpdate/TrustRoot");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+ return -1;
+ }
+
+ ret = download_cert(ctx, node, ca_fname);
+ xml_node_free(ctx->xml, pps);
+
+ return ret;
+}
+
+
+static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *ca_fname)
+{
+ xml_node_t *pps, *node;
+ int ret;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return -1;
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "PolicyUpdate/TrustRoot");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No PolicyUpdate/TrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+ return -1;
+ }
+
+ ret = download_cert(ctx, node, ca_fname);
+ xml_node_free(ctx->xml, pps);
+
+ return ret;
+}
+
+
+static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *ca_fname)
+{
+ xml_node_t *pps, *node, *aaa;
+ int ret;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return -1;
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "AAAServerTrustRoot");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+ return -1;
+ }
+
+ aaa = xml_node_first_child(ctx->xml, node);
+ if (aaa == NULL) {
+ wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+ return -1;
+ }
+
+ ret = download_cert(ctx, aaa, ca_fname);
+ xml_node_free(ctx->xml, pps);
+
+ return ret;
+}
+
+
+static int download_trust_roots(struct hs20_osu_client *ctx,
+ const char *pps_fname)
+{
+ char *dir, *pos;
+ char fname[300];
+ int ret;
+
+ dir = os_strdup(pps_fname);
+ if (dir == NULL)
+ return -1;
+ pos = os_strrchr(dir, '/');
+ if (pos == NULL) {
+ os_free(dir);
+ return -1;
+ }
+ *pos = '\0';
+
+ snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
+ ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
+ snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
+ cmd_dl_polupd_ca(ctx, pps_fname, fname);
+ snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
+ cmd_dl_aaa_ca(ctx, pps_fname, fname);
+
+ os_free(dir);
+
+ return ret;
+}
+
+
+static int server_dnsname_suffix_match(struct hs20_osu_client *ctx,
+ const char *fqdn)
+{
+ size_t match_len, len, i;
+ const char *val;
+
+ match_len = os_strlen(fqdn);
+
+ for (i = 0; i < ctx->server_dnsname_count; i++) {
+ wpa_printf(MSG_INFO,
+ "Checking suffix match against server dNSName %s",
+ ctx->server_dnsname[i]);
+ val = ctx->server_dnsname[i];
+ len = os_strlen(val);
+
+ if (match_len > len)
+ continue;
+
+ if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0)
+ continue; /* no match */
+
+ if (match_len == len)
+ return 1; /* exact match */
+
+ if (val[len - match_len - 1] == '.')
+ return 1; /* full label match completes suffix match */
+
+ /* Reject due to incomplete label match */
+ }
+
+ /* None of the dNSName(s) matched */
+ return 0;
+}
+
+
+int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
+ xml_node_t *add_mo, char *fname, size_t fname_len)
+{
+ char *str;
+ char *fqdn, *pos;
+ xml_node_t *tnds, *mo, *cert;
+ const char *name;
+ int ret;
+
+ if (strncmp(uri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'",
+ uri);
+ write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'",
+ uri);
+ return -1;
+ }
+
+ fqdn = strdup(uri + 8);
+ if (fqdn == NULL)
+ return -1;
+ pos = strchr(fqdn, '/');
+ if (pos) {
+ if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) {
+ wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
+ uri);
+ write_result(ctx, "Unsupported location for addMO to "
+ "add PPS MO (extra directory): '%s'", uri);
+ return -1;
+ }
+ *pos = '\0'; /* remove trailing slash and PPS node name */
+ }
+ wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
+
+ if (!server_dnsname_suffix_match(ctx, fqdn)) {
+ wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+ fqdn);
+ write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+ fqdn);
+ free(fqdn);
+ return -1;
+ }
+
+ if (!valid_fqdn(fqdn)) {
+ wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
+ write_result(ctx, "Invalid FQDN '%s'", fqdn);
+ free(fqdn);
+ return -1;
+ }
+
+ mkdir("SP", S_IRWXU);
+ snprintf(fname, fname_len, "SP/%s", fqdn);
+ if (mkdir(fname, S_IRWXU) < 0) {
+ if (errno != EEXIST) {
+ int err = errno;
+ wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+ fname, strerror(err));
+ free(fqdn);
+ return -1;
+ }
+ }
+
+ snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
+
+ if (os_file_exists(fname)) {
+ wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
+ fname);
+ write_result(ctx, "PPS file '%s' exists - reject addMO",
+ fname);
+ free(fqdn);
+ return -2;
+ }
+ wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
+
+ str = xml_node_get_text(ctx->xml, add_mo);
+ if (str == NULL) {
+ wpa_printf(MSG_INFO, "Could not extract MO text");
+ free(fqdn);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
+
+ tnds = xml_node_from_buf(ctx->xml, str);
+ xml_node_get_text_free(ctx->xml, str);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
+ free(fqdn);
+ return -1;
+ }
+
+ mo = tnds_to_mo(ctx->xml, tnds);
+ if (mo == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
+ free(fqdn);
+ return -1;
+ }
+
+ debug_dump_node(ctx, "Parsed TNDS", mo);
+
+ name = xml_node_get_localname(ctx->xml, mo);
+ if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
+ wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
+ name);
+ free(fqdn);
+ return -1;
+ }
+
+ cert = get_child_node(ctx->xml, mo,
+ "Credential/DigitalCertificate/"
+ "CertSHA256Fingerprint");
+ if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
+ xml_node_free(ctx->xml, mo);
+ free(fqdn);
+ return -1;
+ }
+ free(fqdn);
+
+ if (node_to_file(ctx->xml, fname, mo) < 0) {
+ wpa_printf(MSG_INFO, "Could not write MO to file");
+ xml_node_free(ctx->xml, mo);
+ return -1;
+ }
+ xml_node_free(ctx->xml, mo);
+
+ wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
+ write_summary(ctx, "A new PPS MO added as '%s'", fname);
+
+ ret = download_trust_roots(ctx, fname);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
+ write_summary(ctx, "Remove invalid PPS MO file");
+ unlink(fname);
+ }
+
+ return ret;
+}
+
+
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+ xml_node_t *pps)
+{
+ char *str;
+ FILE *f;
+ char backup[300];
+
+ if (ctx->client_cert_present) {
+ xml_node_t *cert;
+ cert = get_child_node(ctx->xml, pps,
+ "Credential/DigitalCertificate/"
+ "CertSHA256Fingerprint");
+ if (cert && os_file_exists("Cert/est_cert.der") &&
+ process_est_cert(ctx, cert, ctx->fqdn) < 0) {
+ wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
+ return -1;
+ }
+ }
+
+ wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
+
+ str = xml_node_to_str(ctx->xml, pps);
+ wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
+
+ snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
+ rename(pps_fname, backup);
+ f = fopen(pps_fname, "w");
+ if (f == NULL) {
+ wpa_printf(MSG_INFO, "Could not write PPS");
+ rename(backup, pps_fname);
+ free(str);
+ return -1;
+ }
+ fprintf(f, "%s\n", str);
+ fclose(f);
+
+ free(str);
+
+ return 0;
+}
+
+
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+ const char *alt_loc, char **user, char **pw)
+{
+ xml_node_t *node;
+
+ node = get_child_node(ctx->xml, pps,
+ "Credential/UsernamePassword/Username");
+ if (node)
+ *user = xml_node_get_text(ctx->xml, node);
+
+ node = get_child_node(ctx->xml, pps,
+ "Credential/UsernamePassword/Password");
+ if (node)
+ *pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+
+ node = get_child_node(ctx->xml, pps, alt_loc);
+ if (node) {
+ xml_node_t *a;
+ a = get_node(ctx->xml, node, "Username");
+ if (a) {
+ xml_node_get_text_free(ctx->xml, *user);
+ *user = xml_node_get_text(ctx->xml, a);
+ wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
+ }
+
+ a = get_node(ctx->xml, node, "Password");
+ if (a) {
+ free(*pw);
+ *pw = xml_node_get_base64_text(ctx->xml, a, NULL);
+ wpa_printf(MSG_INFO, "Use OSU password");
+ }
+ }
+}
+
+
+/* Remove old credentials based on HomeSP/FQDN */
+static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
+{
+ char cmd[300];
+ os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn);
+ if (wpa_command(ctx->ifname, cmd) < 0)
+ wpa_printf(MSG_INFO, "Failed to remove old credential(s)");
+}
+
+
+static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id,
+ xml_node_t *spe)
+{
+ xml_node_t *ssid;
+ char *txt;
+
+ ssid = get_node(ctx->xml, spe, "SSID");
+ if (ssid == NULL)
+ return;
+ txt = xml_node_get_text(ctx->xml, ssid);
+ if (txt == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt);
+ if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid");
+ xml_node_get_text_free(ctx->xml, txt);
+}
+
+
+static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id,
+ xml_node_t *spel)
+{
+ xml_node_t *child;
+
+ xml_node_for_each_child(ctx->xml, child, spel) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_spe(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id,
+ xml_node_t *prp)
+{
+ xml_node_t *node;
+ char *txt = NULL, *pos;
+ char *prio, *country_buf = NULL;
+ const char *country;
+ char val[200];
+ int priority;
+
+ node = get_node(ctx->xml, prp, "Priority");
+ if (node == NULL)
+ return;
+ prio = xml_node_get_text(ctx->xml, node);
+ if (prio == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
+ prio);
+ priority = atoi(prio);
+ xml_node_get_text_free(ctx->xml, prio);
+
+ node = get_node(ctx->xml, prp, "Country");
+ if (node) {
+ country_buf = xml_node_get_text(ctx->xml, node);
+ if (country_buf == NULL)
+ return;
+ country = country_buf;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
+ country);
+ } else {
+ country = "*";
+ }
+
+ node = get_node(ctx->xml, prp, "FQDN_Match");
+ if (node == NULL)
+ goto out;
+ txt = xml_node_get_text(ctx->xml, node);
+ if (txt == NULL)
+ goto out;
+ wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
+ txt);
+ pos = strrchr(txt, ',');
+ if (pos == NULL)
+ goto out;
+ *pos++ = '\0';
+
+ snprintf(val, sizeof(val), "%s,%d,%d,%s", txt,
+ strcmp(pos, "includeSubdomains") != 0, priority, country);
+ if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred roaming_partner");
+out:
+ xml_node_get_text_free(ctx->xml, country_buf);
+ xml_node_get_text_free(ctx->xml, txt);
+}
+
+
+static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id,
+ xml_node_t *prpl)
+{
+ xml_node_t *child;
+
+ xml_node_for_each_child(ctx->xml, child, prpl) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_prp(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id,
+ xml_node_t *min_backhaul)
+{
+ xml_node_t *node;
+ char *type, *dl = NULL, *ul = NULL;
+ int home;
+
+ node = get_node(ctx->xml, min_backhaul, "NetworkType");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
+ return;
+ }
+
+ type = xml_node_get_text(ctx->xml, node);
+ if (type == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s",
+ type);
+ if (os_strcasecmp(type, "home") == 0)
+ home = 1;
+ else if (os_strcasecmp(type, "roaming") == 0)
+ home = 0;
+ else {
+ wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold with invalid NetworkType");
+ xml_node_get_text_free(ctx->xml, type);
+ return;
+ }
+ xml_node_get_text_free(ctx->xml, type);
+
+ node = get_node(ctx->xml, min_backhaul, "DLBandwidth");
+ if (node)
+ dl = xml_node_get_text(ctx->xml, node);
+
+ node = get_node(ctx->xml, min_backhaul, "ULBandwidth");
+ if (node)
+ ul = xml_node_get_text(ctx->xml, node);
+
+ if (dl == NULL && ul == NULL) {
+ wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes");
+ return;
+ }
+
+ if (dl)
+ wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s",
+ dl);
+ if (ul)
+ wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s",
+ ul);
+
+ if (home) {
+ if (dl &&
+ set_cred(ctx->ifname, id, "min_dl_bandwidth_home", dl) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+ if (ul &&
+ set_cred(ctx->ifname, id, "min_ul_bandwidth_home", ul) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+ } else {
+ if (dl &&
+ set_cred(ctx->ifname, id, "min_dl_bandwidth_roaming", dl) <
+ 0)
+ wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+ if (ul &&
+ set_cred(ctx->ifname, id, "min_ul_bandwidth_roaming", ul) <
+ 0)
+ wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+ }
+
+ xml_node_get_text_free(ctx->xml, dl);
+ xml_node_get_text_free(ctx->xml, ul);
+}
+
+
+static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_min_backhaul(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_policy_update(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- Policy/PolicyUpdate");
+ /* Not used in wpa_supplicant */
+}
+
+
+static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client *ctx,
+ int id, xml_node_t *tuple)
+{
+ xml_node_t *node;
+ char *proto, *port;
+ char *buf;
+ size_t buflen;
+
+ node = get_node(ctx->xml, tuple, "IPProtocol");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node");
+ return;
+ }
+
+ proto = xml_node_get_text(ctx->xml, node);
+ if (proto == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s",
+ proto);
+
+ node = get_node(ctx->xml, tuple, "PortNumber");
+ port = node ? xml_node_get_text(ctx->xml, node) : NULL;
+ if (port) {
+ wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s",
+ port);
+ buflen = os_strlen(proto) + os_strlen(port) + 10;
+ buf = os_malloc(buflen);
+ if (buf)
+ os_snprintf(buf, buflen, "%s:%s", proto, port);
+ xml_node_get_text_free(ctx->xml, port);
+ } else {
+ buflen = os_strlen(proto) + 10;
+ buf = os_malloc(buflen);
+ if (buf)
+ os_snprintf(buf, buflen, "%s", proto);
+ }
+
+ xml_node_get_text_free(ctx->xml, proto);
+
+ if (buf == NULL)
+ return;
+
+ if (set_cred(ctx->ifname, id, "req_conn_capab", buf) < 0)
+ wpa_printf(MSG_INFO, "Could not set req_conn_capab");
+
+ os_free(buf);
+}
+
+
+static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_policy_required_proto_port(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Policy/MaximumBSSLoadValue - %s", str);
+ if (set_cred(ctx->ifname, id, "max_bss_load", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred max_bss_load limit");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_policy(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- Policy");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "PreferredRoamingPartnerList") == 0)
+ set_pps_cred_policy_prpl(ctx, id, child);
+ else if (os_strcasecmp(name, "MinBackhaulThreshold") == 0)
+ set_pps_cred_policy_min_backhaul_list(ctx, id, child);
+ else if (os_strcasecmp(name, "PolicyUpdate") == 0)
+ set_pps_cred_policy_update(ctx, id, child);
+ else if (os_strcasecmp(name, "SPExclusionList") == 0)
+ set_pps_cred_policy_spel(ctx, id, child);
+ else if (os_strcasecmp(name, "RequiredProtoPortTuple") == 0)
+ set_pps_cred_policy_required_proto_ports(ctx, id, child);
+ else if (os_strcasecmp(name, "MaximumBSSLoadValue") == 0)
+ set_pps_cred_policy_max_bss_load(ctx, id, child);
+ else
+ wpa_printf(MSG_INFO, "Unknown Policy node '%s'", name);
+ }
+}
+
+
+static void set_pps_cred_priority(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- CredentialPriority = %s", str);
+ if (set_cred(ctx->ifname, id, "sp_priority", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred sp_priority");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- AAAServerTrustRoot - TODO");
+}
+
+
+static void set_pps_cred_sub_update(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- SubscriptionUpdate");
+ /* not used within wpa_supplicant */
+}
+
+
+static void set_pps_cred_home_sp_network_id(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *ssid_node, *hessid_node;
+ char *ssid, *hessid;
+
+ ssid_node = get_node(ctx->xml, node, "SSID");
+ if (ssid_node == NULL) {
+ wpa_printf(MSG_INFO, "Ignore HomeSP/NetworkID without mandatory SSID node");
+ return;
+ }
+
+ hessid_node = get_node(ctx->xml, node, "HESSID");
+
+ ssid = xml_node_get_text(ctx->xml, ssid_node);
+ if (ssid == NULL)
+ return;
+ hessid = hessid_node ? xml_node_get_text(ctx->xml, hessid_node) : NULL;
+
+ wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid);
+ if (hessid)
+ wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/HESSID = %s",
+ hessid);
+
+ /* TODO: Configure to wpa_supplicant */
+
+ xml_node_get_text_free(ctx->xml, ssid);
+ xml_node_get_text_free(ctx->xml, hessid);
+}
+
+
+static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- HomeSP/NetworkID");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_home_sp_network_id(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/FriendlyName = %s", str);
+ /* not used within wpa_supplicant(?) */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/IconURL = %s", str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/FQDN = %s", str);
+ if (set_cred_quoted(ctx->ifname, id, "domain", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred domain");
+ if (set_cred_quoted(ctx->ifname, id, "domain_suffix_match", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred domain_suffix_match");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+ char *homeoi = NULL;
+ int required = 0;
+ char *str;
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (strcasecmp(name, "HomeOI") == 0 && !homeoi) {
+ homeoi = xml_node_get_text(ctx->xml, child);
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOI = %s",
+ homeoi);
+ } else if (strcasecmp(name, "HomeOIRequired") == 0) {
+ str = xml_node_get_text(ctx->xml, child);
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'",
+ str);
+ if (str == NULL)
+ continue;
+ required = strcasecmp(str, "true") == 0;
+ xml_node_get_text_free(ctx->xml, str);
+ } else
+ wpa_printf(MSG_INFO, "Unknown HomeOIList node '%s'",
+ name);
+ }
+
+ if (homeoi == NULL) {
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> without HomeOI ignored");
+ return;
+ }
+
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> '%s' required=%d",
+ homeoi, required);
+
+ if (required) {
+ if (set_cred(ctx->ifname, id, "required_roaming_consortium",
+ homeoi) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
+ } else {
+ if (set_cred_quoted(ctx->ifname, id, "roaming_consortium",
+ homeoi) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
+ }
+
+ xml_node_get_text_free(ctx->xml, homeoi);
+}
+
+
+static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- HomeSP/HomeOIList");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_home_sp_oi(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+ char *fqdn = NULL;
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) {
+ fqdn = xml_node_get_text(ctx->xml, child);
+ wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s",
+ fqdn);
+ } else
+ wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'",
+ name);
+ }
+
+ if (fqdn == NULL) {
+ wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored");
+ return;
+ }
+
+ if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node");
+
+ xml_node_get_text_free(ctx->xml, fqdn);
+}
+
+
+static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx,
+ int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+
+ wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ set_pps_cred_home_sp_other_partner(ctx, id, child);
+ }
+}
+
+
+static void set_pps_cred_home_sp_roaming_consortium_oi(
+ struct hs20_osu_client *ctx, int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str);
+ /* TODO: Set to wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- HomeSP");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "NetworkID") == 0)
+ set_pps_cred_home_sp_network_ids(ctx, id, child);
+ else if (os_strcasecmp(name, "FriendlyName") == 0)
+ set_pps_cred_home_sp_friendly_name(ctx, id, child);
+ else if (os_strcasecmp(name, "IconURL") == 0)
+ set_pps_cred_home_sp_icon_url(ctx, id, child);
+ else if (os_strcasecmp(name, "FQDN") == 0)
+ set_pps_cred_home_sp_fqdn(ctx, id, child);
+ else if (os_strcasecmp(name, "HomeOIList") == 0)
+ set_pps_cred_home_sp_oi_list(ctx, id, child);
+ else if (os_strcasecmp(name, "OtherHomePartners") == 0)
+ set_pps_cred_home_sp_other_partners(ctx, id, child);
+ else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0)
+ set_pps_cred_home_sp_roaming_consortium_oi(ctx, id,
+ child);
+ else
+ wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name);
+ }
+}
+
+
+static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- SubscriptionParameters");
+ /* not used within wpa_supplicant */
+}
+
+
+static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_username(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s",
+ str);
+ if (set_cred_quoted(ctx->ifname, id, "username", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred username");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_password(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ int len, i;
+ char *pw, *hex, *pos, *end;
+
+ pw = xml_node_get_base64_text(ctx->xml, node, &len);
+ if (pw == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw);
+
+ hex = malloc(len * 2 + 1);
+ if (hex == NULL) {
+ free(pw);
+ return;
+ }
+ end = hex + len * 2 + 1;
+ pos = hex;
+ for (i = 0; i < len; i++) {
+ snprintf(pos, end - pos, "%02x", pw[i]);
+ pos += 2;
+ }
+ free(pw);
+
+ if (set_cred(ctx->ifname, id, "password", hex) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred password");
+ free(hex);
+}
+
+
+static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s",
+ str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s",
+ str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s",
+ str);
+ /* not used within wpa_supplicant */
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO");
+}
+
+
+static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+{
+ xml_node_t *child;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "Username") == 0)
+ set_pps_cred_username(ctx, id, child);
+ else if (os_strcasecmp(name, "Password") == 0)
+ set_pps_cred_password(ctx, id, child);
+ else if (os_strcasecmp(name, "MachineManaged") == 0)
+ set_pps_cred_machine_managed(ctx, id, child);
+ else if (os_strcasecmp(name, "SoftTokenApp") == 0)
+ set_pps_cred_soft_token_app(ctx, id, child);
+ else if (os_strcasecmp(name, "AbleToShare") == 0)
+ set_pps_cred_able_to_share(ctx, id, child);
+ else if (os_strcasecmp(name, "EAPMethod") == 0)
+ set_pps_cred_eap_method(ctx, id, child);
+ else
+ wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'",
+ name);
+ }
+}
+
+
+static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node, const char *fqdn)
+{
+ char buf[200], dir[200];
+
+ wpa_printf(MSG_INFO, "- Credential/DigitalCertificate");
+
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return;
+
+ /* TODO: could build username from Subject of Subject AltName */
+ if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) {
+ wpa_printf(MSG_INFO, "Failed to set username");
+ }
+
+ snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, fqdn);
+ if (os_file_exists(buf)) {
+ if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to set client_cert");
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, fqdn);
+ if (os_file_exists(buf)) {
+ if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to set private_key");
+ }
+ }
+}
+
+
+static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node, const char *fqdn, int sim)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ char buf[200], dir[200];
+
+ if (str == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "- Credential/Realm = %s", str);
+ if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred realm");
+ xml_node_get_text_free(ctx->xml, str);
+
+ if (sim)
+ return;
+
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return;
+ snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn);
+ if (os_file_exists(buf)) {
+ if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to set CA cert");
+ }
+ }
+}
+
+
+static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+
+ if (str == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str);
+ if (os_strcasecmp(str, "true") == 0 &&
+ set_cred(ctx->ifname, id, "ocsp", "2") < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred ocsp");
+ xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id,
+ xml_node_t *sim, xml_node_t *realm)
+{
+ xml_node_t *node;
+ char *imsi, *eaptype, *str, buf[20];
+ int type;
+ int mnc_len = 3;
+ size_t imsi_len;
+
+ node = get_node(ctx->xml, sim, "EAPType");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No SIM/EAPType node in credential");
+ return;
+ }
+ eaptype = xml_node_get_text(ctx->xml, node);
+ if (eaptype == NULL) {
+ wpa_printf(MSG_INFO, "Could not extract SIM/EAPType");
+ return;
+ }
+ wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype);
+ type = atoi(eaptype);
+ xml_node_get_text_free(ctx->xml, eaptype);
+
+ switch (type) {
+ case EAP_TYPE_SIM:
+ if (set_cred(ctx->ifname, id, "eap", "SIM") < 0)
+ wpa_printf(MSG_INFO, "Could not set eap=SIM");
+ break;
+ case EAP_TYPE_AKA:
+ if (set_cred(ctx->ifname, id, "eap", "AKA") < 0)
+ wpa_printf(MSG_INFO, "Could not set eap=SIM");
+ break;
+ case EAP_TYPE_AKA_PRIME:
+ if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0)
+ wpa_printf(MSG_INFO, "Could not set eap=SIM");
+ break;
+ default:
+ wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type);
+ return;
+ }
+
+ node = get_node(ctx->xml, sim, "IMSI");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No SIM/IMSI node in credential");
+ return;
+ }
+ imsi = xml_node_get_text(ctx->xml, node);
+ if (imsi == NULL) {
+ wpa_printf(MSG_INFO, "Could not extract SIM/IMSI");
+ return;
+ }
+ wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi);
+ imsi_len = os_strlen(imsi);
+ if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) {
+ wpa_printf(MSG_INFO, "Invalid IMSI length");
+ xml_node_get_text_free(ctx->xml, imsi);
+ return;
+ }
+
+ str = xml_node_get_text(ctx->xml, node);
+ if (str) {
+ char *pos;
+ pos = os_strstr(str, "mnc");
+ if (pos && os_strlen(pos) >= 6) {
+ if (os_strncmp(imsi + 3, pos + 3, 3) == 0)
+ mnc_len = 3;
+ else if (os_strncmp(imsi + 3, pos + 4, 2) == 0)
+ mnc_len = 2;
+ }
+ xml_node_get_text_free(ctx->xml, str);
+ }
+
+ os_memcpy(buf, imsi, 3 + mnc_len);
+ buf[3 + mnc_len] = '-';
+ os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len,
+ sizeof(buf) - 3 - mnc_len - 1);
+
+ xml_node_get_text_free(ctx->xml, imsi);
+
+ if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0)
+ wpa_printf(MSG_INFO, "Could not set IMSI");
+
+ if (set_cred_quoted(ctx->ifname, id, "milenage",
+ "90dca4eda45b53cf0f12d7c9c3bc6a89:"
+ "cb9cccc4b9258e6dca4760379fb82581:000000000123") <
+ 0)
+ wpa_printf(MSG_INFO, "Could not set Milenage parameters");
+}
+
+
+static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node, const char *fqdn)
+{
+ xml_node_t *child, *sim, *realm;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- Credential");
+
+ sim = get_node(ctx->xml, node, "SIM");
+ realm = get_node(ctx->xml, node, "Realm");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "CreationDate") == 0)
+ set_pps_cred_creation_date(ctx, id, child);
+ else if (os_strcasecmp(name, "ExpirationDate") == 0)
+ set_pps_cred_expiration_date(ctx, id, child);
+ else if (os_strcasecmp(name, "UsernamePassword") == 0)
+ set_pps_cred_username_password(ctx, id, child);
+ else if (os_strcasecmp(name, "DigitalCertificate") == 0)
+ set_pps_cred_digital_cert(ctx, id, child, fqdn);
+ else if (os_strcasecmp(name, "Realm") == 0)
+ set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL);
+ else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0)
+ set_pps_cred_check_aaa_cert_status(ctx, id, child);
+ else if (os_strcasecmp(name, "SIM") == 0)
+ set_pps_cred_sim(ctx, id, child, realm);
+ else
+ wpa_printf(MSG_INFO, "Unknown Credential node '%s'",
+ name);
+ }
+}
+
+
+static void set_pps_credential(struct hs20_osu_client *ctx, int id,
+ xml_node_t *cred, const char *fqdn)
+{
+ xml_node_t *child;
+ const char *name;
+
+ xml_node_for_each_child(ctx->xml, child, cred) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "Policy") == 0)
+ set_pps_cred_policy(ctx, id, child);
+ else if (os_strcasecmp(name, "CredentialPriority") == 0)
+ set_pps_cred_priority(ctx, id, child);
+ else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0)
+ set_pps_cred_aaa_server_trust_root(ctx, id, child);
+ else if (os_strcasecmp(name, "SubscriptionUpdate") == 0)
+ set_pps_cred_sub_update(ctx, id, child);
+ else if (os_strcasecmp(name, "HomeSP") == 0)
+ set_pps_cred_home_sp(ctx, id, child);
+ else if (os_strcasecmp(name, "SubscriptionParameters") == 0)
+ set_pps_cred_sub_params(ctx, id, child);
+ else if (os_strcasecmp(name, "Credential") == 0)
+ set_pps_cred_credential(ctx, id, child, fqdn);
+ else
+ wpa_printf(MSG_INFO, "Unknown credential node '%s'",
+ name);
+ }
+}
+
+
+static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
+ const char *fqdn)
+{
+ xml_node_t *child;
+ const char *name;
+ int id;
+ char *update_identifier = NULL;
+
+ /*
+ * TODO: Could consider more complex mechanism that would remove
+ * credentials only if there are changes in the information sent to
+ * wpa_supplicant.
+ */
+ remove_sp_creds(ctx, fqdn);
+
+ xml_node_for_each_child(ctx->xml, child, pps) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "UpdateIdentifier") == 0) {
+ update_identifier = xml_node_get_text(ctx->xml, child);
+ if (update_identifier) {
+ wpa_printf(MSG_INFO, "- UpdateIdentifier = %s",
+ update_identifier);
+ break;
+ }
+ }
+ }
+
+ xml_node_for_each_child(ctx->xml, child, pps) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "UpdateIdentifier") == 0)
+ continue;
+ id = add_cred(ctx->ifname);
+ if (id < 0) {
+ wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant");
+ write_summary(ctx, "Failed to add credential to wpa_supplicant");
+ break;
+ }
+ write_summary(ctx, "Add a credential to wpa_supplicant");
+ if (update_identifier &&
+ set_cred(ctx->ifname, id, "update_identifier",
+ update_identifier) < 0)
+ wpa_printf(MSG_INFO, "Failed to set update_identifier");
+ if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) <
+ 0)
+ wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
+ wpa_printf(MSG_INFO, "credential localname: '%s'", name);
+ set_pps_credential(ctx, id, child, fqdn);
+ ctx->pps_cred_set = 1;
+ }
+
+ xml_node_get_text_free(ctx->xml, update_identifier);
+}
+
+
+void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
+{
+ xml_node_t *pps;
+ const char *fqdn;
+ char *fqdn_buf = NULL, *pos;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return;
+ }
+
+ fqdn = os_strstr(pps_fname, "SP/");
+ if (fqdn) {
+ fqdn_buf = os_strdup(fqdn + 3);
+ if (fqdn_buf == NULL)
+ return;
+ pos = os_strchr(fqdn_buf, '/');
+ if (pos)
+ *pos = '\0';
+ fqdn = fqdn_buf;
+ } else
+ fqdn = "wi-fi.org";
+
+ wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
+ fqdn);
+ set_pps(ctx, pps, fqdn);
+
+ os_free(fqdn_buf);
+ xml_node_free(ctx->xml, pps);
+}
+
+
+static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname)
+{
+ xml_node_t *pps, *node;
+ char *fqdn = NULL;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+ return -1;
+ }
+
+ node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+ if (node)
+ fqdn = xml_node_get_text(ctx->xml, node);
+
+ xml_node_free(ctx->xml, pps);
+
+ if (fqdn) {
+ FILE *f = fopen("pps-fqdn", "w");
+ if (f) {
+ fprintf(f, "%s", fqdn);
+ fclose(f);
+ }
+ xml_node_get_text_free(ctx->xml, fqdn);
+ return 0;
+ }
+
+ xml_node_get_text_free(ctx->xml, fqdn);
+ return -1;
+}
+
+
+static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+ const char *out_fname, const char *urn, int use_path)
+{
+ xml_node_t *mo, *node;
+
+ mo = node_from_file(ctx->xml, in_fname);
+ if (mo == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+ return;
+ }
+
+ node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL);
+ if (node) {
+ node_to_file(ctx->xml, out_fname, node);
+ xml_node_free(ctx->xml, node);
+ }
+
+ xml_node_free(ctx->xml, mo);
+}
+
+
+static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+ const char *out_fname)
+{
+ xml_node_t *tnds, *mo;
+
+ tnds = node_from_file(ctx->xml, in_fname);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+ return;
+ }
+
+ mo = tnds_to_mo(ctx->xml, tnds);
+ if (mo) {
+ node_to_file(ctx->xml, out_fname, mo);
+ xml_node_free(ctx->xml, mo);
+ }
+
+ xml_node_free(ctx->xml, tnds);
+}
+
+
+struct osu_icon {
+ int id;
+ char lang[4];
+ char mime_type[256];
+ char filename[256];
+};
+
+struct osu_data {
+ char bssid[20];
+ char url[256];
+ unsigned int methods;
+ char osu_ssid[33];
+ char osu_nai[256];
+ struct osu_lang_text friendly_name[MAX_OSU_VALS];
+ size_t friendly_name_count;
+ struct osu_lang_text serv_desc[MAX_OSU_VALS];
+ size_t serv_desc_count;
+ struct osu_icon icon[MAX_OSU_VALS];
+ size_t icon_count;
+};
+
+
+static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
+{
+ FILE *f;
+ char buf[1000];
+ struct osu_data *osu = NULL, *last = NULL;
+ size_t osu_count = 0;
+ char *pos, *end;
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Could not open %s", fname);
+ return NULL;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ pos = strchr(buf, '\n');
+ if (pos)
+ *pos = '\0';
+
+ if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) {
+ last = realloc(osu, (osu_count + 1) * sizeof(*osu));
+ if (last == NULL)
+ break;
+ osu = last;
+ last = &osu[osu_count++];
+ memset(last, 0, sizeof(*last));
+ snprintf(last->bssid, sizeof(last->bssid), "%s",
+ buf + 13);
+ continue;
+ }
+ if (!last)
+ continue;
+
+ if (strncmp(buf, "uri=", 4) == 0) {
+ snprintf(last->url, sizeof(last->url), "%s", buf + 4);
+ continue;
+ }
+
+ if (strncmp(buf, "methods=", 8) == 0) {
+ last->methods = strtol(buf + 8, NULL, 16);
+ continue;
+ }
+
+ if (strncmp(buf, "osu_ssid=", 9) == 0) {
+ snprintf(last->osu_ssid, sizeof(last->osu_ssid),
+ "%s", buf + 9);
+ continue;
+ }
+
+ if (os_strncmp(buf, "osu_nai=", 8) == 0) {
+ os_snprintf(last->osu_nai, sizeof(last->osu_nai),
+ "%s", buf + 8);
+ continue;
+ }
+
+ if (strncmp(buf, "friendly_name=", 14) == 0) {
+ struct osu_lang_text *txt;
+ if (last->friendly_name_count == MAX_OSU_VALS)
+ continue;
+ pos = strchr(buf + 14, ':');
+ if (pos == NULL)
+ continue;
+ *pos++ = '\0';
+ txt = &last->friendly_name[last->friendly_name_count++];
+ snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 14);
+ snprintf(txt->text, sizeof(txt->text), "%s", pos);
+ }
+
+ if (strncmp(buf, "desc=", 5) == 0) {
+ struct osu_lang_text *txt;
+ if (last->serv_desc_count == MAX_OSU_VALS)
+ continue;
+ pos = strchr(buf + 5, ':');
+ if (pos == NULL)
+ continue;
+ *pos++ = '\0';
+ txt = &last->serv_desc[last->serv_desc_count++];
+ snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 5);
+ snprintf(txt->text, sizeof(txt->text), "%s", pos);
+ }
+
+ if (strncmp(buf, "icon=", 5) == 0) {
+ struct osu_icon *icon;
+ if (last->icon_count == MAX_OSU_VALS)
+ continue;
+ icon = &last->icon[last->icon_count++];
+ icon->id = atoi(buf + 5);
+ pos = strchr(buf, ':');
+ if (pos == NULL)
+ continue;
+ pos = strchr(pos + 1, ':');
+ if (pos == NULL)
+ continue;
+ pos = strchr(pos + 1, ':');
+ if (pos == NULL)
+ continue;
+ pos++;
+ end = strchr(pos, ':');
+ if (!end)
+ continue;
+ *end = '\0';
+ snprintf(icon->lang, sizeof(icon->lang), "%s", pos);
+ pos = end + 1;
+
+ end = strchr(pos, ':');
+ if (end)
+ *end = '\0';
+ snprintf(icon->mime_type, sizeof(icon->mime_type),
+ "%s", pos);
+ if (!pos)
+ continue;
+ pos = end + 1;
+
+ end = strchr(pos, ':');
+ if (end)
+ *end = '\0';
+ snprintf(icon->filename, sizeof(icon->filename),
+ "%s", pos);
+ continue;
+ }
+ }
+
+ fclose(f);
+
+ *count = osu_count;
+ return osu;
+}
+
+
+static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
+ const char *ssid, const char *url,
+ unsigned int methods, int no_prod_assoc,
+ const char *osu_nai)
+{
+ int id;
+ const char *ifname = ctx->ifname;
+ char buf[200];
+ struct wpa_ctrl *mon;
+ int res;
+
+ id = add_network(ifname);
+ if (id < 0)
+ return -1;
+ if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
+ return -1;
+ if (osu_nai && os_strlen(osu_nai) > 0) {
+ char dir[255], fname[300];
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return -1;
+ os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
+
+ if (set_network(ifname, id, "proto", "OSEN") < 0 ||
+ set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
+ set_network(ifname, id, "pairwise", "CCMP") < 0 ||
+ set_network(ifname, id, "group", "GTK_NOT_USED") < 0 ||
+ set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
+ set_network(ifname, id, "ocsp", "2") < 0 ||
+ set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
+ set_network_quoted(ifname, id, "ca_cert", fname) < 0)
+ return -1;
+ } else {
+ if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
+ return -1;
+ }
+
+ mon = open_wpa_mon(ifname);
+ if (mon == NULL)
+ return -1;
+
+ wpa_printf(MSG_INFO, "Associate with OSU SSID");
+ write_summary(ctx, "Associate with OSU SSID");
+ snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id);
+ if (wpa_command(ifname, buf) < 0)
+ return -1;
+
+ res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED",
+ buf, sizeof(buf));
+
+ wpa_ctrl_detach(mon);
+ wpa_ctrl_close(mon);
+
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Could not connect");
+ write_summary(ctx, "Could not connect to OSU network");
+ wpa_printf(MSG_INFO, "Remove OSU network connection");
+ snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+ wpa_command(ifname, buf);
+ return -1;
+ }
+
+ write_summary(ctx, "Waiting for IP address for subscription registration");
+ if (wait_ip_addr(ifname, 15) < 0) {
+ wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ }
+
+ if (no_prod_assoc) {
+ if (res < 0)
+ return -1;
+ wpa_printf(MSG_INFO, "No production connection used for testing purposes");
+ write_summary(ctx, "No production connection used for testing purposes");
+ return 0;
+ }
+
+ ctx->no_reconnect = 1;
+ if (methods & 0x02)
+ res = cmd_prov(ctx, url);
+ else if (methods & 0x01)
+ res = cmd_oma_dm_prov(ctx, url);
+
+ wpa_printf(MSG_INFO, "Remove OSU network connection");
+ write_summary(ctx, "Remove OSU network connection");
+ snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+ wpa_command(ifname, buf);
+
+ if (res < 0)
+ return -1;
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ write_summary(ctx, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+ wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+ write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
+ int connect, int no_prod_assoc,
+ const char *friendly_name)
+{
+ char fname[255];
+ FILE *f;
+ struct osu_data *osu = NULL, *last = NULL;
+ size_t osu_count, i, j;
+ int ret;
+
+ write_summary(ctx, "OSU provider selection");
+
+ if (dir == NULL) {
+ wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
+ return -1;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
+ osu = parse_osu_providers(fname, &osu_count);
+ if (osu == NULL) {
+ wpa_printf(MSG_INFO, "Could not any OSU providers from %s",
+ fname);
+ write_result(ctx, "No OSU providers available");
+ return -1;
+ }
+
+ if (friendly_name) {
+ for (i = 0; i < osu_count; i++) {
+ last = &osu[i];
+ for (j = 0; j < last->friendly_name_count; j++) {
+ if (os_strcmp(last->friendly_name[j].text,
+ friendly_name) == 0)
+ break;
+ }
+ if (j < last->friendly_name_count)
+ break;
+ }
+ if (i == osu_count) {
+ wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
+ friendly_name);
+ write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
+ friendly_name);
+ free(osu);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
+ friendly_name);
+ write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
+ friendly_name);
+ ret = i + 1;
+ goto selected;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ wpa_printf(MSG_INFO, "Could not open %s", fname);
+ free(osu);
+ return -1;
+ }
+
+ fprintf(f, "<html><head>"
+ "<meta http-equiv=\"Content-type\" content=\"text/html; "
+ "charset=utf-8\"<title>Select service operator</title>"
+ "</head><body><h1>Select service operator</h1>\n");
+
+ if (osu_count == 0)
+ fprintf(f, "No online signup available\n");
+
+ for (i = 0; i < osu_count; i++) {
+ last = &osu[i];
+#ifdef ANDROID
+ fprintf(f, "<p>\n"
+ "<a href=\"http://localhost:12345/osu/%d\">"
+ "<table><tr><td>", (int) i + 1);
+#else /* ANDROID */
+ fprintf(f, "<p>\n"
+ "<a href=\"osu://%d\">"
+ "<table><tr><td>", (int) i + 1);
+#endif /* ANDROID */
+ for (j = 0; j < last->icon_count; j++) {
+ fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
+ last->icon[j].id,
+ strcasecmp(last->icon[j].mime_type,
+ "image/png") == 0 ? "png" : "icon");
+ }
+ fprintf(f, "<td>");
+ for (j = 0; j < last->friendly_name_count; j++) {
+ fprintf(f, "<small>[%s]</small> %s<br>\n",
+ last->friendly_name[j].lang,
+ last->friendly_name[j].text);
+ }
+ fprintf(f, "<tr><td colspan=2>");
+ for (j = 0; j < last->serv_desc_count; j++) {
+ fprintf(f, "<small>[%s]</small> %s<br>\n",
+ last->serv_desc[j].lang,
+ last->serv_desc[j].text);
+ }
+ fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
+ "SSID: %s<br>\n",
+ last->bssid, last->osu_ssid);
+ if (last->osu_nai)
+ fprintf(f, "NAI: %s<br>\n", last->osu_nai);
+ fprintf(f, "URL: %s<br>\n"
+ "methods:%s%s<br>\n"
+ "</small></p>\n",
+ last->url,
+ last->methods & 0x01 ? " OMA-DM" : "",
+ last->methods & 0x02 ? " SOAP-XML-SPP" : "");
+ }
+
+ fprintf(f, "</body></html>\n");
+
+ fclose(f);
+
+ snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
+ write_summary(ctx, "Start web browser with OSU provider selection page");
+ ret = hs20_web_browser(fname);
+
+selected:
+ if (ret > 0 && (size_t) ret <= osu_count) {
+ char *data;
+ size_t data_len;
+
+ wpa_printf(MSG_INFO, "Selected OSU id=%d", ret);
+ last = &osu[ret - 1];
+ ret = 0;
+ wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
+ wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
+ wpa_printf(MSG_INFO, "URL: %s", last->url);
+ write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
+ ret, last->bssid, last->osu_ssid, last->url);
+
+ ctx->friendly_name_count = last->friendly_name_count;
+ for (j = 0; j < last->friendly_name_count; j++) {
+ wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s",
+ last->friendly_name[j].lang,
+ last->friendly_name[j].text);
+ os_strlcpy(ctx->friendly_name[j].lang,
+ last->friendly_name[j].lang,
+ sizeof(ctx->friendly_name[j].lang));
+ os_strlcpy(ctx->friendly_name[j].text,
+ last->friendly_name[j].text,
+ sizeof(ctx->friendly_name[j].text));
+ }
+
+ ctx->icon_count = last->icon_count;
+ for (j = 0; j < last->icon_count; j++) {
+ char fname[256];
+
+ os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s",
+ dir, last->icon[j].id,
+ strcasecmp(last->icon[j].mime_type,
+ "image/png") == 0 ?
+ "png" : "icon");
+ wpa_printf(MSG_INFO, "ICON: %s (%s)",
+ fname, last->icon[j].filename);
+ os_strlcpy(ctx->icon_filename[j],
+ last->icon[j].filename,
+ sizeof(ctx->icon_filename[j]));
+
+ data = os_readfile(fname, &data_len);
+ if (data) {
+ sha256_vector(1, (const u8 **) &data, &data_len,
+ ctx->icon_hash[j]);
+ os_free(data);
+ }
+ }
+
+ if (connect == 2) {
+ if (last->methods & 0x02)
+ ret = cmd_prov(ctx, last->url);
+ else if (last->methods & 0x01)
+ ret = cmd_oma_dm_prov(ctx, last->url);
+ else
+ ret = -1;
+ } else if (connect)
+ ret = osu_connect(ctx, last->bssid, last->osu_ssid,
+ last->url, last->methods,
+ no_prod_assoc, last->osu_nai);
+ } else
+ ret = -1;
+
+ free(osu);
+
+ return ret;
+}
+
+
+static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc,
+ const char *friendly_name)
+{
+ char dir[255];
+ char fname[300], buf[400];
+ struct wpa_ctrl *mon;
+ const char *ifname;
+ int res;
+
+ ifname = ctx->ifname;
+
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return -1;
+
+ snprintf(fname, sizeof(fname), "%s/osu-info", dir);
+ if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
+ wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+ fname, strerror(errno));
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
+ if (wpa_command(ifname, buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
+ return -1;
+ }
+
+ mon = open_wpa_mon(ifname);
+ if (mon == NULL)
+ return -1;
+
+ wpa_printf(MSG_INFO, "Starting OSU fetch");
+ write_summary(ctx, "Starting OSU provider information fetch");
+ if (wpa_command(ifname, "FETCH_OSU") < 0) {
+ wpa_printf(MSG_INFO, "Could not start OSU fetch");
+ wpa_ctrl_detach(mon);
+ wpa_ctrl_close(mon);
+ return -1;
+ }
+ res = get_wpa_cli_event(mon, "OSU provider fetch completed",
+ buf, sizeof(buf));
+
+ wpa_ctrl_detach(mon);
+ wpa_ctrl_close(mon);
+
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "OSU fetch did not complete");
+ write_summary(ctx, "OSU fetch did not complete");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "OSU provider fetch completed");
+
+ return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name);
+}
+
+
+static void cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname)
+{
+ xml_node_t *pps, *node;
+ char pps_fname_buf[300];
+ char ca_fname_buf[200];
+ char *cred_username = NULL;
+ char *cred_password = NULL;
+ char *sub_rem_uri = NULL;
+ char client_cert_buf[200];
+ char *client_cert = NULL;
+ char client_key_buf[200];
+ char *client_key = NULL;
+ int spp;
+
+ wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
+ address);
+
+ if (!pps_fname) {
+ char buf[256];
+ wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+ if (os_strncmp(address, "fqdn=", 5) == 0) {
+ wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+ os_snprintf(buf, sizeof(buf), "%s", address + 5);
+ address = NULL;
+ } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+ sizeof(buf)) < 0) {
+ wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+ return;
+ }
+ os_free(ctx->fqdn);
+ ctx->fqdn = os_strdup(buf);
+ if (ctx->fqdn == NULL)
+ return;
+ wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+ buf);
+ os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+ "SP/%s/pps.xml", ctx->fqdn);
+ pps_fname = pps_fname_buf;
+
+ os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+ ctx->fqdn);
+ ca_fname = ca_fname_buf;
+ }
+
+ if (!os_file_exists(pps_fname)) {
+ wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+ pps_fname);
+ return;
+ }
+ wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+ if (ca_fname && !os_file_exists(ca_fname)) {
+ wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+ ca_fname);
+ return;
+ }
+ wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+ ctx->ca_fname = ca_fname;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read PPS MO");
+ return;
+ }
+
+ if (!ctx->fqdn) {
+ char *tmp;
+ node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+ return;
+ }
+ tmp = xml_node_get_text(ctx->xml, node);
+ if (tmp == NULL) {
+ wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+ return;
+ }
+ ctx->fqdn = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ if (!ctx->fqdn) {
+ wpa_printf(MSG_INFO, "No FQDN known");
+ return;
+ }
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "SubscriptionUpdate/UpdateMethod");
+ if (node) {
+ char *tmp;
+ tmp = xml_node_get_text(ctx->xml, node);
+ if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+ spp = 0;
+ else
+ spp = 1;
+ } else {
+ wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+ spp = 1;
+ }
+
+ get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
+ &cred_username, &cred_password);
+ if (cred_username)
+ wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+ if (cred_password)
+ wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+ if (cred_username == NULL && cred_password == NULL &&
+ get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+ wpa_printf(MSG_INFO, "Using client certificate");
+ os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+ "SP/%s/client-cert.pem", ctx->fqdn);
+ client_cert = client_cert_buf;
+ os_snprintf(client_key_buf, sizeof(client_key_buf),
+ "SP/%s/client-key.pem", ctx->fqdn);
+ client_key = client_key_buf;
+ ctx->client_cert_present = 1;
+ }
+
+ node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
+ if (node) {
+ sub_rem_uri = xml_node_get_text(ctx->xml, node);
+ if (sub_rem_uri &&
+ (!address || os_strcmp(address, sub_rem_uri) != 0)) {
+ wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
+ sub_rem_uri);
+ address = sub_rem_uri;
+ }
+ }
+ if (!address) {
+ wpa_printf(MSG_INFO, "Server URL not known");
+ return;
+ }
+
+ write_summary(ctx, "Wait for IP address for subscriptiom remediation");
+ wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
+
+ if (wait_ip_addr(ctx->ifname, 15) < 0) {
+ wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ }
+
+ if (spp)
+ spp_sub_rem(ctx, address, pps_fname,
+ client_cert, client_key,
+ cred_username, cred_password, pps);
+ else
+ oma_dm_sub_rem(ctx, address, pps_fname,
+ client_cert, client_key,
+ cred_username, cred_password, pps);
+
+ xml_node_get_text_free(ctx->xml, sub_rem_uri);
+ xml_node_get_text_free(ctx->xml, cred_username);
+ str_clear_free(cred_password);
+ xml_node_free(ctx->xml, pps);
+}
+
+
+static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname, const char *ca_fname)
+{
+ xml_node_t *pps;
+ xml_node_t *node;
+ char pps_fname_buf[300];
+ char ca_fname_buf[200];
+ char *uri = NULL;
+ char *cred_username = NULL;
+ char *cred_password = NULL;
+ char client_cert_buf[200];
+ char *client_cert = NULL;
+ char client_key_buf[200];
+ char *client_key = NULL;
+ int spp;
+
+ wpa_printf(MSG_INFO, "Policy update requested");
+
+ if (!pps_fname) {
+ char buf[256];
+ wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+ if (os_strncmp(address, "fqdn=", 5) == 0) {
+ wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+ os_snprintf(buf, sizeof(buf), "%s", address + 5);
+ address = NULL;
+ } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+ sizeof(buf)) < 0) {
+ wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+ return -1;
+ }
+ os_free(ctx->fqdn);
+ ctx->fqdn = os_strdup(buf);
+ if (ctx->fqdn == NULL)
+ return -1;
+ wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+ buf);
+ os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+ "SP/%s/pps.xml", ctx->fqdn);
+ pps_fname = pps_fname_buf;
+
+ os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+ buf);
+ ca_fname = ca_fname_buf;
+ }
+
+ if (!os_file_exists(pps_fname)) {
+ wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+ pps_fname);
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+ if (ca_fname && !os_file_exists(ca_fname)) {
+ wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+ ca_fname);
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+ ctx->ca_fname = ca_fname;
+
+ pps = node_from_file(ctx->xml, pps_fname);
+ if (pps == NULL) {
+ wpa_printf(MSG_INFO, "Could not read PPS MO");
+ return -1;
+ }
+
+ if (!ctx->fqdn) {
+ char *tmp;
+ node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+ return -1;
+ }
+ tmp = xml_node_get_text(ctx->xml, node);
+ if (tmp == NULL) {
+ wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+ return -1;
+ }
+ ctx->fqdn = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ if (!ctx->fqdn) {
+ wpa_printf(MSG_INFO, "No FQDN known");
+ return -1;
+ }
+ }
+
+ node = get_child_node(ctx->xml, pps,
+ "Policy/PolicyUpdate/UpdateMethod");
+ if (node) {
+ char *tmp;
+ tmp = xml_node_get_text(ctx->xml, node);
+ if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+ spp = 0;
+ else
+ spp = 1;
+ } else {
+ wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+ spp = 1;
+ }
+
+ get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword",
+ &cred_username, &cred_password);
+ if (cred_username)
+ wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+ if (cred_password)
+ wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+ if (cred_username == NULL && cred_password == NULL &&
+ get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+ wpa_printf(MSG_INFO, "Using client certificate");
+ os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+ "SP/%s/client-cert.pem", ctx->fqdn);
+ client_cert = client_cert_buf;
+ os_snprintf(client_key_buf, sizeof(client_key_buf),
+ "SP/%s/client-key.pem", ctx->fqdn);
+ client_key = client_key_buf;
+ }
+
+ if (!address) {
+ node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI");
+ if (node) {
+ uri = xml_node_get_text(ctx->xml, node);
+ wpa_printf(MSG_INFO, "URI based on PPS: %s", uri);
+ address = uri;
+ }
+ }
+ if (!address) {
+ wpa_printf(MSG_INFO, "Server URL not known");
+ return -1;
+ }
+
+ if (spp)
+ spp_pol_upd(ctx, address, pps_fname,
+ client_cert, client_key,
+ cred_username, cred_password, pps);
+ else
+ oma_dm_pol_upd(ctx, address, pps_fname,
+ client_cert, client_key,
+ cred_username, cred_password, pps);
+
+ xml_node_get_text_free(ctx->xml, uri);
+ xml_node_get_text_free(ctx->xml, cred_username);
+ str_clear_free(cred_password);
+ xml_node_free(ctx->xml, pps);
+
+ return 0;
+}
+
+
+static char * get_hostname(const char *url)
+{
+ const char *pos, *end, *end2;
+ char *ret;
+
+ if (url == NULL)
+ return NULL;
+
+ pos = os_strchr(url, '/');
+ if (pos == NULL)
+ return NULL;
+ pos++;
+ if (*pos != '/')
+ return NULL;
+ pos++;
+
+ end = os_strchr(pos, '/');
+ end2 = os_strchr(pos, ':');
+ if (end && end2 && end2 < end)
+ end = end2;
+ if (end)
+ end--;
+ else {
+ end = pos;
+ while (*end)
+ end++;
+ if (end > pos)
+ end--;
+ }
+
+ ret = os_malloc(end - pos + 2);
+ if (ret == NULL)
+ return NULL;
+
+ os_memcpy(ret, pos, end - pos + 1);
+ ret[end - pos + 1] = '\0';
+
+ return ret;
+}
+
+
+static int osu_cert_cb(void *_ctx, struct http_cert *cert)
+{
+ struct hs20_osu_client *ctx = _ctx;
+ unsigned int i, j;
+ int found;
+ char *host = NULL;
+
+ wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d)",
+ !ctx->no_osu_cert_validation);
+
+ host = get_hostname(ctx->server_url);
+
+ for (i = 0; i < ctx->server_dnsname_count; i++)
+ os_free(ctx->server_dnsname[i]);
+ os_free(ctx->server_dnsname);
+ ctx->server_dnsname = os_calloc(cert->num_dnsname, sizeof(char *));
+ ctx->server_dnsname_count = 0;
+
+ found = 0;
+ for (i = 0; i < cert->num_dnsname; i++) {
+ if (ctx->server_dnsname) {
+ ctx->server_dnsname[ctx->server_dnsname_count] =
+ os_strdup(cert->dnsname[i]);
+ if (ctx->server_dnsname[ctx->server_dnsname_count])
+ ctx->server_dnsname_count++;
+ }
+ if (host && os_strcasecmp(host, cert->dnsname[i]) == 0)
+ found = 1;
+ wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]);
+ }
+
+ if (host && !found) {
+ wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection",
+ host);
+ write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection",
+ host);
+ os_free(host);
+ return -1;
+ }
+
+ os_free(host);
+
+ for (i = 0; i < cert->num_othername; i++) {
+ if (os_strcmp(cert->othername[i].oid,
+ "1.3.6.1.4.1.40808.1.1.1") == 0) {
+ wpa_hexdump_ascii(MSG_INFO,
+ "id-wfa-hotspot-friendlyName",
+ cert->othername[i].data,
+ cert->othername[i].len);
+ }
+ }
+
+ for (j = 0; !ctx->no_osu_cert_validation &&
+ j < ctx->friendly_name_count; j++) {
+ int found = 0;
+ for (i = 0; i < cert->num_othername; i++) {
+ if (os_strcmp(cert->othername[i].oid,
+ "1.3.6.1.4.1.40808.1.1.1") != 0)
+ continue;
+ if (cert->othername[i].len < 3)
+ continue;
+ if (os_strncasecmp((char *) cert->othername[i].data,
+ ctx->friendly_name[j].lang, 3) != 0)
+ continue;
+ if (os_strncmp((char *) cert->othername[i].data + 3,
+ ctx->friendly_name[j].text,
+ cert->othername[i].len - 3) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'",
+ ctx->friendly_name[j].lang,
+ ctx->friendly_name[j].text);
+ write_result(ctx, "No friendly name match found for '[%s]%s'",
+ ctx->friendly_name[j].lang,
+ ctx->friendly_name[j].text);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < cert->num_logo; i++) {
+ struct http_logo *logo = &cert->logo[i];
+
+ wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'",
+ logo->alg_oid, logo->uri);
+ wpa_hexdump_ascii(MSG_INFO, "hashValue",
+ logo->hash, logo->hash_len);
+ }
+
+ for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
+ int found = 0;
+ char *name = ctx->icon_filename[j];
+ size_t name_len = os_strlen(name);
+
+ wpa_printf(MSG_INFO, "Looking for icon file name '%s' match",
+ name);
+ for (i = 0; i < cert->num_logo; i++) {
+ struct http_logo *logo = &cert->logo[i];
+ size_t uri_len = os_strlen(logo->uri);
+ char *pos;
+
+ wpa_printf(MSG_INFO, "Comparing to '%s' uri_len=%d name_len=%d",
+ logo->uri, (int) uri_len, (int) name_len);
+ if (uri_len < 1 + name_len)
+ continue;
+ pos = &logo->uri[uri_len - name_len - 1];
+ if (*pos != '/')
+ continue;
+ pos++;
+ if (os_strcmp(pos, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
+ name);
+ write_result(ctx,
+ "No icon filename match found for '%s'",
+ name);
+ return -1;
+ }
+ }
+
+ for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
+ int found = 0;
+
+ for (i = 0; i < cert->num_logo; i++) {
+ struct http_logo *logo = &cert->logo[i];
+
+ if (logo->hash_len != 32)
+ continue;
+ if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "No icon hash match found");
+ write_result(ctx, "No icon hash match found");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int init_ctx(struct hs20_osu_client *ctx)
+{
+ xml_node_t *devinfo, *devid;
+
+ os_memset(ctx, 0, sizeof(*ctx));
+ ctx->ifname = "wlan0";
+ ctx->xml = xml_node_init_ctx(ctx, NULL);
+ if (ctx->xml == NULL)
+ return -1;
+
+ devinfo = node_from_file(ctx->xml, "devinfo.xml");
+ if (!devinfo) {
+ wpa_printf(MSG_ERROR, "devinfo.xml not found");
+ return -1;
+ }
+
+ devid = get_node(ctx->xml, devinfo, "DevId");
+ if (devid) {
+ char *tmp = xml_node_get_text(ctx->xml, devid);
+ if (tmp) {
+ ctx->devid = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ }
+ }
+ xml_node_free(ctx->xml, devinfo);
+
+ if (ctx->devid == NULL) {
+ wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml");
+ return -1;
+ }
+
+ ctx->http = http_init_ctx(ctx, ctx->xml);
+ if (ctx->http == NULL) {
+ xml_node_deinit_ctx(ctx->xml);
+ return -1;
+ }
+ http_ocsp_set(ctx->http, 2);
+ http_set_cert_cb(ctx->http, osu_cert_cb, ctx);
+
+ return 0;
+}
+
+
+static void deinit_ctx(struct hs20_osu_client *ctx)
+{
+ size_t i;
+
+ http_deinit_ctx(ctx->http);
+ xml_node_deinit_ctx(ctx->xml);
+ os_free(ctx->fqdn);
+ os_free(ctx->server_url);
+ os_free(ctx->devid);
+
+ for (i = 0; i < ctx->server_dnsname_count; i++)
+ os_free(ctx->server_dnsname[i]);
+ os_free(ctx->server_dnsname);
+}
+
+
+static void check_workarounds(struct hs20_osu_client *ctx)
+{
+ FILE *f;
+ char buf[100];
+ unsigned long int val = 0;
+
+ f = fopen("hs20-osu-client.workarounds", "r");
+ if (f == NULL)
+ return;
+
+ if (fgets(buf, sizeof(buf), f))
+ val = strtoul(buf, NULL, 16);
+
+ fclose(f);
+
+ if (val) {
+ wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val);
+ ctx->workarounds = val;
+ if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL)
+ http_ocsp_set(ctx->http, 1);
+ }
+}
+
+
+static void usage(void)
+{
+ printf("usage: hs20-osu-client [-dddqqKt] [-S<station ifname>] \\\n"
+ " [-w<wpa_supplicant ctrl_iface dir>] "
+ "[-r<result file>] [-f<debug file>] \\\n"
+ " [-s<summary file>] \\\n"
+ " <command> [arguments..]\n"
+ "commands:\n"
+ "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n"
+ "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) "
+ "[URN]>\n"
+ "- from_tnds <XML MO in TNDS format> <XML MO>\n"
+ "- set_pps <PerProviderSubscription XML file name>\n"
+ "- get_fqdn <PerProviderSubscription XML file name>\n"
+ "- pol_upd [Server URL] [PPS] [CA cert]\n"
+ "- sub_rem <Server URL> [PPS] [CA cert]\n"
+ "- prov <Server URL> [CA cert]\n"
+ "- oma_dm_prov <Server URL> [CA cert]\n"
+ "- sim_prov <Server URL> [CA cert]\n"
+ "- oma_dm_sim_prov <Server URL> [CA cert]\n"
+ "- signup [CA cert]\n"
+ "- dl_osu_ca <PPS> <CA file>\n"
+ "- dl_polupd_ca <PPS> <CA file>\n"
+ "- dl_aaa_ca <PPS> <CA file>\n"
+ "- browser <URL>\n"
+ "- parse_cert <X.509 certificate (DER)>\n"
+ "- osu_select <OSU info directory> [CA cert]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct hs20_osu_client ctx;
+ int c;
+ int ret = 0;
+ int no_prod_assoc = 0;
+ const char *friendly_name = NULL;
+ const char *wpa_debug_file_path = NULL;
+ extern char *wpas_ctrl_path;
+ extern int wpa_debug_level;
+ extern int wpa_debug_show_keys;
+ extern int wpa_debug_timestamp;
+
+ if (init_ctx(&ctx) < 0)
+ return -1;
+
+ for (;;) {
+ c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'd':
+ if (wpa_debug_level > 0)
+ wpa_debug_level--;
+ break;
+ case 'f':
+ wpa_debug_file_path = optarg;
+ break;
+ case 'K':
+ wpa_debug_show_keys++;
+ break;
+ case 'N':
+ no_prod_assoc = 1;
+ break;
+ case 'O':
+ friendly_name = optarg;
+ break;
+ case 'q':
+ wpa_debug_level++;
+ break;
+ case 'r':
+ ctx.result_file = optarg;
+ break;
+ case 's':
+ ctx.summary_file = optarg;
+ break;
+ case 'S':
+ ctx.ifname = optarg;
+ break;
+ case 't':
+ wpa_debug_timestamp++;
+ break;
+ case 'w':
+ wpas_ctrl_path = optarg;
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ break;
+ }
+ }
+
+ if (argc - optind < 1) {
+ usage();
+ exit(0);
+ }
+
+ wpa_debug_open_file(wpa_debug_file_path);
+
+#ifdef __linux__
+ setlinebuf(stdout);
+#endif /* __linux__ */
+
+ if (ctx.result_file)
+ unlink(ctx.result_file);
+ wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======"
+ "================", argv[optind]);
+ check_workarounds(&ctx);
+
+ if (strcmp(argv[optind], "to_tnds") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
+ argc > optind + 3 ? argv[optind + 3] : NULL,
+ 0);
+ } else if (strcmp(argv[optind], "to_tnds2") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
+ argc > optind + 3 ? argv[optind + 3] : NULL,
+ 1);
+ } else if (strcmp(argv[optind], "from_tnds") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "sub_rem") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ if (argc - optind < 2)
+ wpa_printf(MSG_ERROR, "Server URL missing from command line");
+ else
+ cmd_sub_rem(&ctx, argv[optind + 1],
+ argc > optind + 2 ? argv[optind + 2] : NULL,
+ argc > optind + 3 ? argv[optind + 3] :
+ NULL);
+ } else if (strcmp(argv[optind], "pol_upd") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL,
+ argc > optind + 2 ? argv[optind + 2] : NULL,
+ argc > optind + 3 ? argv[optind + 3] : NULL);
+ } else if (strcmp(argv[optind], "prov") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ ctx.ca_fname = argv[optind + 2];
+ cmd_prov(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "sim_prov") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ ctx.ca_fname = argv[optind + 2];
+ cmd_sim_prov(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "osu_select") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
+ cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
+ } else if (strcmp(argv[optind], "signup") == 0) {
+ ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
+ ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
+ } else if (strcmp(argv[optind], "set_pps") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_set_pps(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "get_fqdn") == 0) {
+ if (argc - optind < 1) {
+ usage();
+ exit(0);
+ }
+ ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "oma_dm_prov") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ ctx.ca_fname = argv[optind + 2];
+ cmd_oma_dm_prov(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ ctx.ca_fname = argv[optind + 2];
+ if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) {
+ write_summary(&ctx, "Failed to complete OMA DM SIM provisioning");
+ return -1;
+ }
+ } else if (strcmp(argv[optind], "oma_dm_add") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "oma_dm_replace") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]);
+ } else if (strcmp(argv[optind], "est_csr") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+ mkdir("Cert", S_IRWXU);
+ est_build_csr(&ctx, argv[optind + 1]);
+ } else if (strcmp(argv[optind], "browser") == 0) {
+ int ret;
+
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+
+ wpa_printf(MSG_INFO, "Launch web browser to URL %s",
+ argv[optind + 1]);
+ ret = hs20_web_browser(argv[optind + 1]);
+ wpa_printf(MSG_INFO, "Web browser result: %d", ret);
+ } else if (strcmp(argv[optind], "parse_cert") == 0) {
+ if (argc - optind < 2) {
+ usage();
+ exit(0);
+ }
+
+ wpa_debug_level = MSG_MSGDUMP;
+ http_parse_x509_certificate(ctx.http, argv[optind + 1]);
+ wpa_debug_level = MSG_INFO;
+ } else {
+ wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
+ }
+
+ deinit_ctx(&ctx);
+ wpa_printf(MSG_DEBUG,
+ "===[hs20-osu-client END ]======================");
+
+ wpa_debug_close_file();
+
+ return ret;
+}
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
new file mode 100644
index 0000000..9a7059e
--- /dev/null
+++ b/hs20/client/osu_client.h
@@ -0,0 +1,118 @@
+/*
+ * Hotspot 2.0 - OSU client
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OSU_CLIENT_H
+#define OSU_CLIENT_H
+
+#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
+
+#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
+#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
+#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+#define MAX_OSU_VALS 10
+
+struct osu_lang_text {
+ char lang[4];
+ char text[253];
+};
+
+struct hs20_osu_client {
+ struct xml_node_ctx *xml;
+ struct http_ctx *http;
+ int no_reconnect;
+ char pps_fname[300];
+ char *devid;
+ const char *result_file;
+ const char *summary_file;
+ const char *ifname;
+ const char *ca_fname;
+ int no_osu_cert_validation; /* for EST operations */
+ char *fqdn;
+ char *server_url;
+ struct osu_lang_text friendly_name[MAX_OSU_VALS];
+ size_t friendly_name_count;
+ size_t icon_count;
+ char icon_filename[MAX_OSU_VALS][256];
+ u8 icon_hash[MAX_OSU_VALS][32];
+ int pps_cred_set;
+ int pps_updated;
+ int client_cert_present;
+ char **server_dnsname;
+ size_t server_dnsname_count;
+#define WORKAROUND_OCSP_OPTIONAL 0x00000001
+ unsigned long int workarounds;
+};
+
+
+/* osu_client.c */
+
+void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
+ xml_node_t *node);
+int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
+int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
+ xml_node_t *add_mo, char *fname, size_t fname_len);
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+ const char *alt_loc, char **user, char **pw);
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+ xml_node_t *pps);
+void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+/* spp_client.c */
+
+void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps);
+void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps);
+int cmd_prov(struct hs20_osu_client *ctx, const char *url);
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url);
+
+
+/* oma_dm_client.c */
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url);
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url);
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps);
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps);
+void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname);
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *add_fname);
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+ const char *replace_fname);
+
+/* est.c */
+
+int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
+int est_build_csr(struct hs20_osu_client *ctx, const char *url);
+int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
+ const char *user, const char *pw);
+
+#endif /* OSU_CLIENT_H */
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
new file mode 100644
index 0000000..302a050
--- /dev/null
+++ b/hs20/client/spp_client.c
@@ -0,0 +1,995 @@
+/*
+ * Hotspot 2.0 SPP client
+ * Copyright (c) 2012-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 <sys/stat.h>
+
+#include "common.h"
+#include "browser.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/base64.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+ const char *session_id,
+ const char *spp_status,
+ const char *error_code);
+static void hs20_policy_update_complete(
+ struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+ char *attr_name)
+{
+ return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
+}
+
+
+static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
+ const char *expected_name)
+{
+ struct xml_node_ctx *xctx = ctx->xml;
+ const char *name;
+ char *err;
+ int ret;
+
+ if (!xml_node_is_element(xctx, node))
+ return -1;
+
+ name = xml_node_get_localname(xctx, node);
+ if (name == NULL)
+ return -1;
+
+ if (strcmp(expected_name, name) != 0) {
+ wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
+ name, expected_name);
+ write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
+ name, expected_name);
+ return -1;
+ }
+
+ ret = xml_validate(xctx, node, "spp.xsd", &err);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
+ write_summary(ctx, "SPP XML schema validation failed");
+ os_free(err);
+ }
+ return ret;
+}
+
+
+static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
+ xml_node_t *parent, const char *urn,
+ const char *fname)
+{
+ xml_node_t *node;
+ xml_node_t *fnode, *tnds;
+ char *str;
+
+ fnode = node_from_file(ctx, fname);
+ if (!fnode)
+ return;
+ tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
+ xml_node_free(ctx, fnode);
+ if (!tnds)
+ return;
+
+ str = xml_node_to_str(ctx, tnds);
+ xml_node_free(ctx, tnds);
+ if (str == NULL)
+ return;
+
+ node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
+ if (node)
+ xml_node_add_attr(ctx, node, ns, "moURN", urn);
+ os_free(str);
+}
+
+
+static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
+ xml_namespace_t **ret_ns,
+ const char *session_id,
+ const char *reason)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+
+ write_summary(ctx, "Building sppPostDevData requestReason='%s'",
+ reason);
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppPostDevData");
+ if (spp_node == NULL)
+ return NULL;
+ if (ret_ns)
+ *ret_ns = ns;
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
+ if (session_id)
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
+ session_id);
+ xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
+ "http://localhost:12345/");
+
+ xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
+ "1.0");
+ xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
+ URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
+ URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
+
+ add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
+ "devinfo.xml");
+ add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
+ "devdetail.xml");
+
+ return spp_node;
+}
+
+
+static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
+ xml_node_t *update)
+{
+ xml_node_t *node, *parent, *tnds, *unode;
+ char *str;
+ const char *name;
+ char *uri, *pos;
+ char *cdata, *cdata_end;
+ size_t fqdn_len;
+
+ wpa_printf(MSG_INFO, "Processing updateNode");
+ debug_dump_node(ctx, "updateNode", update);
+
+ uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
+ if (uri == NULL) {
+ wpa_printf(MSG_INFO, "No managementTreeURI present");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
+
+ name = os_strrchr(uri, '/');
+ if (name == NULL) {
+ wpa_printf(MSG_INFO, "Unexpected URI");
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ name++;
+ wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
+
+ str = xml_node_get_text(ctx->xml, update);
+ if (str == NULL) {
+ wpa_printf(MSG_INFO, "Could not extract MO text");
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
+ cdata = strstr(str, "<![CDATA[");
+ cdata_end = strstr(str, "]]>");
+ if (cdata && cdata_end && cdata_end > cdata &&
+ cdata < strstr(str, "MgmtTree") &&
+ cdata_end > strstr(str, "/MgmtTree")) {
+ char *tmp;
+ wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
+ tmp = strdup(cdata + 9);
+ if (tmp) {
+ cdata_end = strstr(tmp, "]]>");
+ if (cdata_end)
+ *cdata_end = '\0';
+ wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
+ tmp);
+ tnds = xml_node_from_buf(ctx->xml, tmp);
+ free(tmp);
+ } else
+ tnds = NULL;
+ } else
+ tnds = xml_node_from_buf(ctx->xml, str);
+ xml_node_get_text_free(ctx->xml, str);
+ if (tnds == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+
+ unode = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (unode == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+
+ debug_dump_node(ctx, "Parsed TNDS", unode);
+
+ if (get_node_uri(ctx->xml, unode, name) == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+
+ if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ pos = uri + 8;
+
+ if (ctx->fqdn == NULL) {
+ wpa_printf(MSG_INFO, "FQDN not known");
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ fqdn_len = os_strlen(ctx->fqdn);
+ if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+ pos[fqdn_len] != '/') {
+ wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
+ ctx->fqdn);
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ pos += fqdn_len + 1;
+
+ if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+ wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
+ ctx->fqdn);
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ pos += 24;
+
+ wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
+
+ node = get_node(ctx->xml, pps, pos);
+ if (node) {
+ parent = xml_node_get_parent(ctx->xml, node);
+ xml_node_detach(ctx->xml, node);
+ wpa_printf(MSG_INFO, "Replace '%s' node", name);
+ } else {
+ char *pos2;
+ pos2 = os_strrchr(pos, '/');
+ if (pos2 == NULL) {
+ parent = pps;
+ } else {
+ *pos2 = '\0';
+ parent = get_node(ctx->xml, pps, pos);
+ }
+ if (parent == NULL) {
+ wpa_printf(MSG_INFO, "Could not find parent %s", pos);
+ xml_node_free(ctx->xml, unode);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "Add '%s' node", name);
+ }
+ xml_node_add_child(ctx->xml, parent, unode);
+
+ xml_node_get_attr_value_free(ctx->xml, uri);
+
+ return 0;
+}
+
+
+static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
+ const char *pps_fname, xml_node_t *pps)
+{
+ wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
+ xml_node_for_each_sibling(ctx->xml, update) {
+ xml_node_for_each_check(ctx->xml, update);
+ if (process_update_node(ctx, pps, update) < 0)
+ return -1;
+ }
+
+ return update_pps_file(ctx, pps_fname, pps);
+}
+
+
+static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
+ const char *pps_fname)
+{
+ /*
+ * Update wpa_supplicant credentials and reconnect using updated
+ * information.
+ */
+ wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+ cmd_set_pps(ctx, pps_fname);
+
+ if (ctx->no_reconnect)
+ return;
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+ wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
+ xml_node_t *cmd,
+ const char *session_id,
+ const char *pps_fname)
+{
+ xml_namespace_t *ns;
+ xml_node_t *node, *ret_node;
+ char *urn;
+
+ urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
+ if (!urn) {
+ wpa_printf(MSG_INFO, "No URN included");
+ return NULL;
+ }
+ wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
+ if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+ wpa_printf(MSG_INFO, "Unsupported moURN");
+ xml_node_get_attr_value_free(ctx->xml, urn);
+ return NULL;
+ }
+ xml_node_get_attr_value_free(ctx->xml, urn);
+
+ if (!pps_fname) {
+ wpa_printf(MSG_INFO, "PPS file name no known");
+ return NULL;
+ }
+
+ node = build_spp_post_dev_data(ctx, &ns, session_id,
+ "MO upload");
+ if (node == NULL)
+ return NULL;
+ add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
+
+ ret_node = soap_send_receive(ctx->http, node);
+ if (ret_node == NULL)
+ return NULL;
+
+ debug_dump_node(ctx, "Received response to MO upload", ret_node);
+
+ if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return NULL;
+ }
+
+ return ret_node;
+}
+
+
+static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
+ char *fname, size_t fname_len)
+{
+ char *uri, *urn;
+ int ret;
+
+ debug_dump_node(ctx, "Received addMO", add_mo);
+
+ urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
+ if (urn == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
+ if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+ wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
+ xml_node_get_attr_value_free(ctx->xml, urn);
+ return -1;
+ }
+ xml_node_get_attr_value_free(ctx->xml, urn);
+
+ uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
+ if (uri == NULL) {
+ wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
+
+ ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
+ xml_node_get_attr_value_free(ctx->xml, uri);
+ return ret;
+}
+
+
+static int process_spp_user_input_response(struct hs20_osu_client *ctx,
+ const char *session_id,
+ xml_node_t *add_mo)
+{
+ int ret;
+ char fname[300];
+
+ debug_dump_node(ctx, "addMO", add_mo);
+
+ wpa_printf(MSG_INFO, "Subscription registration completed");
+
+ if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
+ wpa_printf(MSG_INFO, "Could not add MO");
+ ret = hs20_spp_update_response(
+ ctx, session_id,
+ "Error occurred",
+ "MO addition or update failed");
+ return 0;
+ }
+
+ ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
+ if (ret == 0)
+ hs20_sub_rem_complete(ctx, fname);
+
+ return 0;
+}
+
+
+static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
+ const char *session_id)
+{
+ xml_node_t *node, *ret_node;
+
+ node = build_spp_post_dev_data(ctx, NULL, session_id,
+ "User input completed");
+ if (node == NULL)
+ return NULL;
+
+ ret_node = soap_send_receive(ctx->http, node);
+ if (!ret_node) {
+ if (soap_reinit_client(ctx->http) < 0)
+ return NULL;
+ wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+ node = build_spp_post_dev_data(ctx, NULL, session_id,
+ "User input completed");
+ if (node == NULL)
+ return NULL;
+ ret_node = soap_send_receive(ctx->http, node);
+ if (ret_node == NULL)
+ return NULL;
+ wpa_printf(MSG_INFO, "Continue with new connection");
+ }
+
+ if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return NULL;
+ }
+
+ return ret_node;
+}
+
+
+static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
+ xml_node_t *cmd,
+ const char *session_id,
+ const char *pps_fname)
+{
+ xml_namespace_t *ns;
+ xml_node_t *node, *ret_node;
+ int res;
+
+ wpa_printf(MSG_INFO, "Client certificate enrollment");
+
+ res = osu_get_certificate(ctx, cmd);
+ if (res < 0)
+ wpa_printf(MSG_INFO, "EST simpleEnroll failed");
+
+ node = build_spp_post_dev_data(ctx, &ns, session_id,
+ res == 0 ?
+ "Certificate enrollment completed" :
+ "Certificate enrollment failed");
+ if (node == NULL)
+ return NULL;
+
+ ret_node = soap_send_receive(ctx->http, node);
+ if (ret_node == NULL)
+ return NULL;
+
+ debug_dump_node(ctx, "Received response to certificate enrollment "
+ "completed", ret_node);
+
+ if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return NULL;
+ }
+
+ return ret_node;
+}
+
+
+static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
+ const char *session_id, const char *pps_fname,
+ xml_node_t *pps, xml_node_t **ret_node)
+{
+ xml_node_t *cmd;
+ const char *name;
+ char *uri;
+ char *id = strdup(session_id);
+
+ if (id == NULL)
+ return -1;
+
+ *ret_node = NULL;
+
+ debug_dump_node(ctx, "exec", exec);
+
+ xml_node_for_each_child(ctx->xml, cmd, exec) {
+ xml_node_for_each_check(ctx->xml, cmd);
+ break;
+ }
+ if (!cmd) {
+ wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
+ cmd);
+ free(id);
+ return -1;
+ }
+
+ name = xml_node_get_localname(ctx->xml, cmd);
+
+ if (strcasecmp(name, "launchBrowserToURI") == 0) {
+ int res;
+ uri = xml_node_get_text(ctx->xml, cmd);
+ if (!uri) {
+ wpa_printf(MSG_INFO, "No URI found");
+ free(id);
+ return -1;
+ }
+ wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
+ write_summary(ctx, "Launch browser to URI '%s'", uri);
+ res = hs20_web_browser(uri);
+ xml_node_get_text_free(ctx->xml, uri);
+ if (res > 0) {
+ wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
+ id);
+ write_summary(ctx, "User response in browser completed successfully");
+ *ret_node = hs20_spp_user_input_completed(ctx, id);
+ free(id);
+ return *ret_node ? 0 : -1;
+ } else {
+ wpa_printf(MSG_INFO, "Failed to receive user response");
+ write_summary(ctx, "Failed to receive user response");
+ hs20_spp_update_response(
+ ctx, id, "Error occurred", "Other");
+ free(id);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (strcasecmp(name, "uploadMO") == 0) {
+ if (pps_fname == NULL)
+ return -1;
+ *ret_node = hs20_spp_upload_mo(ctx, cmd, id,
+ pps_fname);
+ free(id);
+ return *ret_node ? 0 : -1;
+ }
+
+ if (strcasecmp(name, "getCertificate") == 0) {
+ *ret_node = hs20_spp_get_certificate(ctx, cmd, id,
+ pps_fname);
+ free(id);
+ return *ret_node ? 0 : -1;
+ }
+
+ wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
+ free(id);
+ return -1;
+}
+
+
+enum spp_post_dev_data_use {
+ SPP_SUBSCRIPTION_REMEDIATION,
+ SPP_POLICY_UPDATE,
+ SPP_SUBSCRIPTION_REGISTRATION,
+};
+
+static void process_spp_post_dev_data_response(
+ struct hs20_osu_client *ctx,
+ enum spp_post_dev_data_use use, xml_node_t *node,
+ const char *pps_fname, xml_node_t *pps)
+{
+ xml_node_t *child;
+ char *status = NULL;
+ xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
+ char *session_id = NULL;
+
+ debug_dump_node(ctx, "sppPostDevDataResponse node", node);
+
+ status = get_spp_attr_value(ctx->xml, node, "sppStatus");
+ if (status == NULL) {
+ wpa_printf(MSG_INFO, "No sppStatus attribute");
+ goto out;
+ }
+ write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
+ status);
+
+ session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
+ if (session_id == NULL) {
+ wpa_printf(MSG_INFO, "No sessionID attribute");
+ goto out;
+ }
+
+ wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'",
+ status, session_id);
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ const char *name;
+ xml_node_for_each_check(ctx->xml, child);
+ debug_dump_node(ctx, "child", child);
+ name = xml_node_get_localname(ctx->xml, child);
+ wpa_printf(MSG_INFO, "localname: '%s'", name);
+ if (!update && strcasecmp(name, "updateNode") == 0)
+ update = child;
+ if (!exec && strcasecmp(name, "exec") == 0)
+ exec = child;
+ if (!add_mo && strcasecmp(name, "addMO") == 0)
+ add_mo = child;
+ if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
+ no_mo = child;
+ }
+
+ if (use == SPP_SUBSCRIPTION_REMEDIATION &&
+ strcasecmp(status,
+ "Remediation complete, request sppUpdateResponse") == 0)
+ {
+ int res, ret;
+ if (!update && !no_mo) {
+ wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
+ goto out;
+ }
+ wpa_printf(MSG_INFO, "Subscription remediation completed");
+ res = update_pps(ctx, update, pps_fname, pps);
+ if (res < 0)
+ wpa_printf(MSG_INFO, "Failed to update PPS MO");
+ ret = hs20_spp_update_response(
+ ctx, session_id,
+ res < 0 ? "Error occurred" : "OK",
+ res < 0 ? "MO addition or update failed" : NULL);
+ if (res == 0 && ret == 0)
+ hs20_sub_rem_complete(ctx, pps_fname);
+ goto out;
+ }
+
+ if (use == SPP_SUBSCRIPTION_REMEDIATION &&
+ strcasecmp(status, "Exchange complete, release TLS connection") ==
+ 0) {
+ if (!no_mo) {
+ wpa_printf(MSG_INFO, "No noMOUpdate element");
+ goto out;
+ }
+ wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
+ goto out;
+ }
+
+ if (use == SPP_POLICY_UPDATE &&
+ strcasecmp(status, "Update complete, request sppUpdateResponse") ==
+ 0) {
+ int res, ret;
+ wpa_printf(MSG_INFO, "Policy update received - update PPS");
+ res = update_pps(ctx, update, pps_fname, pps);
+ ret = hs20_spp_update_response(
+ ctx, session_id,
+ res < 0 ? "Error occurred" : "OK",
+ res < 0 ? "MO addition or update failed" : NULL);
+ if (res == 0 && ret == 0)
+ hs20_policy_update_complete(ctx, pps_fname);
+ goto out;
+ }
+
+ if (use == SPP_SUBSCRIPTION_REGISTRATION &&
+ strcasecmp(status, "Provisioning complete, request "
+ "sppUpdateResponse") == 0) {
+ if (!add_mo) {
+ wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
+ goto out;
+ }
+ process_spp_user_input_response(ctx, session_id, add_mo);
+ node = NULL;
+ goto out;
+ }
+
+ if (strcasecmp(status, "No update available at this time") == 0) {
+ wpa_printf(MSG_INFO, "No update available at this time");
+ goto out;
+ }
+
+ if (strcasecmp(status, "OK") == 0) {
+ int res;
+ xml_node_t *ret;
+
+ if (!exec) {
+ wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
+ goto out;
+ }
+ res = hs20_spp_exec(ctx, exec, session_id,
+ pps_fname, pps, &ret);
+ /* xml_node_free(ctx->xml, node); */
+ node = NULL;
+ if (res == 0 && ret)
+ process_spp_post_dev_data_response(ctx, use,
+ ret, pps_fname, pps);
+ goto out;
+ }
+
+ if (strcasecmp(status, "Error occurred") == 0) {
+ xml_node_t *err;
+ char *code = NULL;
+ err = get_node(ctx->xml, node, "sppError");
+ if (err)
+ code = xml_node_get_attr_value(ctx->xml, err,
+ "errorCode");
+ wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
+ code ? code : "N/A");
+ xml_node_get_attr_value_free(ctx->xml, code);
+ goto out;
+ }
+
+ wpa_printf(MSG_INFO,
+ "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
+ status);
+out:
+ xml_node_get_attr_value_free(ctx->xml, status);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ xml_node_free(ctx->xml, node);
+}
+
+
+static int spp_post_dev_data(struct hs20_osu_client *ctx,
+ enum spp_post_dev_data_use use,
+ const char *reason,
+ const char *pps_fname, xml_node_t *pps)
+{
+ xml_node_t *payload;
+ xml_node_t *ret_node;
+
+ payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
+ if (payload == NULL)
+ return -1;
+
+ ret_node = soap_send_receive(ctx->http, payload);
+ if (!ret_node) {
+ const char *err = http_get_err(ctx->http);
+ if (err) {
+ wpa_printf(MSG_INFO, "HTTP error: %s", err);
+ write_result(ctx, "HTTP error: %s", err);
+ } else {
+ write_summary(ctx, "Failed to send SOAP message");
+ }
+ return -1;
+ }
+
+ if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return -1;
+ }
+
+ process_spp_post_dev_data_response(ctx, use, ret_node,
+ pps_fname, pps);
+ return 0;
+}
+
+
+void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps)
+{
+ wpa_printf(MSG_INFO, "SPP subscription remediation");
+ write_summary(ctx, "SPP subscription remediation");
+
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(address);
+
+ if (soap_init_client(ctx->http, address, ctx->ca_fname,
+ cred_username, cred_password, client_cert,
+ client_key) == 0) {
+ spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
+ "Subscription remediation", pps_fname, pps);
+ }
+}
+
+
+static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
+ const char *pps_fname)
+{
+ wpa_printf(MSG_INFO, "Policy update completed");
+
+ /*
+ * Update wpa_supplicant credentials and reconnect using updated
+ * information.
+ */
+ wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+ cmd_set_pps(ctx, pps_fname);
+
+ wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+ if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+ wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
+ xml_node_t *node)
+{
+ char *status, *session_id;
+
+ debug_dump_node(ctx, "sppExchangeComplete", node);
+
+ status = get_spp_attr_value(ctx->xml, node, "sppStatus");
+ if (status == NULL) {
+ wpa_printf(MSG_INFO, "No sppStatus attribute");
+ return -1;
+ }
+ write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
+ status);
+
+ session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
+ if (session_id == NULL) {
+ wpa_printf(MSG_INFO, "No sessionID attribute");
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'",
+ status, session_id);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+
+ if (strcasecmp(status, "Exchange complete, release TLS connection") ==
+ 0) {
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return 0;
+ }
+
+ wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
+ write_summary(ctx, "Unexpected sppStatus '%s'", status);
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return -1;
+}
+
+
+static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
+ const char *session_id,
+ const char *spp_status,
+ const char *error_code)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppUpdateResponse");
+ if (spp_node == NULL)
+ return NULL;
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
+
+ if (error_code) {
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ if (node)
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+ const char *session_id,
+ const char *spp_status,
+ const char *error_code)
+{
+ xml_node_t *node, *ret_node;
+ int ret;
+
+ write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
+ spp_status, error_code);
+ node = build_spp_update_response(ctx, session_id, spp_status,
+ error_code);
+ if (node == NULL)
+ return -1;
+ ret_node = soap_send_receive(ctx->http, node);
+ if (!ret_node) {
+ if (soap_reinit_client(ctx->http) < 0)
+ return -1;
+ wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+ node = build_spp_update_response(ctx, session_id, spp_status,
+ error_code);
+ if (node == NULL)
+ return -1;
+ ret_node = soap_send_receive(ctx->http, node);
+ if (ret_node == NULL)
+ return -1;
+ wpa_printf(MSG_INFO, "Continue with new connection");
+ }
+
+ if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
+ wpa_printf(MSG_INFO, "SPP validation failed");
+ xml_node_free(ctx->xml, ret_node);
+ return -1;
+ }
+
+ ret = process_spp_exchange_complete(ctx, ret_node);
+ xml_node_free(ctx->xml, ret_node);
+ return ret;
+}
+
+
+void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
+ const char *pps_fname,
+ const char *client_cert, const char *client_key,
+ const char *cred_username, const char *cred_password,
+ xml_node_t *pps)
+{
+ wpa_printf(MSG_INFO, "SPP policy update");
+ write_summary(ctx, "SPP policy update");
+
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(address);
+
+ if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
+ cred_password, client_cert, client_key) == 0) {
+ spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
+ pps_fname, pps);
+ }
+}
+
+
+int cmd_prov(struct hs20_osu_client *ctx, const char *url)
+{
+ unlink("Cert/est_cert.der");
+ unlink("Cert/est_cert.pem");
+
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "Credential provisioning requested");
+
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(url);
+
+ if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
+ NULL) < 0)
+ return -1;
+ spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+ "Subscription registration", NULL, NULL);
+
+ return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
+{
+ if (url == NULL) {
+ wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "SIM provisioning requested");
+
+ os_free(ctx->server_url);
+ ctx->server_url = os_strdup(url);
+
+ wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+
+ if (wait_ip_addr(ctx->ifname, 15) < 0) {
+ wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+ }
+
+ if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
+ NULL) < 0)
+ return -1;
+ spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+ "Subscription provisioning", NULL, NULL);
+
+ return ctx->pps_cred_set ? 0 : -1;
+}
diff --git a/hs20/server/Makefile b/hs20/server/Makefile
new file mode 100644
index 0000000..587633b
--- /dev/null
+++ b/hs20/server/Makefile
@@ -0,0 +1,45 @@
+all: hs20_spp_server
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I../../src/utils
+CFLAGS += -I../../src/crypto
+
+LIBS += -lsqlite3
+
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+
+OBJS=spp_server.o
+OBJS += hs20_spp_server.o
+OBJS += ../../src/utils/xml-utils.o
+OBJS += ../../src/utils/base64.o
+OBJS += ../../src/utils/common.o
+OBJS += ../../src/utils/os_unix.o
+OBJS += ../../src/utils/wpa_debug.o
+OBJS += ../../src/crypto/md5-internal.o
+CFLAGS += $(shell xml2-config --cflags)
+LIBS += $(shell xml2-config --libs)
+OBJS += ../../src/utils/xml_libxml2.o
+
+hs20_spp_server: $(OBJS)
+ $(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
+
+clean:
+ rm -f core *~ *.o *.d hs20_spp_server
+ rm -f ../../src/utils/*.o
+ rm -f ../../src/utils/*.d
+ rm -f ../../src/crypto/*.o
+ rm -f ../../src/crypto/*.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hs20/server/ca/clean.sh b/hs20/server/ca/clean.sh
new file mode 100644
index 0000000..c69a1f5
--- /dev/null
+++ b/hs20/server/ca/clean.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+for i in server-client server server-revoked user ocsp; do
+ rm -f $i.csr $i.key $i.pem
+done
+
+rm -f openssl.cnf.tmp
+rm -r demoCA
+rm -f ca.pem logo.asn1 logo.der server.der ocsp-server-cache.der
+#rm -r rootCA
diff --git a/hs20/server/ca/est-csrattrs.cnf b/hs20/server/ca/est-csrattrs.cnf
new file mode 100644
index 0000000..b50ea00
--- /dev/null
+++ b/hs20/server/ca/est-csrattrs.cnf
@@ -0,0 +1,17 @@
+asn1 = SEQUENCE:attrs
+
+[attrs]
+#oid1 = OID:challengePassword
+attr1 = SEQUENCE:extreq
+oid2 = OID:sha256WithRSAEncryption
+
+[extreq]
+oid = OID:extensionRequest
+vals = SET:extreqvals
+
+[extreqvals]
+
+oid1 = OID:macAddress
+#oid2 = OID:imei
+#oid3 = OID:meid
+#oid4 = OID:DevId
diff --git a/hs20/server/ca/est-csrattrs.sh b/hs20/server/ca/est-csrattrs.sh
new file mode 100644
index 0000000..0b73a04
--- /dev/null
+++ b/hs20/server/ca/est-csrattrs.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+openssl asn1parse -genconf est-csrattrs.cnf -out est-csrattrs.der -oid hs20.oid
+base64 est-csrattrs.der > est-attrs.b64
diff --git a/hs20/server/ca/hs20.oid b/hs20/server/ca/hs20.oid
new file mode 100644
index 0000000..a829ff2
--- /dev/null
+++ b/hs20/server/ca/hs20.oid
@@ -0,0 +1,7 @@
+1.3.6.1.1.1.1.22 macAddress
+1.2.840.113549.1.9.14 extensionRequest
+1.3.6.1.4.1.40808.1.1.1 id-wfa-hotspot-friendlyName
+1.3.6.1.4.1.40808.1.1.2 id-kp-HS2.0Auth
+1.3.6.1.4.1.40808.1.1.3 imei
+1.3.6.1.4.1.40808.1.1.4 meid
+1.3.6.1.4.1.40808.1.1.5 DevId
diff --git a/hs20/server/ca/ocsp-req.sh b/hs20/server/ca/ocsp-req.sh
new file mode 100644
index 0000000..931a206
--- /dev/null
+++ b/hs20/server/ca/ocsp-req.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+for i in *.pem; do
+ echo "===[ $i ]==================="
+ openssl ocsp -text -CAfile ca.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+
+# openssl ocsp -text -CAfile rootCA/cacert.pem -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+
+# openssl ocsp -text -CAfile rootCA/cacert.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+# openssl ocsp -text -CAfile rootCA/cacert.pem -VAfile ca.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+done
diff --git a/hs20/server/ca/ocsp-responder-ica.sh b/hs20/server/ca/ocsp-responder-ica.sh
new file mode 100644
index 0000000..116c6e1
--- /dev/null
+++ b/hs20/server/ca/ocsp-responder-ica.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner demoCA/cacert.pem -rkey demoCA/private/cakey-plain.pem -CA demoCA/cacert.pem -resp_no_certs -text
diff --git a/hs20/server/ca/ocsp-responder.sh b/hs20/server/ca/ocsp-responder.sh
new file mode 100644
index 0000000..8cebd74
--- /dev/null
+++ b/hs20/server/ca/ocsp-responder.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner ocsp.pem -rkey ocsp.key -CA demoCA/cacert.pem -text
diff --git a/hs20/server/ca/ocsp-update-cache.sh b/hs20/server/ca/ocsp-update-cache.sh
new file mode 100644
index 0000000..8ddef9b
--- /dev/null
+++ b/hs20/server/ca/ocsp-update-cache.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+openssl ocsp \
+ -no_nonce \
+ -CAfile ca.pem \
+ -verify_other demoCA/cacert.pem \
+ -issuer demoCA/cacert.pem \
+ -cert server.pem \
+ -url http://localhost:8888/ \
+ -respout ocsp-server-cache.der
diff --git a/hs20/server/ca/openssl-root.cnf b/hs20/server/ca/openssl-root.cnf
new file mode 100644
index 0000000..5b220fe
--- /dev/null
+++ b/hs20/server/ca/openssl-root.cnf
@@ -0,0 +1,125 @@
+# OpenSSL configuration file for Hotspot 2.0 PKI (Root CA)
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+oid_section = new_oids
+
+[ new_oids ]
+
+#logotypeoid=1.3.6.1.5.5.7.1.12
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./rootCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several certificates with same subject
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = optional
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+input_password = whatever
+output_password = whatever
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = US
+countryName_min = 2
+countryName_max = 2
+
+localityName = Locality Name (eg, city)
+localityName_default = Tuusula
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = WFA Hotspot 2.0
+
+##organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+#@OU@
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+[ req_attributes ]
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName=DNS:example.com,DNS:another.example.com
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+basicConstraints = critical,CA:true
+keyUsage = critical, cRLSign, keyCertSign
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
diff --git a/hs20/server/ca/openssl.cnf b/hs20/server/ca/openssl.cnf
new file mode 100644
index 0000000..a939f08
--- /dev/null
+++ b/hs20/server/ca/openssl.cnf
@@ -0,0 +1,200 @@
+# OpenSSL configuration file for Hotspot 2.0 PKI (Intermediate CA)
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+oid_section = new_oids
+
+[ new_oids ]
+
+#logotypeoid=1.3.6.1.5.5.7.1.12
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./demoCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several certificates with same subject
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = ext_client # The extentions to add to the cert
+
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+copy_extensions = copy
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = supplied
+stateOrProvinceName = optional
+organizationName = supplied
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_osu_server ]
+countryName = match
+stateOrProvinceName = optional
+organizationName = match
+organizationalUnitName = supplied
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+input_password = whatever
+output_password = whatever
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = FI
+countryName_min = 2
+countryName_max = 2
+
+localityName = Locality Name (eg, city)
+localityName_default = Tuusula
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = w1.fi
+
+##organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+#@OU@
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+[ req_attributes ]
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, cRLSign, keyCertSign
+authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+# For SP intermediate CA
+#subjectAltName=critical,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engExample OSU
+#nameConstraints=permitted;DNS:.w1.fi
+#1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+
+[ v3_osu_server ]
+
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, keyEncipherment
+#@ALTNAME@
+
+#logotypeoid=ASN1:SEQUENCE:LogotypeExtn
+1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+[LogotypeExtn]
+communityLogos=EXP:0,SEQUENCE:LogotypeInfo
+[LogotypeInfo]
+# note: implicit tag converted to explicit for CHOICE
+direct=EXP:0,SEQUENCE:LogotypeData
+[LogotypeData]
+image=SEQUENCE:LogotypeImage
+[LogotypeImage]
+imageDetails=SEQUENCE:LogotypeDetails
+imageInfo=SEQUENCE:LogotypeImageInfo
+[LogotypeDetails]
+mediaType=IA5STRING:image/png
+logotypeHash=SEQUENCE:HashAlgAndValues
+logotypeURI=SEQUENCE:URI
+[HashAlgAndValues]
+value1=SEQUENCE:HashAlgAndValueSHA256
+#value2=SEQUENCE:HashAlgAndValueSHA1
+[HashAlgAndValueSHA256]
+hashAlg=SEQUENCE:sha256_alg
+hashValue=FORMAT:HEX,OCTETSTRING:4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d
+[HashAlgAndValueSHA1]
+hashAlg=SEQUENCE:sha1_alg
+hashValue=FORMAT:HEX,OCTETSTRING:5e1d5085676eede6b02da14d31c523ec20ffba0b
+[sha256_alg]
+algorithm=OID:sha256
+[sha1_alg]
+algorithm=OID:sha1
+[URI]
+uri=IA5STRING:http://osu.w1.fi/w1fi_logo.png
+[LogotypeImageInfo]
+# default value color(1), component optional
+#type=IMP:0,INTEGER:1
+fileSize=INTEGER:7549
+xSize=INTEGER:128
+ySize=INTEGER:80
+language=IMP:4,IA5STRING:zxx
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
+
+[ ext_client ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+#@ALTNAME@
+extendedKeyUsage = clientAuth
+
+[ ext_server ]
+
+# Hotspot 2.0 PKI requirements
+basicConstraints=critical, CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+#@ALTNAME@
+extendedKeyUsage = critical, serverAuth
+keyUsage = critical, keyEncipherment
diff --git a/hs20/server/ca/setup.sh b/hs20/server/ca/setup.sh
new file mode 100644
index 0000000..f61bf73
--- /dev/null
+++ b/hs20/server/ca/setup.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+
+if [ -z "$OPENSSL" ]; then
+ OPENSSL=openssl
+fi
+export OPENSSL_CONF=$PWD/openssl.cnf
+PASS=whatever
+
+fail()
+{
+ echo "$*"
+ exit 1
+}
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+cat openssl-root.cnf | sed "s/#@CN@/commonName_default = Hotspot 2.0 Trust Root CA - 99/" > openssl.cnf.tmp
+mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
+touch rootCA/index.txt
+if [ -e rootCA/private/cakey.pem ]; then
+ echo " * Use existing Root CA"
+else
+ echo " * Generate Root CA private key"
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
+ echo " * Sign Root CA certificate"
+ $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
+fi
+if [ ! -e rootCA/crlnumber ]; then
+ echo 00 > rootCA/crlnumber
+fi
+
+echo
+echo "---[ Intermediate CA ]--------------------------------------------------"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = w1.fi Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
+mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
+touch demoCA/index.txt
+if [ -e demoCA/private/cakey.pem ]; then
+ echo " * Use existing Intermediate CA"
+else
+ echo " * Generate Intermediate CA private key"
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -keyout demoCA/private/cakey.pem -out demoCA/careq.pem || fail "Failed to generate Intermediate CA private key"
+ echo " * Sign Intermediate CA certificate"
+ $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
+ # horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
+ openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
+fi
+if [ ! -e demoCA/crlnumber ]; then
+ echo 00 > demoCA/crlnumber
+fi
+
+echo
+echo "OCSP responder"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = ocsp.w1.fi/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP
+
+echo
+echo "---[ Server - to be revoked ] ------------------------------------------"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = osu-revoked.w1.fi/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
+$OPENSSL ca -revoke server-revoked.pem -key $PASS
+
+echo
+echo "---[ Server - with client ext key use ] ---------------------------------"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = osu-client.w1.fi/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+ALT="DNS:osu.w1.fi"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engw1.fi TESTING USE"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:finw1.fi TESTIKÄYTTÖ"
+
+cat openssl.cnf |
+ sed "s/#@CN@/commonName_default = osu.w1.fi/" |
+ sed "s/^##organizationalUnitName/organizationalUnitName/" |
+ sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
+ > openssl.cnf.tmp
+echo $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server || fail "Failed to generate server request"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server.csr -out server.pem -key $PASS -days 730 -extensions ext_server -policy policy_osu_server || fail "Failed to sign server certificate"
+
+#dump logotype details for debugging
+$OPENSSL x509 -in server.pem -out server.der -outform DER
+openssl asn1parse -in server.der -inform DER | grep HEX | tail -1 | sed 's/.*://' | xxd -r -p > logo.der
+openssl asn1parse -in logo.der -inform DER > logo.asn1
+
+
+echo
+echo "---[ CRL ]---------------------------------------------------------------"
+echo
+
+$OPENSSL ca -config $PWD/openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
+
+echo
+echo "---[ Verify ]------------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile rootCA/cacert.pem demoCA/cacert.pem
+$OPENSSL verify -CAfile rootCA/cacert.pem -untrusted demoCA/cacert.pem *.pem
+
+cat rootCA/cacert.pem demoCA/cacert.pem > ca.pem
diff --git a/hs20/server/ca/w1fi_logo.png b/hs20/server/ca/w1fi_logo.png
new file mode 100644
index 0000000..ac7c259
--- /dev/null
+++ b/hs20/server/ca/w1fi_logo.png
Binary files differ
diff --git a/hs20/server/hs20-osu-server.txt b/hs20/server/hs20-osu-server.txt
new file mode 100644
index 0000000..80985f7
--- /dev/null
+++ b/hs20/server/hs20-osu-server.txt
@@ -0,0 +1,196 @@
+Hotspot 2.0 OSU server
+======================
+
+The information in this document is based on the assumption that Ubuntu
+12.04 server (64-bit) distribution is used and the web server is
+Apache2. Neither of these are requirements for the installation, but if
+other combinations are used, the package names and configuration
+parameters may need to be adjusted.
+
+NOTE: This implementation and the example configuration here is meant
+only for testing purposes in a lab environment. This design is not
+secure to be installed in a publicly available Internet server without
+considerable amount of modification and review for security issues.
+
+NOTE: While this describes use on Ubuntu 12.04, the version of Apache2
+included in that distribution is not new enough to support all OSU
+server validation steps. In other words, it may be most adapt the steps
+described here to Ubuntu 13.10.
+
+
+Build dependencies
+------------------
+
+Ubuntu 12.04 server
+- default installation
+- upgraded to latest package versions
+ sudo apt-get update
+ sudo apt-get upgrade
+
+Packages needed for running the service:
+ sudo apt-get install sqlite3
+ sudo apt-get install apache2
+ sudo apt-get install php5-sqlite libapache2-mod-php5
+
+Additional packages needed for building the components:
+ sudo apt-get install build-essential
+ sudo apt-get install libsqlite3-dev
+ sudo apt-get install libssl-dev
+ sudo apt-get install libxml2-dev
+
+
+Installation location
+---------------------
+
+Select a location for the installation root directory. The example here
+assumes /home/user/hs20-server to be used, but this can be changed by
+editing couple of files as indicated below.
+
+sudo mkdir -p /home/user/hs20-server
+sudo chown $USER /home/user/hs20-server
+mkdir -p /home/user/hs20-server/spp
+mkdir -p /home/user/hs20-server/AS
+
+
+Build
+-----
+
+# hostapd as RADIUS server
+cd hostapd
+
+#example build configuration
+cat > .config <<EOF
+CONFIG_DRIVER_NONE=y
+CONFIG_PKCS12=y
+CONFIG_RADIUS_SERVER=y
+CONFIG_EAP=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_AKA_PRIME=y
+CONFIG_SQLITE=y
+CONFIG_HS20=y
+EOF
+
+make hostapd hlr_auc_gw
+cp hostapd hlr_auc_gw /home/user/hs20-server/AS
+
+# build hs20_spp_server
+cd ../hs20/server
+make clean
+make
+cp hs20_spp_server /home/user/hs20-server/spp
+# prepare database (web server user/group needs to have write access)
+mkdir -p /home/user/hs20-server/AS/DB
+sudo chgrp www-data /home/user/hs20-server/AS/DB
+sudo chmod g+w /home/user/hs20-server/AS/DB
+sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql.txt
+sudo chgrp www-data /home/user/hs20-server/AS/DB/eap_user.db
+sudo chmod g+w /home/user/hs20-server/AS/DB/eap_user.db
+# add example configuration (note: need to update URLs to match the system)
+sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql-example.txt
+
+# copy PHP scripts
+# Modify config.php if different installation directory is used.
+# Modify PHP scripts to get the desired behavior for user interaction (or use
+# the examples as-is for initial testing).
+cp -r www /home/user/hs20-server
+
+
+# Configure subscription policies
+mkdir -p /home/user/hs20-server/spp/policy
+cat > /home/user/hs20-server/spp/policy/default.xml <<EOF
+<Policy>
+ <PolicyUpdate>
+ <UpdateInterval>30</UpdateInterval>
+ <UpdateMethod>ClientInitiated</UpdateMethod>
+ <Restriction>Unrestricted</Restriction>
+ <URI>https://policy-server.osu.example.com/hs20/spp.php</URI>
+ </PolicyUpdate>
+</Policy>
+EOF
+
+
+# Install Hotspot 2.0 SPP and OMA DM XML schema/DTD files
+
+# XML schema for SPP
+# Copy the latest XML schema into /home/user/hs20-server/spp/spp.xsd
+
+# OMA DM Device Description Framework DTD
+# Copy into /home/user/hs20-server/spp/dm_ddf-v1_2.dtd
+# http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd
+
+
+# Configure RADIUS authentication service
+# Note: Change the URL to match the setup
+# Note: Install AAA server key/certificate and root CA in Key directory
+
+cat > /home/user/hs20-server/AS/as-sql.conf <<EOF
+driver=none
+radius_server_clients=as.radius_clients
+eap_server=1
+eap_user_file=sqlite:DB/eap_user.db
+ca_cert=Key/ca.pem
+server_cert=Key/server.pem
+private_key=Key/server.key
+private_key_passwd=passphrase
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=eap_sim.db
+subscr_remediation_url=https://subscription-server.osu.example.com/hs20/spp.php
+EOF
+
+# Set RADIUS passphrase for the APs
+# Note: Modify to match the setup
+cat > /home/user/hs20-server/AS/as.radius_clients <<EOF
+0.0.0.0/0 radius
+EOF
+
+
+Start RADIUS authentication server
+----------------------------------
+
+cd /home/user/hs20-server/AS
+./hostapd -B as-sql.conf
+
+
+Configure web server
+--------------------
+
+Edit /etc/apache2/sites-available/default-ssl
+
+Add following block just before "SSL Engine Switch" line":
+
+ Alias /hs20/ "/home/user/hs20-server/www/"
+ <Directory "/home/user/hs20-server/www/">
+ Options Indexes MultiViews FollowSymLinks
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+ </Directory>
+
+Update SSL configuration to use the OSU server certificate/key.
+
+Enable default-ssl site and restart Apache2:
+ sudo a2ensite default-ssl
+ sudo a2enmod ssl
+ sudo service apache2 restart
+
+
+Management UI
+-------------
+
+The sample PHP scripts include a management UI for testing
+purposes. That is available at https://<server>/hs20/users.php
+
+
+AP configuration
+----------------
+
+APs can now be configured to use the OSU server as the RADIUS
+authentication server. In addition, the OSU Provider List ANQP element
+should be configured to use the SPP (SOAP+XML) option and with the
+following Server URL:
+https://<server>/hs20/spp.php/signup?realm=example.com
diff --git a/hs20/server/hs20_spp_server.c b/hs20/server/hs20_spp_server.c
new file mode 100644
index 0000000..591f66b
--- /dev/null
+++ b/hs20/server/hs20_spp_server.c
@@ -0,0 +1,187 @@
+/*
+ * Hotspot 2.0 SPP server - standalone version
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <sqlite3.h>
+
+#include "common.h"
+#include "xml-utils.h"
+#include "spp_server.h"
+
+
+static void write_timestamp(FILE *f)
+{
+ time_t t;
+ struct tm *tm;
+
+ time(&t);
+ tm = localtime(&t);
+
+ fprintf(f, "%04u-%02u-%02u %02u:%02u:%02u ",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+
+void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (ctx->debug_log == NULL)
+ return;
+
+ write_timestamp(ctx->debug_log);
+ va_start(ap, fmt);
+ vfprintf(ctx->debug_log, fmt, ap);
+ va_end(ap);
+
+ fprintf(ctx->debug_log, "\n");
+}
+
+
+void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node)
+{
+ char *str;
+
+ if (ctx->debug_log == NULL)
+ return;
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+
+ write_timestamp(ctx->debug_log);
+ fprintf(ctx->debug_log, "%s: '%s'\n", title, str);
+ os_free(str);
+}
+
+
+static int process(struct hs20_svc *ctx)
+{
+ int dmacc = 0;
+ xml_node_t *soap, *spp, *resp;
+ char *user, *realm, *post, *str;
+
+ ctx->addr = getenv("HS20ADDR");
+ if (ctx->addr)
+ debug_print(ctx, 1, "Connection from %s", ctx->addr);
+
+ user = getenv("HS20USER");
+ if (user && strlen(user) == 0)
+ user = NULL;
+ realm = getenv("HS20REALM");
+ if (realm == NULL) {
+ debug_print(ctx, 1, "HS20REALM not set");
+ return -1;
+ }
+ post = getenv("HS20POST");
+ if (post == NULL) {
+ debug_print(ctx, 1, "HS20POST not set");
+ return -1;
+ }
+
+ soap = xml_node_from_buf(ctx->xml, post);
+ if (soap == NULL) {
+ debug_print(ctx, 1, "Could not parse SOAP data");
+ return -1;
+ }
+ debug_dump_node(ctx, "Received SOAP message", soap);
+ spp = soap_get_body(ctx->xml, soap);
+ if (spp == NULL) {
+ debug_print(ctx, 1, "Could not get SPP message");
+ xml_node_free(ctx->xml, soap);
+ return -1;
+ }
+ debug_dump_node(ctx, "Received SPP message", spp);
+
+ resp = hs20_spp_server_process(ctx, spp, user, realm, dmacc);
+ xml_node_free(ctx->xml, soap);
+ if (resp == NULL && user == NULL) {
+ debug_print(ctx, 1, "Request HTTP authentication");
+ return 2; /* Request authentication */
+ }
+ if (resp == NULL) {
+ debug_print(ctx, 1, "No response");
+ return -1;
+ }
+
+ soap = soap_build_envelope(ctx->xml, resp);
+ if (soap == NULL) {
+ debug_print(ctx, 1, "SOAP envelope building failed");
+ return -1;
+ }
+ str = xml_node_to_str(ctx->xml, soap);
+ xml_node_free(ctx->xml, soap);
+ if (str == NULL) {
+ debug_print(ctx, 1, "Could not get node string");
+ return -1;
+ }
+ printf("%s", str);
+ free(str);
+
+ return 0;
+}
+
+
+static void usage(void)
+{
+ printf("usage:\n"
+ "hs20_spp_server -r<root directory> [-f<debug log>]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct hs20_svc ctx;
+ int ret;
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ for (;;) {
+ int c = getopt(argc, argv, "f:r:");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'f':
+ if (ctx.debug_log)
+ break;
+ ctx.debug_log = fopen(optarg, "a");
+ if (ctx.debug_log == NULL) {
+ printf("Could not write to %s\n", optarg);
+ return -1;
+ }
+ break;
+ case 'r':
+ ctx.root_dir = optarg;
+ break;
+ default:
+ usage();
+ return -1;
+ }
+ }
+ if (ctx.root_dir == NULL) {
+ usage();
+ return -1;
+ }
+ ctx.xml = xml_node_init_ctx(&ctx, NULL);
+ if (ctx.xml == NULL)
+ return -1;
+ if (hs20_spp_server_init(&ctx) < 0) {
+ xml_node_deinit_ctx(ctx.xml);
+ return -1;
+ }
+
+ ret = process(&ctx);
+ debug_print(&ctx, 1, "process() --> %d", ret);
+
+ xml_node_deinit_ctx(ctx.xml);
+ hs20_spp_server_deinit(&ctx);
+ if (ctx.debug_log)
+ fclose(ctx.debug_log);
+
+ return ret;
+}
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
new file mode 100644
index 0000000..4d77d0e
--- /dev/null
+++ b/hs20/server/spp_server.c
@@ -0,0 +1,2263 @@
+/*
+ * Hotspot 2.0 SPP server
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <sqlite3.h>
+
+#include "common.h"
+#include "base64.h"
+#include "md5_i.h"
+#include "xml-utils.h"
+#include "spp_server.h"
+
+
+#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
+
+#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
+#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
+#define URN_OMA_DM_DMACC "urn:oma:mo:oma-dm-dmacc:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+/* TODO: timeout to expire sessions */
+
+enum hs20_session_operation {
+ NO_OPERATION,
+ UPDATE_PASSWORD,
+ CONTINUE_SUBSCRIPTION_REMEDIATION,
+ CONTINUE_POLICY_UPDATE,
+ USER_REMEDIATION,
+ SUBSCRIPTION_REGISTRATION,
+ POLICY_REMEDIATION,
+ POLICY_UPDATE,
+ FREE_REMEDIATION,
+};
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *field);
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+ const char *field);
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+ const char *realm, int use_dmacc);
+
+
+static int db_add_session(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *pw,
+ const char *redirect_uri,
+ enum hs20_session_operation operation)
+{
+ char *sql;
+ int ret = 0;
+
+ sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
+ "operation,password,redirect_uri) "
+ "VALUES "
+ "(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+ "%Q,%Q,%Q,%d,%Q,%Q)",
+ sessionid, user ? user : "", realm ? realm : "",
+ operation, pw ? pw : "",
+ redirect_uri ? redirect_uri : "");
+ if (sql == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session entry into sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ ret = -1;
+ }
+ sqlite3_free(sql);
+ return ret;
+}
+
+
+static void db_update_session_password(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *sessionid,
+ const char *pw)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET password=%Q WHERE id=%Q AND "
+ "user=%Q AND realm=%Q",
+ pw, sessionid, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update session password: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET pps=%Q WHERE id=%Q AND "
+ "user=%Q AND realm=%Q",
+ str, sessionid, user, realm);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session pps: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_devinfo(struct hs20_svc *ctx, const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET devinfo=%Q WHERE id=%Q",
+ str, sessionid);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session devinfo: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_devdetail(struct hs20_svc *ctx,
+ const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET devdetail=%Q WHERE id=%Q",
+ str, sessionid);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session devdetail: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_remove_session(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid)
+{
+ char *sql;
+
+ if (user == NULL || realm == NULL) {
+ sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+ "id=%Q", sessionid);
+ } else {
+ sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+ "user=%Q AND realm=%Q AND id=%Q",
+ user, realm, sessionid);
+ }
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to delete session entry from "
+ "sqlite database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *notes,
+ const char *dump)
+{
+ char *sql;
+ char *user_buf = NULL, *realm_buf = NULL;
+
+ debug_print(ctx, 1, "eventlog: %s", notes);
+
+ if (user == NULL) {
+ user_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+ "user");
+ user = user_buf;
+ realm_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+ "realm");
+ realm = realm_buf;
+ }
+
+ sql = sqlite3_mprintf("INSERT INTO eventlog"
+ "(user,realm,sessionid,timestamp,notes,dump,addr)"
+ " VALUES (%Q,%Q,%Q,"
+ "strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+ "%Q,%Q,%Q)",
+ user, realm, sessionid, notes,
+ dump ? dump : "", ctx->addr ? ctx->addr : "");
+ free(user_buf);
+ free(realm_buf);
+ if (sql == NULL)
+ return;
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add eventlog entry into sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog_node(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *notes,
+ xml_node_t *node)
+{
+ char *str;
+
+ if (node)
+ str = xml_node_to_str(ctx->xml, node);
+ else
+ str = NULL;
+ hs20_eventlog(ctx, user, realm, sessionid, notes, str);
+ free(str);
+}
+
+
+static void db_update_mo_str(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *name,
+ const char *str)
+{
+ char *sql;
+ if (user == NULL || realm == NULL || name == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE users SET %s=%Q "
+ "WHERE identity=%Q AND realm=%Q AND phase2=1",
+ name, str, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update user MO entry in sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_update_mo(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *name, xml_node_t *mo)
+{
+ char *str;
+
+ str = xml_node_to_str(ctx->xml, mo);
+ if (str == NULL)
+ return;
+
+ db_update_mo_str(ctx, user, realm, name, str);
+ free(str);
+}
+
+
+static void add_text_node(struct hs20_svc *ctx, xml_node_t *parent,
+ const char *name, const char *value)
+{
+ xml_node_create_text(ctx->xml, parent, NULL, name, value ? value : "");
+}
+
+
+static void add_text_node_conf(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *parent, const char *name,
+ const char *field)
+{
+ char *val;
+ val = db_get_osu_config_val(ctx, realm, field);
+ xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+ os_free(val);
+}
+
+
+static int new_password(char *buf, int buflen)
+{
+ int i;
+
+ if (buflen < 1)
+ return -1;
+ buf[buflen - 1] = '\0';
+ if (os_get_random((unsigned char *) buf, buflen - 1) < 0)
+ return -1;
+
+ for (i = 0; i < buflen - 1; i++) {
+ unsigned char val = buf[i];
+ val %= 2 * 26 + 10;
+ if (val < 26)
+ buf[i] = 'a' + val;
+ else if (val < 2 * 26)
+ buf[i] = 'A' + val - 26;
+ else
+ buf[i] = '0' + val - 2 * 26;
+ }
+
+ return 0;
+}
+
+
+struct get_db_field_data {
+ const char *field;
+ char *value;
+};
+
+
+static int get_db_field(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct get_db_field_data *data = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], data->field) == 0 && argv[i]) {
+ os_free(data->value);
+ data->value = os_strdup(argv[i]);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static char * db_get_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *field, int dmacc)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ cmd = sqlite3_mprintf("SELECT %s FROM users WHERE "
+ "%s=%Q AND realm=%Q AND phase2=1",
+ field, dmacc ? "osu_user" : "identity",
+ user, realm);
+ if (cmd == NULL)
+ return NULL;
+ memset(&data, 0, sizeof(data));
+ data.field = field;
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "Could not find user '%s'", user);
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: user='%s' realm='%s' field='%s' dmacc=%d --> "
+ "value='%s'", user, realm, field, dmacc, data.value);
+
+ return data.value;
+}
+
+
+static int db_update_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *field,
+ const char *val, int dmacc)
+{
+ char *cmd;
+ int ret;
+
+ cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE "
+ "%s=%Q AND realm=%Q AND phase2=1",
+ field, val, dmacc ? "osu_user" : "identity", user,
+ realm);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to update user in sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ ret = -1;
+ } else {
+ debug_print(ctx, 1,
+ "DB: user='%s' realm='%s' field='%s' set to '%s'",
+ user, realm, field, val);
+ ret = 0;
+ }
+ sqlite3_free(cmd);
+
+ return ret;
+}
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *field)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ if (user == NULL || realm == NULL) {
+ cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+ "id=%Q", field, session_id);
+ } else {
+ cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+ "user=%Q AND realm=%Q AND id=%Q",
+ field, user, realm, session_id);
+ }
+ if (cmd == NULL)
+ return NULL;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ memset(&data, 0, sizeof(data));
+ data.field = field;
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "DB: Could not find session %s: %s",
+ session_id, sqlite3_errmsg(ctx->db));
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: return '%s'", data.value);
+ return data.value;
+}
+
+
+static int update_password(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *pw, int dmacc)
+{
+ char *cmd;
+
+ cmd = sqlite3_mprintf("UPDATE users SET password=%Q, "
+ "remediation='' "
+ "WHERE %s=%Q AND phase2=1",
+ pw, dmacc ? "osu_user" : "identity",
+ user);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update database for user '%s'",
+ user);
+ }
+ sqlite3_free(cmd);
+
+ return 0;
+}
+
+
+static int add_eap_ttls(struct hs20_svc *ctx, xml_node_t *parent)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "EAPMethod");
+ if (node == NULL)
+ return -1;
+
+ add_text_node(ctx, node, "EAPType", "21");
+ add_text_node(ctx, node, "InnerMethod", "MS-CHAP-V2");
+
+ return 0;
+}
+
+
+static xml_node_t * build_username_password(struct hs20_svc *ctx,
+ xml_node_t *parent,
+ const char *user, const char *pw)
+{
+ xml_node_t *node;
+ char *b64;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword");
+ if (node == NULL)
+ return NULL;
+
+ add_text_node(ctx, node, "Username", user);
+
+ b64 = (char *) base64_encode((unsigned char *) pw, strlen(pw), NULL);
+ if (b64 == NULL)
+ return NULL;
+ add_text_node(ctx, node, "Password", b64);
+ free(b64);
+
+ return node;
+}
+
+
+static int add_username_password(struct hs20_svc *ctx, xml_node_t *cred,
+ const char *user, const char *pw)
+{
+ xml_node_t *node;
+
+ node = build_username_password(ctx, cred, user, pw);
+ if (node == NULL)
+ return -1;
+
+ add_text_node(ctx, node, "MachineManaged", "TRUE");
+ add_text_node(ctx, node, "SoftTokenApp", "");
+ add_eap_ttls(ctx, node);
+
+ return 0;
+}
+
+
+static void add_creation_date(struct hs20_svc *ctx, xml_node_t *cred)
+{
+ char str[30];
+ time_t now;
+ struct tm tm;
+
+ time(&now);
+ gmtime_r(&now, &tm);
+ snprintf(str, sizeof(str), "%04u-%02u-%02uT%02u:%02u:%02uZ",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ xml_node_create_text(ctx->xml, cred, NULL, "CreationDate", str);
+}
+
+
+static xml_node_t * build_credential_pw(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *pw)
+{
+ xml_node_t *cred;
+
+ cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+ if (cred == NULL) {
+ debug_print(ctx, 1, "Failed to create Credential node");
+ return NULL;
+ }
+ add_creation_date(ctx, cred);
+ if (add_username_password(ctx, cred, user, pw) < 0) {
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return cred;
+}
+
+
+static xml_node_t * build_credential(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ char *new_pw, size_t new_pw_len)
+{
+ if (new_password(new_pw, new_pw_len) < 0)
+ return NULL;
+ debug_print(ctx, 1, "Update password to '%s'", new_pw);
+ return build_credential_pw(ctx, user, realm, new_pw);
+}
+
+
+static xml_node_t * build_credential_cert(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *cert_fingerprint)
+{
+ xml_node_t *cred, *cert;
+
+ cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+ if (cred == NULL) {
+ debug_print(ctx, 1, "Failed to create Credential node");
+ return NULL;
+ }
+ add_creation_date(ctx, cred);
+ cert = xml_node_create(ctx->xml, cred, NULL, "DigitalCertificate");
+ add_text_node(ctx, cert, "CertificateType", "x509v3");
+ add_text_node(ctx, cert, "CertSHA256Fingerprint", cert_fingerprint);
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return cred;
+}
+
+
+static xml_node_t * build_post_dev_data_response(struct hs20_svc *ctx,
+ xml_namespace_t **ret_ns,
+ const char *session_id,
+ const char *status,
+ const char *error_code)
+{
+ xml_node_t *spp_node = NULL;
+ xml_namespace_t *ns;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppPostDevDataResponse");
+ if (spp_node == NULL)
+ return NULL;
+ if (ret_ns)
+ *ret_ns = ns;
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+ if (error_code) {
+ xml_node_t *node;
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ if (node)
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int add_update_node(struct hs20_svc *ctx, xml_node_t *spp_node,
+ xml_namespace_t *ns, const char *uri,
+ xml_node_t *upd_node)
+{
+ xml_node_t *node, *tnds;
+ char *str;
+
+ tnds = mo_to_tnds(ctx->xml, upd_node, 0, NULL, NULL);
+ if (!tnds)
+ return -1;
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL)
+ return -1;
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "updateNode", str);
+ free(str);
+
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", uri);
+
+ return 0;
+}
+
+
+static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id,
+ int machine_rem, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *cred;
+ char buf[400];
+ char new_pw[33];
+ char *real_user = NULL;
+ char *status;
+ char *cert;
+
+ if (dmacc) {
+ real_user = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (real_user == NULL) {
+ debug_print(ctx, 1, "Could not find user identity for "
+ "dmacc user '%s'", user);
+ return NULL;
+ }
+ }
+
+ cert = db_get_val(ctx, user, realm, "cert", dmacc);
+ if (cert && cert[0] == '\0')
+ cert = NULL;
+ if (cert) {
+ cred = build_credential_cert(ctx, real_user ? real_user : user,
+ realm, cert);
+ } else {
+ cred = build_credential(ctx, real_user ? real_user : user,
+ realm, new_pw, sizeof(new_pw));
+ }
+ free(real_user);
+ if (!cred) {
+ debug_print(ctx, 1, "Could not build credential");
+ return NULL;
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL) {
+ debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
+ return NULL;
+ }
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Credential1/Credential",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+ debug_print(ctx, 1, "Could not add update node");
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ machine_rem ? "machine remediation" :
+ "user remediation", cred);
+ xml_node_free(ctx->xml, cred);
+
+ if (cert) {
+ debug_print(ctx, 1, "Certificate credential - no need for DB "
+ "password update on success notification");
+ } else {
+ debug_print(ctx, 1, "Request DB password update on success "
+ "notification");
+ db_add_session(ctx, user, realm, session_id, new_pw, NULL,
+ UPDATE_PASSWORD);
+ }
+
+ return spp_node;
+}
+
+
+static xml_node_t * machine_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id, int dmacc)
+{
+ return build_sub_rem_resp(ctx, user, realm, session_id, 1, dmacc);
+}
+
+
+static xml_node_t * policy_remediation(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *policy;
+ char buf[400];
+ const char *status;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires policy remediation", NULL);
+
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ POLICY_REMEDIATION);
+
+ policy = build_policy(ctx, user, realm, dmacc);
+ if (!policy) {
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "No update available at this time", NULL);
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Credential1/Policy",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "policy update (sub rem)", policy);
+ xml_node_free(ctx->xml, policy);
+
+ return spp_node;
+}
+
+
+static xml_node_t * browser_remediation(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *redirect_uri,
+ const char *uri)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *exec_node;
+
+ if (redirect_uri == NULL) {
+ debug_print(ctx, 1, "Missing redirectURI attribute for user "
+ "remediation");
+ return NULL;
+ }
+ debug_print(ctx, 1, "redirectURI %s", redirect_uri);
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+ xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+ uri);
+ return spp_node;
+}
+
+
+static xml_node_t * user_remediation(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *redirect_uri)
+{
+ char uri[300], *val;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires user remediation", NULL);
+ val = db_get_osu_config_val(ctx, realm, "remediation_url");
+ if (val == NULL)
+ return NULL;
+
+ db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+ USER_REMEDIATION);
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * free_remediation(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id,
+ const char *redirect_uri)
+{
+ char uri[300], *val;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires free/public account remediation", NULL);
+ val = db_get_osu_config_val(ctx, realm, "free_remediation_url");
+ if (val == NULL)
+ return NULL;
+
+ db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+ FREE_REMEDIATION);
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * no_sub_rem(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id)
+{
+ const char *status;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "no subscription mediation available", NULL);
+
+ status = "No update available at this time";
+ return build_post_dev_data_response(ctx, NULL, session_id, status,
+ NULL);
+}
+
+
+static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc,
+ const char *redirect_uri)
+{
+ char *type, *identity;
+ xml_node_t *ret;
+ char *free_account;
+
+ identity = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (identity == NULL || strlen(identity) == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "user not found in database for remediation",
+ NULL);
+ os_free(identity);
+ return build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred",
+ "Not found");
+ }
+ os_free(identity);
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ if (free_account && strcmp(free_account, user) == 0) {
+ free(free_account);
+ return no_sub_rem(ctx, user, realm, session_id);
+ }
+ free(free_account);
+
+ type = db_get_val(ctx, user, realm, "remediation", dmacc);
+ if (type && strcmp(type, "free") != 0) {
+ char *val;
+ int shared = 0;
+ val = db_get_val(ctx, user, realm, "shared", dmacc);
+ if (val)
+ shared = atoi(val);
+ free(val);
+ if (shared) {
+ free(type);
+ return no_sub_rem(ctx, user, realm, session_id);
+ }
+ }
+ if (type && strcmp(type, "user") == 0)
+ ret = user_remediation(ctx, user, realm, session_id,
+ redirect_uri);
+ else if (type && strcmp(type, "free") == 0)
+ ret = free_remediation(ctx, user, realm, session_id,
+ redirect_uri);
+ else if (type && strcmp(type, "policy") == 0)
+ ret = policy_remediation(ctx, user, realm, session_id, dmacc);
+ else
+ ret = machine_remediation(ctx, user, realm, session_id, dmacc);
+ free(type);
+
+ return ret;
+}
+
+
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+ const char *realm, int use_dmacc)
+{
+ char *policy_id;
+ char fname[200];
+ xml_node_t *policy, *node;
+
+ policy_id = db_get_val(ctx, user, realm, "policy", use_dmacc);
+ if (policy_id == NULL || strlen(policy_id) == 0) {
+ free(policy_id);
+ policy_id = strdup("default");
+ if (policy_id == NULL)
+ return NULL;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml",
+ ctx->root_dir, policy_id);
+ free(policy_id);
+ debug_print(ctx, 1, "Use policy file %s", fname);
+
+ policy = node_from_file(ctx->xml, fname);
+ if (policy == NULL)
+ return NULL;
+
+ node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI");
+ if (node) {
+ char *url;
+ url = db_get_osu_config_val(ctx, realm, "policy_url");
+ if (url == NULL) {
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+ xml_node_set_text(ctx->xml, node, url);
+ free(url);
+ }
+
+ node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate");
+ if (node && use_dmacc) {
+ char *pw;
+ pw = db_get_val(ctx, user, realm, "osu_password", use_dmacc);
+ if (pw == NULL ||
+ build_username_password(ctx, node, user, pw) == NULL) {
+ debug_print(ctx, 1, "Failed to add Policy/PolicyUpdate/"
+ "UsernamePassword");
+ free(pw);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+ free(pw);
+ }
+
+ return policy;
+}
+
+
+static xml_node_t * hs20_policy_update(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+ xml_node_t *policy;
+ char buf[400];
+ const char *status;
+ char *identity;
+
+ identity = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (identity == NULL || strlen(identity) == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "user not found in database for policy update",
+ NULL);
+ os_free(identity);
+ return build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred",
+ "Not found");
+ }
+ os_free(identity);
+
+ policy = build_policy(ctx, user, realm, dmacc);
+ if (!policy) {
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "No update available at this time", NULL);
+ }
+
+ db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE);
+
+ status = "Update complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Credential1/Policy",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id, "policy update",
+ policy);
+ xml_node_free(ctx->xml, policy);
+
+ return spp_node;
+}
+
+
+static xml_node_t * spp_get_mo(struct hs20_svc *ctx, xml_node_t *node,
+ const char *urn, int *valid, char **ret_err)
+{
+ xml_node_t *child, *tnds, *mo;
+ const char *name;
+ char *mo_urn;
+ char *str;
+ char fname[200];
+
+ *valid = -1;
+ if (ret_err)
+ *ret_err = NULL;
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (strcmp(name, "moContainer") != 0)
+ continue;
+ mo_urn = xml_node_get_attr_value_ns(ctx->xml, child, SPP_NS_URI,
+ "moURN");
+ if (strcasecmp(urn, mo_urn) == 0) {
+ xml_node_get_attr_value_free(ctx->xml, mo_urn);
+ break;
+ }
+ xml_node_get_attr_value_free(ctx->xml, mo_urn);
+ }
+
+ if (child == NULL)
+ return NULL;
+
+ debug_print(ctx, 1, "moContainer text for %s", urn);
+ debug_dump_node(ctx, "moContainer", child);
+
+ str = xml_node_get_text(ctx->xml, child);
+ debug_print(ctx, 1, "moContainer payload: '%s'", str);
+ tnds = xml_node_from_buf(ctx->xml, str);
+ xml_node_get_text_free(ctx->xml, str);
+ if (tnds == NULL) {
+ debug_print(ctx, 1, "could not parse moContainer text");
+ return NULL;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/dm_ddf-v1_2.dtd", ctx->root_dir);
+ if (xml_validate_dtd(ctx->xml, tnds, fname, ret_err) == 0)
+ *valid = 1;
+ else if (ret_err && *ret_err &&
+ os_strcmp(*ret_err, "No declaration for attribute xmlns of element MgmtTree\n") == 0) {
+ free(*ret_err);
+ debug_print(ctx, 1, "Ignore OMA-DM DDF DTD validation error for MgmtTree namespace declaration with xmlns attribute");
+ *ret_err = NULL;
+ *valid = 1;
+ } else
+ *valid = 0;
+
+ mo = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (mo == NULL) {
+ debug_print(ctx, 1, "invalid moContainer for %s", urn);
+ }
+
+ return mo;
+}
+
+
+static xml_node_t * spp_exec_upload_mo(struct hs20_svc *ctx,
+ const char *session_id, const char *urn)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node, *exec_node;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ node = xml_node_create(ctx->xml, exec_node, ns, "uploadMO");
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", urn);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_subscription_registration(struct hs20_svc *ctx,
+ const char *realm,
+ const char *session_id,
+ const char *redirect_uri)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *exec_node;
+ char uri[300], *val;
+
+ if (db_add_session(ctx, NULL, realm, session_id, NULL, redirect_uri,
+ SUBSCRIPTION_REGISTRATION) < 0)
+ return NULL;
+ val = db_get_osu_config_val(ctx, realm, "signup_url");
+ if (val == NULL)
+ return NULL;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+ uri);
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ return build_sub_rem_resp(ctx, user, realm, session_id, 0, dmacc);
+}
+
+
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+ const char *field)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ cmd = sqlite3_mprintf("SELECT value FROM osu_config WHERE realm=%Q AND "
+ "field=%Q", realm, field);
+ if (cmd == NULL)
+ return NULL;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ memset(&data, 0, sizeof(data));
+ data.field = "value";
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "DB: Could not find osu_config %s: %s",
+ realm, sqlite3_errmsg(ctx->db));
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: return '%s'", data.value);
+ return data.value;
+}
+
+
+static xml_node_t * build_pps(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *pw, const char *cert,
+ int machine_managed)
+{
+ xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp;
+ xml_node_t *cred, *eap, *userpw;
+
+ pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "PerProviderSubscription");
+ if (pps == NULL)
+ return NULL;
+
+ add_text_node(ctx, pps, "UpdateIdentifier", "1");
+
+ c = xml_node_create(ctx->xml, pps, NULL, "Credential1");
+
+ add_text_node(ctx, c, "CredentialPriority", "1");
+
+ aaa = xml_node_create(ctx->xml, c, NULL, "AAAServerTrustRoot");
+ aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1");
+ add_text_node_conf(ctx, realm, aaa1, "CertURL",
+ "aaa_trust_root_cert_url");
+ add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+
+ upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
+ add_text_node(ctx, upd, "UpdateInterval", "4294967295");
+ add_text_node(ctx, upd, "UpdateMethod", "ClientInitiated");
+ add_text_node(ctx, upd, "Restriction", "HomeSP");
+ add_text_node_conf(ctx, realm, upd, "URI", "spp_http_auth_url");
+ trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+ add_text_node_conf(ctx, realm, trust, "CertURL", "trust_root_cert_url");
+ add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+
+ homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP");
+ add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name");
+ add_text_node_conf(ctx, realm, homesp, "FQDN", "fqdn");
+
+ xml_node_create(ctx->xml, c, NULL, "SubscriptionParameters");
+
+ cred = xml_node_create(ctx->xml, c, NULL, "Credential");
+ add_creation_date(ctx, cred);
+ if (cert) {
+ xml_node_t *dc;
+ dc = xml_node_create(ctx->xml, cred, NULL,
+ "DigitalCertificate");
+ add_text_node(ctx, dc, "CertificateType", "x509v3");
+ add_text_node(ctx, dc, "CertSHA256Fingerprint", cert);
+ } else {
+ userpw = build_username_password(ctx, cred, user, pw);
+ add_text_node(ctx, userpw, "MachineManaged",
+ machine_managed ? "TRUE" : "FALSE");
+ eap = xml_node_create(ctx->xml, userpw, NULL, "EAPMethod");
+ add_text_node(ctx, eap, "EAPType", "21");
+ add_text_node(ctx, eap, "InnerMethod", "MS-CHAP-V2");
+ }
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return pps;
+}
+
+
+static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *user,
+ const char *realm)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *enroll, *exec_node;
+ char *val;
+ char password[11];
+ char *b64;
+
+ if (new_password(password, sizeof(password)) < 0)
+ return NULL;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ enroll = xml_node_create(ctx->xml, exec_node, ns, "getCertificate");
+ xml_node_add_attr(ctx->xml, enroll, NULL, "enrollmentProtocol", "EST");
+
+ val = db_get_osu_config_val(ctx, realm, "est_url");
+ xml_node_create_text(ctx->xml, enroll, ns, "enrollmentServerURI",
+ val ? val : "");
+ os_free(val);
+ xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
+
+ b64 = (char *) base64_encode((unsigned char *) password,
+ strlen(password), NULL);
+ if (b64 == NULL) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+ xml_node_create_text(ctx->xml, enroll, ns, "estPassword", b64);
+ free(b64);
+
+ db_update_session_password(ctx, user, realm, session_id, password);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx,
+ const char *session_id,
+ int enrollment_done)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node = NULL;
+ xml_node_t *pps, *tnds;
+ char buf[400];
+ char *str;
+ char *user, *realm, *pw, *type, *mm;
+ const char *status;
+ int cert = 0;
+ int machine_managed = 0;
+ char *fingerprint;
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+
+ if (!user || !realm || !pw) {
+ debug_print(ctx, 1, "Could not find session info from DB for "
+ "the new subscription");
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+
+ mm = db_get_session_val(ctx, NULL, NULL, session_id, "machine_managed");
+ if (mm && atoi(mm))
+ machine_managed = 1;
+ free(mm);
+
+ type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+ if (type && strcmp(type, "cert") == 0)
+ cert = 1;
+ free(type);
+
+ if (cert && !enrollment_done) {
+ xml_node_t *ret;
+ hs20_eventlog(ctx, user, realm, session_id,
+ "request client certificate enrollment", NULL);
+ ret = spp_exec_get_certificate(ctx, session_id, user, realm);
+ free(user);
+ free(realm);
+ free(pw);
+ return ret;
+ }
+
+ if (!cert && strlen(pw) == 0) {
+ machine_managed = 1;
+ free(pw);
+ pw = malloc(11);
+ if (pw == NULL || new_password(pw, 11) < 0) {
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+ }
+
+ status = "Provisioning complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ pps = build_pps(ctx, user, realm, pw,
+ fingerprint ? fingerprint : NULL, machine_managed);
+ free(fingerprint);
+ if (!pps) {
+ xml_node_free(ctx->xml, spp_node);
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+
+ debug_print(ctx, 1, "Request DB subscription registration on success "
+ "notification");
+ db_add_session_pps(ctx, user, realm, session_id, pps);
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "new subscription", pps);
+ free(user);
+ free(pw);
+
+ tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+ xml_node_free(ctx->xml, pps);
+ if (!tnds) {
+ xml_node_free(ctx->xml, spp_node);
+ free(realm);
+ return NULL;
+ }
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL) {
+ xml_node_free(ctx->xml, spp_node);
+ free(realm);
+ return NULL;
+ }
+
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+ free(str);
+ snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+ free(realm);
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_free_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+ xml_node_t *cred;
+ char buf[400];
+ char *status;
+ char *free_account, *pw;
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ if (free_account == NULL)
+ return NULL;
+ pw = db_get_val(ctx, free_account, realm, "password", 0);
+ if (pw == NULL) {
+ free(free_account);
+ return NULL;
+ }
+
+ cred = build_credential_pw(ctx, free_account, realm, pw);
+ free(free_account);
+ free(pw);
+ if (!cred) {
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Credential1/Credential",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "free/public remediation", cred);
+ xml_node_free(ctx->xml, cred);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_complete(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper == USER_REMEDIATION) {
+ return hs20_user_input_remediation(ctx, user, realm, dmacc,
+ session_id);
+ }
+
+ if (oper == FREE_REMEDIATION) {
+ return hs20_user_input_free_remediation(ctx, user, realm,
+ session_id);
+ }
+
+ if (oper == SUBSCRIPTION_REGISTRATION) {
+ return hs20_user_input_registration(ctx, session_id, 0);
+ }
+
+ debug_print(ctx, 1, "User session %s not in state for user input "
+ "completion", session_id);
+ return NULL;
+}
+
+
+static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper == SUBSCRIPTION_REGISTRATION)
+ return hs20_user_input_registration(ctx, session_id, 1);
+
+ debug_print(ctx, 1, "User session %s not in state for certificate "
+ "enrollment completion", session_id);
+ return NULL;
+}
+
+
+static xml_node_t * hs20_cert_enroll_failed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+ xml_node_t *spp_node, *node;
+ char *status;
+ xml_namespace_t *ns;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper != SUBSCRIPTION_REGISTRATION) {
+ debug_print(ctx, 1, "User session %s not in state for "
+ "enrollment failure", session_id);
+ return NULL;
+ }
+
+ status = "Error occurred";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ "Credentials cannot be provisioned at this time");
+ db_remove_session(ctx, user, realm, session_id);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx,
+ xml_node_t *node,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc)
+{
+ const char *req_reason;
+ char *redirect_uri = NULL;
+ char *req_reason_buf = NULL;
+ char str[200];
+ xml_node_t *ret = NULL, *devinfo = NULL, *devdetail = NULL;
+ xml_node_t *mo;
+ char *version;
+ int valid;
+ char *supp, *pos;
+ char *err;
+
+ version = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sppVersion");
+ if (version == NULL || strstr(version, "1.0") == NULL) {
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "SPP version not supported");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Unsupported sppVersion", ret);
+ xml_node_get_attr_value_free(ctx->xml, version);
+ return ret;
+ }
+ xml_node_get_attr_value_free(ctx->xml, version);
+
+ mo = get_node(ctx->xml, node, "supportedMOList");
+ if (mo == NULL) {
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No supportedMOList element", ret);
+ return ret;
+ }
+ supp = xml_node_get_text(ctx->xml, mo);
+ for (pos = supp; pos && *pos; pos++)
+ *pos = tolower(*pos);
+ if (supp == NULL ||
+ strstr(supp, URN_OMA_DM_DEVINFO) == NULL ||
+ strstr(supp, URN_OMA_DM_DEVDETAIL) == NULL ||
+ strstr(supp, URN_HS20_PPS) == NULL) {
+ xml_node_get_text_free(ctx->xml, supp);
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "One or more mandatory MOs not supported");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Unsupported MOs", ret);
+ return ret;
+ }
+ xml_node_get_text_free(ctx->xml, supp);
+
+ req_reason_buf = xml_node_get_attr_value(ctx->xml, node,
+ "requestReason");
+ if (req_reason_buf == NULL) {
+ debug_print(ctx, 1, "No requestReason attribute");
+ return NULL;
+ }
+ req_reason = req_reason_buf;
+
+ redirect_uri = xml_node_get_attr_value(ctx->xml, node, "redirectURI");
+
+ debug_print(ctx, 1, "requestReason: %s sessionID: %s redirectURI: %s",
+ req_reason, session_id, redirect_uri);
+ snprintf(str, sizeof(str), "sppPostDevData: requestReason=%s",
+ req_reason);
+ hs20_eventlog(ctx, user, realm, session_id, str, NULL);
+
+ devinfo = spp_get_mo(ctx, node, URN_OMA_DM_DEVINFO, &valid, &err);
+ if (devinfo == NULL) {
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No DevInfo moContainer in sppPostDevData",
+ ret);
+ os_free(err);
+ goto out;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received DevInfo MO", devinfo);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors in DevInfo MO",
+ err);
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ os_free(err);
+ goto out;
+ }
+ os_free(err);
+ if (user)
+ db_update_mo(ctx, user, realm, "devinfo", devinfo);
+
+ devdetail = spp_get_mo(ctx, node, URN_OMA_DM_DEVDETAIL, &valid, &err);
+ if (devdetail == NULL) {
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No DevDetail moContainer in sppPostDevData",
+ ret);
+ os_free(err);
+ goto out;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received DevDetail MO", devdetail);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors "
+ "in DevDetail MO", err);
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ os_free(err);
+ goto out;
+ }
+ os_free(err);
+ if (user)
+ db_update_mo(ctx, user, realm, "devdetail", devdetail);
+
+ if (user)
+ mo = spp_get_mo(ctx, node, URN_HS20_PPS, &valid, &err);
+ else {
+ mo = NULL;
+ err = NULL;
+ }
+ if (user && mo) {
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received PPS MO", mo);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors "
+ "in PPS MO", err);
+ xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+ os_free(err);
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "Error occurred", "Other");
+ }
+ db_update_mo(ctx, user, realm, "pps", mo);
+ db_update_val(ctx, user, realm, "fetch_pps", "0", dmacc);
+ xml_node_free(ctx->xml, mo);
+ }
+ os_free(err);
+
+ if (user && !mo) {
+ char *fetch;
+ int fetch_pps;
+
+ fetch = db_get_val(ctx, user, realm, "fetch_pps", dmacc);
+ fetch_pps = fetch ? atoi(fetch) : 0;
+ free(fetch);
+
+ if (fetch_pps) {
+ enum hs20_session_operation oper;
+ if (strcasecmp(req_reason, "Subscription remediation")
+ == 0)
+ oper = CONTINUE_SUBSCRIPTION_REMEDIATION;
+ else if (strcasecmp(req_reason, "Policy update") == 0)
+ oper = CONTINUE_POLICY_UPDATE;
+ else
+ oper = NO_OPERATION;
+ if (db_add_session(ctx, user, realm, session_id, NULL,
+ NULL, oper) < 0)
+ goto out;
+
+ ret = spp_exec_upload_mo(ctx, session_id,
+ URN_HS20_PPS);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "request PPS MO upload",
+ ret);
+ goto out;
+ }
+ }
+
+ if (user && strcasecmp(req_reason, "MO upload") == 0) {
+ char *val = db_get_session_val(ctx, user, realm, session_id,
+ "operation");
+ enum hs20_session_operation oper;
+ if (!val) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ goto out;
+ }
+ oper = atoi(val);
+ free(val);
+ if (oper == CONTINUE_SUBSCRIPTION_REMEDIATION)
+ req_reason = "Subscription remediation";
+ else if (oper == CONTINUE_POLICY_UPDATE)
+ req_reason = "Policy update";
+ else {
+ debug_print(ctx, 1,
+ "No pending operation in session %s",
+ session_id);
+ goto out;
+ }
+ }
+
+ if (strcasecmp(req_reason, "Subscription registration") == 0) {
+ ret = hs20_subscription_registration(ctx, realm, session_id,
+ redirect_uri);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription registration response",
+ ret);
+ goto out;
+ }
+ if (user && strcasecmp(req_reason, "Subscription remediation") == 0) {
+ ret = hs20_subscription_remediation(ctx, user, realm,
+ session_id, dmacc,
+ redirect_uri);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription remediation response",
+ ret);
+ goto out;
+ }
+ if (user && strcasecmp(req_reason, "Policy update") == 0) {
+ ret = hs20_policy_update(ctx, user, realm, session_id, dmacc);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "policy update response",
+ ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "User input completed") == 0) {
+ if (devinfo)
+ db_add_session_devinfo(ctx, session_id, devinfo);
+ if (devdetail)
+ db_add_session_devdetail(ctx, session_id, devdetail);
+ ret = hs20_user_input_complete(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "user input completed response", ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Certificate enrollment completed") == 0) {
+ ret = hs20_cert_enroll_completed(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate enrollment response", ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Certificate enrollment failed") == 0) {
+ ret = hs20_cert_enroll_failed(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate enrollment failed response",
+ ret);
+ goto out;
+ }
+
+ debug_print(ctx, 1, "Unsupported requestReason '%s' user '%s'",
+ req_reason, user);
+out:
+ xml_node_get_attr_value_free(ctx->xml, req_reason_buf);
+ xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+ if (devinfo)
+ xml_node_free(ctx->xml, devinfo);
+ if (devdetail)
+ xml_node_free(ctx->xml, devdetail);
+ return ret;
+}
+
+
+static xml_node_t * build_spp_exchange_complete(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *status,
+ const char *error_code)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppExchangeComplete");
+
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+ if (error_code) {
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int add_subscription(struct hs20_svc *ctx, const char *session_id)
+{
+ char *user, *realm, *pw, *pw_mm, *pps, *str;
+ char *sql;
+ int ret = -1;
+ char *free_account;
+ int free_acc;
+ char *type;
+ int cert = 0;
+ char *cert_pem, *fingerprint;
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+ pw_mm = db_get_session_val(ctx, NULL, NULL, session_id,
+ "machine_managed");
+ pps = db_get_session_val(ctx, NULL, NULL, session_id, "pps");
+ cert_pem = db_get_session_val(ctx, NULL, NULL, session_id, "cert_pem");
+ fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+ if (type && strcmp(type, "cert") == 0)
+ cert = 1;
+ free(type);
+
+ if (!user || !realm || !pw) {
+ debug_print(ctx, 1, "Could not find session info from DB for "
+ "the new subscription");
+ goto out;
+ }
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ free_acc = free_account && strcmp(free_account, user) == 0;
+ free(free_account);
+
+ debug_print(ctx, 1,
+ "New subscription: user='%s' realm='%s' free_acc=%d",
+ user, realm, free_acc);
+ debug_print(ctx, 1, "New subscription: pps='%s'", pps);
+
+ sql = sqlite3_mprintf("UPDATE eventlog SET user=%Q, realm=%Q WHERE "
+ "sessionid=%Q AND (user='' OR user IS NULL)",
+ user, realm, session_id);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update eventlog in "
+ "sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+
+ if (free_acc) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "completed shared free account registration",
+ NULL);
+ ret = 0;
+ goto out;
+ }
+
+ sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,"
+ "methods,cert,cert_pem,machine_managed) VALUES "
+ "(%Q,%Q,1,%Q,%Q,%Q,%d)",
+ user, realm, cert ? "TLS" : "TTLS-MSCHAPV2",
+ fingerprint ? fingerprint : "",
+ cert_pem ? cert_pem : "",
+ pw_mm && atoi(pw_mm) ? 1 : 0);
+ if (sql == NULL)
+ goto out;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add user in sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ sqlite3_free(sql);
+ goto out;
+ }
+ sqlite3_free(sql);
+
+ if (cert)
+ ret = 0;
+ else
+ ret = update_password(ctx, user, realm, pw, 0);
+ if (ret < 0) {
+ sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND "
+ "realm=%Q AND phase2=1",
+ user, realm);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ sqlite3_exec(ctx->db, sql, NULL, NULL, NULL);
+ sqlite3_free(sql);
+ }
+ }
+
+ if (pps)
+ db_update_mo_str(ctx, user, realm, "pps", pps);
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "devinfo");
+ if (str) {
+ db_update_mo_str(ctx, user, realm, "devinfo", str);
+ free(str);
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "devdetail");
+ if (str) {
+ db_update_mo_str(ctx, user, realm, "devdetail", str);
+ free(str);
+ }
+
+ if (ret == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "completed subscription registration", NULL);
+ }
+
+out:
+ free(user);
+ free(realm);
+ free(pw);
+ free(pw_mm);
+ free(pps);
+ free(cert_pem);
+ free(fingerprint);
+ return ret;
+}
+
+
+static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx,
+ xml_node_t *node,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc)
+{
+ char *status;
+ xml_node_t *ret;
+ char *val;
+ enum hs20_session_operation oper;
+
+ status = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sppStatus");
+ if (status == NULL) {
+ debug_print(ctx, 1, "No sppStatus attribute");
+ return NULL;
+ }
+
+ debug_print(ctx, 1, "sppUpdateResponse: sppStatus: %s sessionID: %s",
+ status, session_id);
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (!val) {
+ debug_print(ctx, 1,
+ "No session active for user: %s sessionID: %s",
+ user, session_id);
+ oper = NO_OPERATION;
+ } else
+ oper = atoi(val);
+
+ if (strcasecmp(status, "OK") == 0) {
+ char *new_pw = NULL;
+
+ xml_node_get_attr_value_free(ctx->xml, status);
+
+ if (oper == USER_REMEDIATION) {
+ new_pw = db_get_session_val(ctx, user, realm,
+ session_id, "password");
+ if (new_pw == NULL || strlen(new_pw) == 0) {
+ free(new_pw);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "No password "
+ "had been assigned for "
+ "session", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ oper = UPDATE_PASSWORD;
+ }
+ if (oper == UPDATE_PASSWORD) {
+ if (!new_pw) {
+ new_pw = db_get_session_val(ctx, user, realm,
+ session_id,
+ "password");
+ if (!new_pw) {
+ db_remove_session(ctx, user, realm,
+ session_id);
+ return NULL;
+ }
+ }
+ debug_print(ctx, 1, "Update user '%s' password in DB",
+ user);
+ if (update_password(ctx, user, realm, new_pw, dmacc) <
+ 0) {
+ debug_print(ctx, 1, "Failed to update user "
+ "'%s' password in DB", user);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "Failed to "
+ "update database", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ hs20_eventlog(ctx, user, realm,
+ session_id, "Updated user password "
+ "in database", NULL);
+ }
+ if (oper == SUBSCRIPTION_REGISTRATION) {
+ if (add_subscription(ctx, session_id) < 0) {
+ debug_print(ctx, 1, "Failed to add "
+ "subscription into DB");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "Failed to "
+ "update database", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ }
+ if (oper == POLICY_REMEDIATION || oper == POLICY_UPDATE) {
+ char *val;
+ val = db_get_val(ctx, user, realm, "remediation",
+ dmacc);
+ if (val && strcmp(val, "policy") == 0)
+ db_update_val(ctx, user, realm, "remediation",
+ "", dmacc);
+ free(val);
+ }
+ ret = build_spp_exchange_complete(
+ ctx, session_id,
+ "Exchange complete, release TLS connection", NULL);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Exchange completed", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+
+ ret = build_spp_exchange_complete(ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id, "Error occurred", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return ret;
+}
+
+
+#define SPP_SESSION_ID_LEN 16
+
+static char * gen_spp_session_id(void)
+{
+ FILE *f;
+ int i;
+ char *session;
+
+ session = os_malloc(SPP_SESSION_ID_LEN * 2 + 1);
+ if (session == NULL)
+ return NULL;
+
+ f = fopen("/dev/urandom", "r");
+ if (f == NULL) {
+ os_free(session);
+ return NULL;
+ }
+ for (i = 0; i < SPP_SESSION_ID_LEN; i++)
+ os_snprintf(session + i * 2, 3, "%02x", fgetc(f));
+
+ fclose(f);
+ return session;
+}
+
+xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
+ const char *auth_user,
+ const char *auth_realm, int dmacc)
+{
+ xml_node_t *ret = NULL;
+ char *session_id;
+ const char *op_name;
+ char *xml_err;
+ char fname[200];
+
+ debug_dump_node(ctx, "received request", node);
+
+ if (!dmacc && auth_user && auth_realm) {
+ char *real;
+ real = db_get_val(ctx, auth_user, auth_realm, "identity", 0);
+ if (!real) {
+ real = db_get_val(ctx, auth_user, auth_realm,
+ "identity", 1);
+ if (real)
+ dmacc = 1;
+ }
+ os_free(real);
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/spp.xsd", ctx->root_dir);
+ if (xml_validate(ctx->xml, node, fname, &xml_err) < 0) {
+ /*
+ * We may not be able to extract the sessionID from invalid
+ * input, but well, we can try.
+ */
+ session_id = xml_node_get_attr_value_ns(ctx->xml, node,
+ SPP_NS_URI,
+ "sessionID");
+ debug_print(ctx, 1, "SPP message failed validation");
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "SPP message failed validation", node);
+ hs20_eventlog(ctx, auth_user, auth_realm, session_id,
+ "Validation errors", xml_err);
+ os_free(xml_err);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppValidationError");
+ return ret;
+ }
+
+ session_id = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sessionID");
+ if (session_id) {
+ char *tmp;
+ debug_print(ctx, 1, "Received sessionID %s", session_id);
+ tmp = os_strdup(session_id);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ if (tmp == NULL)
+ return NULL;
+ session_id = tmp;
+ } else {
+ session_id = gen_spp_session_id();
+ if (session_id == NULL) {
+ debug_print(ctx, 1, "Failed to generate sessionID");
+ return NULL;
+ }
+ debug_print(ctx, 1, "Generated sessionID %s", session_id);
+ }
+
+ op_name = xml_node_get_localname(ctx->xml, node);
+ if (op_name == NULL) {
+ debug_print(ctx, 1, "Could not get op_name");
+ return NULL;
+ }
+
+ if (strcmp(op_name, "sppPostDevData") == 0) {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "sppPostDevData received and validated",
+ node);
+ ret = hs20_spp_post_dev_data(ctx, node, auth_user, auth_realm,
+ session_id, dmacc);
+ } else if (strcmp(op_name, "sppUpdateResponse") == 0) {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "sppUpdateResponse received and validated",
+ node);
+ ret = hs20_spp_update_response(ctx, node, auth_user,
+ auth_realm, session_id, dmacc);
+ } else {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "Unsupported SPP message received and "
+ "validated", node);
+ debug_print(ctx, 1, "Unsupported operation '%s'", op_name);
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppUnknownCommandError");
+ }
+ os_free(session_id);
+
+ if (ret == NULL) {
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppInternalError");
+ }
+
+ return ret;
+}
+
+
+int hs20_spp_server_init(struct hs20_svc *ctx)
+{
+ char fname[200];
+ ctx->db = NULL;
+ snprintf(fname, sizeof(fname), "%s/AS/DB/eap_user.db", ctx->root_dir);
+ if (sqlite3_open(fname, &ctx->db)) {
+ printf("Failed to open sqlite database: %s\n",
+ sqlite3_errmsg(ctx->db));
+ sqlite3_close(ctx->db);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hs20_spp_server_deinit(struct hs20_svc *ctx)
+{
+ sqlite3_close(ctx->db);
+ ctx->db = NULL;
+}
diff --git a/hs20/server/spp_server.h b/hs20/server/spp_server.h
new file mode 100644
index 0000000..7b27be3
--- /dev/null
+++ b/hs20/server/spp_server.h
@@ -0,0 +1,32 @@
+/*
+ * Hotspot 2.0 SPP server
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SPP_SERVER_H
+#define SPP_SERVER_H
+
+struct hs20_svc {
+ const void *ctx;
+ struct xml_node_ctx *xml;
+ char *root_dir;
+ FILE *debug_log;
+ sqlite3 *db;
+ const char *addr;
+};
+
+
+void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)));
+void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node);
+
+xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
+ const char *auth_user,
+ const char *auth_realm, int dmacc);
+int hs20_spp_server_init(struct hs20_svc *ctx);
+void hs20_spp_server_deinit(struct hs20_svc *ctx);
+
+#endif /* SPP_SERVER_H */
diff --git a/hs20/server/sql-example.txt b/hs20/server/sql-example.txt
new file mode 100644
index 0000000..20dcf2f
--- /dev/null
+++ b/hs20/server/sql-example.txt
@@ -0,0 +1,17 @@
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','fqdn','example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','friendly_name','Example Operator');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','spp_http_auth_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/spp-root-ca.der');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/aaa-root-ca.der');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_account','free');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','policy_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','remediation_url','https://subscription-server.osu.example.com/hs20/remediation.php?session_id=');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_remediation_url','https://subscription-server.osu.example.com/hs20/free-remediation.php?session_id=');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','signup_url','https://subscription-server.osu.example.com/hs20/signup.php?session_id=');
+
+
+INSERT INTO users(identity,realm,methods,password,phase2,shared) VALUES('free','example.com','TTLS-MSCHAPV2','free',1,1);
+
+INSERT INTO wildcards(identity,methods) VALUES('','TTLS,TLS');
diff --git a/hs20/server/sql.txt b/hs20/server/sql.txt
new file mode 100644
index 0000000..6609538
--- /dev/null
+++ b/hs20/server/sql.txt
@@ -0,0 +1,59 @@
+CREATE TABLE eventlog(
+ user TEXT,
+ realm TEXT,
+ sessionid TEXT COLLATE NOCASE,
+ timestamp TEXT,
+ notes TEXT,
+ dump TEXT,
+ addr TEXT
+);
+
+CREATE TABLE sessions(
+ timestamp TEXT,
+ id TEXT COLLATE NOCASE,
+ user TEXT,
+ realm TEXT,
+ password TEXT,
+ machine_managed BOOLEAN,
+ operation INTEGER,
+ type TEXT,
+ pps TEXT,
+ redirect_uri TEXT,
+ devinfo TEXT,
+ devdetail TEXT,
+ cert TEXT,
+ cert_pem TEXT
+);
+
+CREATE index sessions_id_index ON sessions(id);
+
+CREATE TABLE osu_config(
+ realm TEXT,
+ field TEXT,
+ value TEXT
+);
+
+CREATE TABLE users(
+ identity TEXT PRIMARY KEY,
+ methods TEXT,
+ password TEXT,
+ machine_managed BOOLEAN,
+ remediation TEXT,
+ phase2 INTEGER,
+ realm TEXT,
+ policy TEXT,
+ devinfo TEXT,
+ devdetail TEXT,
+ pps TEXT,
+ fetch_pps INTEGER,
+ osu_user TEXT,
+ osu_password TEXT,
+ shared INTEGER,
+ cert TEXT,
+ cert_pem TEXT
+);
+
+CREATE TABLE wildcards(
+ identity TEXT PRIMARY KEY,
+ methods TEXT
+);
diff --git a/hs20/server/www/add-free.php b/hs20/server/www/add-free.php
new file mode 100644
index 0000000..1efc655
--- /dev/null
+++ b/hs20/server/www/add-free.php
@@ -0,0 +1,50 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_POST["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
+else
+ die("Missing session id");
+if (strlen($id) < 32)
+ die("Invalid session id");
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+$realm = $row['realm'];
+
+$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
+if (!$row || strlen($row['value']) == 0) {
+ die("Free account disabled");
+}
+
+$user = $row['value'];
+
+$row = $db->query("SELECT password FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
+if (!$row)
+ die("Free account not found");
+
+$pw = $row['password'];
+
+if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', machine_managed='1' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for a new PPS MO')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/hs20/server/www/add-mo.php b/hs20/server/www/add-mo.php
new file mode 100644
index 0000000..a3b4513
--- /dev/null
+++ b/hs20/server/www/add-mo.php
@@ -0,0 +1,56 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_POST["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
+else
+ die("Missing session id");
+
+$user = $_POST["user"];
+$pw = $_POST["password"];
+if (strlen($id) < 32 || !isset($user) || !isset($pw)) {
+ die("Invalid POST data");
+}
+
+if (strlen($user) < 1 || strncasecmp($user, "cert-", 5) == 0) {
+ echo "<html><body><p><red>Invalid username</red></p>\n";
+ echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
+ echo "</body></html>\n";
+ exit;
+}
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+$realm = $row['realm'];
+
+$userrow = $db->query("SELECT identity FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
+if ($userrow) {
+ echo "<html><body><p><red>Selected username is not available</red></p>\n";
+ echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
+ echo "</body></html>\n";
+ exit;
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+
+if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', type='password' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for a new PPS MO')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/hs20/server/www/cert-enroll.php b/hs20/server/www/cert-enroll.php
new file mode 100644
index 0000000..f023ca5
--- /dev/null
+++ b/hs20/server/www/cert-enroll.php
@@ -0,0 +1,39 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_GET["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
+else
+ die("Missing session id");
+if (strlen($id) < 32)
+ die("Invalid session id");
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+$realm = $row['realm'];
+
+$user = sha1(mt_rand());
+
+if (!$db->exec("UPDATE sessions SET user='$user', type='cert' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for client certificate enrollment')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/hs20/server/www/config.php b/hs20/server/www/config.php
new file mode 100644
index 0000000..e3af435
--- /dev/null
+++ b/hs20/server/www/config.php
@@ -0,0 +1,4 @@
+<?php
+$osu_root = "/home/user/hs20-server";
+$osu_db = "sqlite:$osu_root/AS/DB/eap_user.db";
+?>
diff --git a/hs20/server/www/est.php b/hs20/server/www/est.php
new file mode 100644
index 0000000..a45648b
--- /dev/null
+++ b/hs20/server/www/est.php
@@ -0,0 +1,198 @@
+<?php
+
+require('config.php');
+
+$params = split("/", $_SERVER["PATH_INFO"], 3);
+$realm = $params[1];
+$cmd = $params[2];
+$method = $_SERVER["REQUEST_METHOD"];
+
+unset($user);
+unset($rowid);
+
+if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
+ $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
+ 'uri'=>1, 'response'=>1);
+ $data = array();
+ $keys = implode('|', array_keys($needed));
+ preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
+ $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
+ foreach ($matches as $m) {
+ $data[$m[1]] = $m[3] ? $m[3] : $m[4];
+ unset($needed[$m[1]]);
+ }
+ if ($needed) {
+ error_log("EST: Missing auth parameter");
+ die('Authentication failed');
+ }
+ $user = $data['username'];
+ if (strlen($user) < 1) {
+ error_log("EST: Empty username");
+ die('Authentication failed');
+ }
+
+ $db = new PDO($osu_db);
+ if (!$db) {
+ error_log("EST: Could not access database");
+ die("Could not access database");
+ }
+
+ $sql = "SELECT rowid,password,operation FROM sessions " .
+ "WHERE user='$user' AND realm='$realm'";
+ $q = $db->query($sql);
+ if (!$q) {
+ error_log("EST: Session not found for user=$user realm=$realm");
+ die("Session not found");
+ }
+ $row = $q->fetch();
+ if (!$row) {
+ error_log("EST: Session fetch failed for user=$user realm=$realm");
+ die('Session not found');
+ }
+ $rowid = $row['rowid'];
+
+ $oper = $row['operation'];
+ if ($oper != '5') {
+ error_log("EST: Unexpected operation $oper for user=$user realm=$realm");
+ die("Session not found");
+ }
+ $pw = $row['password'];
+ if (strlen($pw) < 1) {
+ error_log("EST: Empty password for user=$user realm=$realm");
+ die('Authentication failed');
+ }
+
+ $A1 = md5($user . ':' . $realm . ':' . $pw);
+ $A2 = md5($method . ':' . $data['uri']);
+ $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
+ $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
+ if ($data['response'] != $resp) {
+ error_log("EST: Incorrect authentication response for user=$user realm=$realm");
+ die('Authentication failed');
+ }
+}
+
+
+if ($method == "GET" && $cmd == "cacerts") {
+ $fname = "$osu_root/est/$realm-cacerts.pkcs7";
+ if (!file_exists($fname)) {
+ error_log("EST: cacerts - unknown realm $realm");
+ die("Unknown realm");
+ }
+
+ header("Content-Transfer-Encoding: base64");
+ header("Content-Type: application/pkcs7-mime");
+
+ $data = file_get_contents($fname);
+ echo wordwrap(base64_encode($data), 72, "\n", true);
+ echo "\n";
+ error_log("EST: cacerts");
+} else if ($method == "GET" && $cmd == "csrattrs") {
+ header("Content-Transfer-Encoding: base64");
+ header("Content-Type: application/csrattrs");
+ readfile("$osu_root/est/est-attrs.b64");
+ error_log("EST: csrattrs");
+} else if ($method == "POST" && $cmd == "simpleenroll") {
+ if (!isset($user) || strlen($user) == 0) {
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Digest realm="'.$realm.
+ '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+ error_log("EST: simpleenroll - require authentication");
+ die('Authentication required');
+ }
+ if (!isset($_SERVER["CONTENT_TYPE"])) {
+ error_log("EST: simpleenroll without Content-Type");
+ die("Missing Content-Type");
+ }
+ if (!stristr($_SERVER["CONTENT_TYPE"], "application/pkcs10")) {
+ error_log("EST: simpleenroll - unexpected Content-Type: " .
+ $_SERVER["CONTENT_TYPE"]);
+ die("Unexpected Content-Type");
+ }
+
+ $data = file_get_contents("php://input");
+ error_log("EST: simpleenroll - POST data from php://input: " . $data);
+ $req = base64_decode($data);
+ if ($req == FALSE) {
+ error_log("EST: simpleenroll - Invalid base64-encoded PKCS#10 data");
+ die("Invalid base64-encoded PKCS#10 data");
+ }
+ $cadir = "$osu_root/est";
+ $reqfile = "$cadir/tmp/cert-req.pkcs10";
+ $f = fopen($reqfile, "wb");
+ fwrite($f, $req);
+ fclose($f);
+
+ $req_pem = "$reqfile.pem";
+ if (file_exists($req_pem))
+ unlink($req_pem);
+ exec("openssl req -in $reqfile -inform DER -out $req_pem -outform PEM");
+ if (!file_exists($req_pem)) {
+ error_log("EST: simpleenroll - Failed to parse certificate request");
+ die("Failed to parse certificate request");
+ }
+
+ /* FIX: validate request and add HS 2.0 extensions to cert */
+ $cert_pem = "$cadir/tmp/req-signed.pem";
+ if (file_exists($cert_pem))
+ unlink($cert_pem);
+ exec("openssl x509 -req -in $req_pem -CAkey $cadir/cakey.pem -out $cert_pem -CA $cadir/cacert.pem -CAserial $cadir/serial -days 365 -text");
+ if (!file_exists($cert_pem)) {
+ error_log("EST: simpleenroll - Failed to sign certificate");
+ die("Failed to sign certificate");
+ }
+
+ $cert = file_get_contents($cert_pem);
+ $handle = popen("openssl x509 -in $cert_pem -serial -noout", "r");
+ $serial = fread($handle, 200);
+ pclose($handle);
+ $pattern = "/serial=(?P<snhex>[0-9a-fA-F:]*)/m";
+ preg_match($pattern, $serial, $matches);
+ if (!isset($matches['snhex']) || strlen($matches['snhex']) < 1) {
+ error_log("EST: simpleenroll - Could not get serial number");
+ die("Could not get serial number");
+ }
+ $sn = str_replace(":", "", strtoupper($matches['snhex']));
+
+ $user = "cert-$sn";
+ error_log("EST: user = $user");
+
+ $cert_der = "$cadir/tmp/req-signed.der";
+ if (file_exists($cert_der))
+ unlink($cert_der);
+ exec("openssl x509 -in $cert_pem -inform PEM -out $cert_der -outform DER");
+ if (!file_exists($cert_der)) {
+ error_log("EST: simpleenroll - Failed to convert certificate");
+ die("Failed to convert certificate");
+ }
+ $der = file_get_contents($cert_der);
+ $fingerprint = hash("sha256", $der);
+
+ $pkcs7 = "$cadir/tmp/est-client.pkcs7";
+ if (file_exists($pkcs7))
+ unlink($pkcs7);
+ exec("openssl crl2pkcs7 -nocrl -certfile $cert_pem -out $pkcs7 -outform DER");
+ if (!file_exists($pkcs7)) {
+ error_log("EST: simpleenroll - Failed to prepare PKCS#7 file");
+ die("Failed to prepare PKCS#7 file");
+ }
+
+ if (!$db->exec("UPDATE sessions SET user='$user', cert='$fingerprint', cert_pem='$cert' WHERE rowid=$rowid")) {
+ error_log("EST: simpleenroll - Failed to update session database");
+ die("Failed to update session database");
+ }
+
+ header("Content-Transfer-Encoding: base64");
+ header("Content-Type: application/pkcs7-mime");
+
+ $data = file_get_contents($pkcs7);
+ $resp = wordwrap(base64_encode($data), 72, "\n", true);
+ echo $resp . "\n";
+ error_log("EST: simpleenroll - PKCS#7 response: " . $resp);
+} else {
+ header("HTTP/1.0 404 Not Found");
+ error_log("EST: Unexpected method or path");
+ die("Unexpected method or path");
+}
+
+?>
diff --git a/hs20/server/www/free-remediation.php b/hs20/server/www/free-remediation.php
new file mode 100644
index 0000000..5648b30
--- /dev/null
+++ b/hs20/server/www/free-remediation.php
@@ -0,0 +1,19 @@
+<html>
+<head>
+<title>Hotspot 2.0 - public and free hotspot - remediation</title>
+</head>
+<body>
+
+<h3>Hotspot 2.0 - public and free hotspot</h3>
+
+<p>Terms and conditions have changed. You need to accept the new terms
+to continue using this network.</p>
+
+<p>Terms and conditions..</p>
+
+<?php
+echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Accept</a><br>\n";
+?>
+
+</body>
+</html>
diff --git a/hs20/server/www/free.php b/hs20/server/www/free.php
new file mode 100644
index 0000000..8195069
--- /dev/null
+++ b/hs20/server/www/free.php
@@ -0,0 +1,23 @@
+<html>
+<head>
+<title>Hotspot 2.0 - public and free hotspot</title>
+</head>
+<body>
+
+<?php
+
+$id = $_GET["session_id"];
+
+echo "<h3>Hotspot 2.0 - public and free hotspot</h3>\n";
+
+echo "<form action=\"add-free.php\" method=\"POST\">\n";
+echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
+
+?>
+
+<p>Terms and conditions..</p>
+<input type="submit" value="Accept">
+</form>
+
+</body>
+</html>
diff --git a/hs20/server/www/redirect.php b/hs20/server/www/redirect.php
new file mode 100644
index 0000000..8fc9cd6
--- /dev/null
+++ b/hs20/server/www/redirect.php
@@ -0,0 +1,32 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_GET["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
+else
+ $id = 0;
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+
+header("Location: $uri", true, 302);
+
+$user = $row['user'];
+$realm = $row['realm'];
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'redirected after user input')");
+
+?>
diff --git a/hs20/server/www/remediation.php b/hs20/server/www/remediation.php
new file mode 100644
index 0000000..392a7bd
--- /dev/null
+++ b/hs20/server/www/remediation.php
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>Hotspot 2.0 subscription remediation</title>
+</head>
+<body>
+
+<?php
+
+echo "SessionID: " . $_GET["session_id"] . "<br>\n";
+
+echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
+
+?>
+
+This will provide a new machine-generated password.
+
+</body>
+</html>
diff --git a/hs20/server/www/signup.php b/hs20/server/www/signup.php
new file mode 100644
index 0000000..a626704
--- /dev/null
+++ b/hs20/server/www/signup.php
@@ -0,0 +1,46 @@
+<html>
+<head>
+<title>Hotspot 2.0 signup</title>
+</head>
+<body>
+
+<?php
+
+$id = $_GET["session_id"];
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+$row = $db->query("SELECT realm FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+$realm = $row['realm'];
+
+echo "<h3>Sign up for a subscription - $realm</h3>\n";
+
+$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
+if ($row && strlen($row['value']) > 0) {
+ echo "<p><a href=\"free.php?session_id=$id\">Sign up for free access</a></p>\n";
+}
+
+echo "<form action=\"add-mo.php\" method=\"POST\">\n";
+echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
+?>
+Select a username and password. Leave password empty to get automatically
+generated and machine managed password.<br>
+Username: <input type="text" name="user"><br>
+Password: <input type="password" name="password"><br>
+<input type="submit" value="Complete subscription registration">
+</form>
+
+<?php
+echo "<p><a href=\"cert-enroll.php?id=$id\">Enroll a client certificate</a></p>\n"
+?>
+
+</body>
+</html>
diff --git a/hs20/server/www/spp.php b/hs20/server/www/spp.php
new file mode 100644
index 0000000..dde4434
--- /dev/null
+++ b/hs20/server/www/spp.php
@@ -0,0 +1,127 @@
+<?php
+
+require('config.php');
+
+if (!stristr($_SERVER["CONTENT_TYPE"], "application/soap+xml")) {
+ error_log("spp.php - Unexpected Content-Type " . $_SERVER["CONTENT_TYPE"]);
+ die("Unexpected Content-Type");
+}
+
+if ($_SERVER["REQUEST_METHOD"] != "POST") {
+ error_log("spp.php - Unexpected method " . $_SERVER["REQUEST_METHOD"]);
+ die("Unexpected method");
+}
+
+if (isset($_GET["realm"])) {
+ $realm = $_GET["realm"];
+ $realm = PREG_REPLACE("/[^0-9a-zA-Z\.\-]/i", '', $realm);
+} else {
+ error_log("spp.php - Realm not specified");
+ die("Realm not specified");
+}
+
+unset($user);
+putenv("HS20CERT");
+
+if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
+ $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
+ 'uri'=>1, 'response'=>1);
+ $data = array();
+ $keys = implode('|', array_keys($needed));
+ preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
+ $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
+ foreach ($matches as $m) {
+ $data[$m[1]] = $m[3] ? $m[3] : $m[4];
+ unset($needed[$m[1]]);
+ }
+ if ($needed) {
+ error_log("spp.php - Authentication failed - missing: " . print_r($needed));
+ die('Authentication failed');
+ }
+ $user = $data['username'];
+ if (strlen($user) < 1) {
+ error_log("spp.php - Authentication failed - empty username");
+ die('Authentication failed');
+ }
+
+
+ $db = new PDO($osu_db);
+ if (!$db) {
+ error_log("spp.php - Could not access database");
+ die("Could not access database");
+ }
+ $row = $db->query("SELECT password FROM users " .
+ "WHERE identity='$user' AND realm='$realm'")->fetch();
+ if (!$row) {
+ $row = $db->query("SELECT osu_password FROM users " .
+ "WHERE osu_user='$user' AND realm='$realm'")->fetch();
+ $pw = $row['osu_password'];
+ } else
+ $pw = $row['password'];
+ if (!$row) {
+ error_log("spp.php - Authentication failed - user '$user' not found");
+ die('Authentication failed');
+ }
+ if (strlen($pw) < 1) {
+ error_log("spp.php - Authentication failed - empty password");
+ die('Authentication failed');
+ }
+
+ $A1 = md5($user . ':' . $realm . ':' . $pw);
+ $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
+ $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
+ $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
+ if ($data['response'] != $resp) {
+ error_log("Authentication failure - response mismatch");
+ die('Authentication failed');
+ }
+} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) &&
+ $_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" &&
+ isset($_SERVER["SSL_CLIENT_M_SERIAL"])) {
+ $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"];
+ putenv("HS20CERT=yes");
+} else if (!isset($_SERVER["PATH_INFO"]) ||
+ $_SERVER["PATH_INFO"] != "/signup") {
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Digest realm="'.$realm.
+ '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+ error_log("spp.php - Authentication required (not signup)");
+ die('Authentication required (not signup)');
+}
+
+
+if (isset($user) && strlen($user) > 0)
+ putenv("HS20USER=$user");
+else
+ putenv("HS20USER");
+
+putenv("HS20REALM=$realm");
+putenv("HS20POST=$HTTP_RAW_POST_DATA");
+$addr = $_SERVER["REMOTE_ADDR"];
+putenv("HS20ADDR=$addr");
+
+$last = exec("$osu_root/spp/hs20_spp_server -r$osu_root -f/tmp/hs20_spp_server.log", $output, $ret);
+
+if ($ret == 2) {
+ if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Digest realm="'.$realm.
+ '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+ error_log("spp.php - Authentication required (ret 2)");
+ die('Authentication required');
+ } else {
+ error_log("spp.php - Unexpected authentication error");
+ die("Unexpected authentication error");
+ }
+}
+if ($ret != 0) {
+ error_log("spp.php - Failed to process SPP request");
+ die("Failed to process SPP request");
+}
+//error_log("spp.php: Response: " . implode($output));
+
+header("Content-Type: application/soap+xml");
+
+echo implode($output);
+
+?>
diff --git a/hs20/server/www/users.php b/hs20/server/www/users.php
new file mode 100644
index 0000000..c340a33
--- /dev/null
+++ b/hs20/server/www/users.php
@@ -0,0 +1,349 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_GET["id"])) {
+ $id = $_GET["id"];
+ if (!is_numeric($id))
+ $id = 0;
+} else
+ $id = 0;
+if (isset($_GET["cmd"]))
+ $cmd = $_GET["cmd"];
+else
+ $cmd = '';
+
+if ($cmd == 'eventlog' && $id > 0) {
+ $row = $db->query("SELECT dump FROM eventlog WHERE rowid=$id")->fetch();
+ $dump = $row['dump'];
+ if ($dump[0] == '<') {
+ header("Content-type: text/xml");
+ echo "<?xml version=\"1.0\"?>\n";
+ echo $dump;
+ } else {
+ header("Content-type: text/plain");
+ echo $dump;
+ }
+ exit;
+}
+
+if ($cmd == 'mo' && $id > 0) {
+ $mo = $_GET["mo"];
+ if (!isset($mo))
+ exit;
+ if ($mo != "devinfo" && $mo != "devdetail" && $mo != "pps")
+ exit;
+ $row = $db->query("SELECT $mo FROM users WHERE rowid=$id")->fetch();
+ header("Content-type: text/xml");
+ echo "<?xml version=\"1.0\"?>\n";
+ echo $row[$mo];
+ exit;
+}
+
+if ($cmd == 'cert' && $id > 0) {
+ $row = $db->query("SELECT cert_pem FROM users WHERE rowid=$id")->fetch();
+ header("Content-type: text/plain");
+ echo $row['cert_pem'];
+ exit;
+}
+
+?>
+
+<html>
+<head><title>HS 2.0 users</title></head>
+<body>
+
+<?php
+
+if ($cmd == 'subrem-clear' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-user' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='user' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-machine' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='machine' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-policy' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='policy' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-free' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='free' WHERE rowid=$id");
+}
+if ($cmd == 'fetch-pps-on' && $id > 0) {
+ $db->exec("UPDATE users SET fetch_pps=1 WHERE rowid=$id");
+}
+if ($cmd == 'fetch-pps-off' && $id > 0) {
+ $db->exec("UPDATE users SET fetch_pps=0 WHERE rowid=$id");
+}
+if ($cmd == 'reset-pw' && $id > 0) {
+ $db->exec("UPDATE users SET password='ChangeMe' WHERE rowid=$id");
+}
+if ($cmd == "policy" && $id > 0 && isset($_GET["policy"])) {
+ $policy = $_GET["policy"];
+ if ($policy == "no-policy" ||
+ is_readable("$osu_root/spp/policy/$policy.xml")) {
+ $db->exec("UPDATE users SET policy='$policy' WHERE rowid=$id");
+ }
+}
+if ($cmd == "account-type" && $id > 0 && isset($_GET["type"])) {
+ $type = $_GET["type"];
+ if ($type == "shared")
+ $db->exec("UPDATE users SET shared=1 WHERE rowid=$id");
+ if ($type == "default")
+ $db->exec("UPDATE users SET shared=0 WHERE rowid=$id");
+}
+
+if ($cmd == "set-osu-cred" && $id > 0) {
+ $osu_user = $_POST["osu_user"];
+ $osu_password = $_POST["osu_password"];
+ if (strlen($osu_user) == 0)
+ $osu_password = "";
+ $db->exec("UPDATE users SET osu_user='$osu_user', osu_password='$osu_password' WHERE rowid=$id");
+}
+
+$dump = 0;
+
+if ($id > 0) {
+
+if (isset($_GET["dump"])) {
+ $dump = $_GET["dump"];
+ if (!is_numeric($dump))
+ $dump = 0;
+} else
+ $dump = 0;
+
+echo "[<a href=\"users.php\">All users</a>] ";
+if ($dump == 0)
+ echo "[<a href=\"users.php?id=$id&dump=1\">Include debug dump</a>] ";
+else
+ echo "[<a href=\"users.php?id=$id\">Without debug dump</a>] ";
+echo "<br>\n";
+
+$row = $db->query("SELECT rowid,* FROM users WHERE rowid=$id")->fetch();
+
+echo "<H3>" . $row['identity'] . "@" . $row['realm'] . "</H3>\n";
+
+echo "MO: ";
+if (strlen($row['devinfo']) > 0) {
+ echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devinfo\">DevInfo</a>]\n";
+}
+if (strlen($row['devdetail']) > 0) {
+ echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devdetail\">DevDetail</a>]\n";
+}
+if (strlen($row['pps']) > 0) {
+ echo "[<a href=\"users.php?cmd=mo&id=$id&mo=pps\">PPS</a>]\n";
+}
+if (strlen($row['cert_pem']) > 0) {
+ echo "[<a href=\"users.php?cmd=cert&id=$id\">Certificate</a>]\n";
+}
+echo "<BR>\n";
+
+echo "Fetch PPS MO: ";
+if ($row['fetch_pps'] == "1") {
+ echo "On next connection " .
+ "[<a href=\"users.php?cmd=fetch-pps-off&id=$id\">" .
+ "do not fetch</a>]<br>\n";
+} else {
+ echo "Do not fetch " .
+ "[<a href=\"users.php?cmd=fetch-pps-on&id=$id\">" .
+ "request fetch</a>]<br>\n";
+}
+
+$cert = $row['cert'];
+if (strlen($cert) > 0) {
+ echo "Certificate fingerprint: $cert<br>\n";
+}
+
+echo "Remediation: ";
+$rem = $row['remediation'];
+if ($rem == "") {
+ echo "Not required";
+ echo " [<a href=\"users.php?cmd=subrem-add-user&id=" .
+ $row['rowid'] . "\">add:user</a>]";
+ echo " [<a href=\"users.php?cmd=subrem-add-machine&id=" .
+ $row['rowid'] . "\">add:machine</a>]";
+ echo " [<a href=\"users.php?cmd=subrem-add-policy&id=" .
+ $row['rowid'] . "\">add:policy</a>]";
+ echo " [<a href=\"users.php?cmd=subrem-add-free&id=" .
+ $row['rowid'] . "\">add:free</a>]";
+} else if ($rem == "user") {
+ echo "User [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+} else if ($rem == "policy") {
+ echo "Policy [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+} else if ($rem == "free") {
+ echo "Free [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+} else {
+ echo "Machine [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+}
+echo "<br>\n";
+
+echo "<form>Policy: <select name=\"policy\" " .
+ "onChange=\"window.location='users.php?cmd=policy&id=" .
+ $row['rowid'] . "&policy=' + this.value;\">\n";
+echo "<option value=\"" . $row['policy'] . "\" selected>" . $row['policy'] .
+ "</option>\n";
+$files = scandir("$osu_root/spp/policy");
+foreach ($files as $file) {
+ if (!preg_match("/.xml$/", $file))
+ continue;
+ if ($file == $row['policy'] . ".xml")
+ continue;
+ $p = substr($file, 0, -4);
+ echo "<option value=\"$p\">$p</option>\n";
+}
+echo "<option value=\"no-policy\">no policy</option>\n";
+echo "</select></form>\n";
+
+echo "<form>Account type: <select name=\"type\" " .
+ "onChange=\"window.location='users.php?cmd=account-type&id=" .
+ $row['rowid'] . "&type=' + this.value;\">\n";
+if ($row['shared'] > 0) {
+ $default_sel = "";
+ $shared_sel = " selected";
+} else {
+ $default_sel = " selected";
+ $shared_sel = "";
+}
+echo "<option value=\"default\"$default_sel>default</option>\n";
+echo "<option value=\"shared\"$shared_sel>shared</option>\n";
+echo "</select></form>\n";
+
+echo "Phase 2 method(s): " . $row['methods'] . "<br>\n";
+
+echo "<br>\n";
+echo "<a href=\"users.php?cmd=reset-pw&id=" .
+ $row['rowid'] . "\">Reset AAA password</a><br>\n";
+
+echo "<br>\n";
+echo "<form action=\"users.php?cmd=set-osu-cred&id=" . $row['rowid'] .
+ "\" method=\"POST\">\n";
+echo "OSU credentials (if username empty, AAA credentials are used):<br>\n";
+echo "username: <input type=\"text\" name=\"osu_user\" value=\"" .
+ $row['osu_user'] . "\">\n";
+echo "password: <input type=\"password\" name=\"osu_password\">\n";
+echo "<input type=\"submit\" value=\"Set OSU credentials\">\n";
+echo "</form>\n";
+
+echo "<hr>\n";
+
+$user = $row['identity'];
+$osu_user = $row['osu_user'];
+$realm = $row['realm'];
+}
+
+if ($id > 0 || ($id == 0 && $cmd == 'eventlog')) {
+
+ if ($id == 0) {
+ echo "[<a href=\"users.php\">All users</a>] ";
+ echo "<br>\n";
+ }
+
+echo "<table border=1>\n";
+echo "<tr>";
+if ($id == 0) {
+ echo "<th>user<th>realm";
+}
+echo "<th>time<th>address<th>sessionID<th>notes";
+if ($dump > 0)
+ echo "<th>dump";
+echo "\n";
+if (isset($_GET["limit"])) {
+ $limit = $_GET["limit"];
+ if (!is_numeric($limit))
+ $limit = 20;
+} else
+ $limit = 20;
+if ($id == 0)
+ $res = $db->query("SELECT rowid,* FROM eventlog ORDER BY timestamp DESC LIMIT $limit");
+else if (strlen($osu_user) > 0)
+ $res = $db->query("SELECT rowid,* FROM eventlog WHERE (user='$user' OR user='$osu_user') AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
+else
+ $res = $db->query("SELECT rowid,* FROM eventlog WHERE user='$user' AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
+foreach ($res as $row) {
+ echo "<tr>";
+ if ($id == 0) {
+ echo "<td>" . $row['user'] . "\n";
+ echo "<td>" . $row['realm'] . "\n";
+ }
+ echo "<td>" . $row['timestamp'] . "\n";
+ echo "<td>" . $row['addr'] . "\n";
+ echo "<td>" . $row['sessionid'] . "\n";
+ echo "<td>" . $row['notes'] . "\n";
+ $d = $row['dump'];
+ if (strlen($d) > 0) {
+ echo "[<a href=\"users.php?cmd=eventlog&id=" . $row['rowid'] .
+ "\">";
+ if ($d[0] == '<')
+ echo "XML";
+ else
+ echo "txt";
+ echo "</a>]\n";
+ if ($dump > 0)
+ echo "<td>" . htmlspecialchars($d) . "\n";
+ }
+}
+echo "</table>\n";
+
+}
+
+
+if ($id == 0 && $cmd != 'eventlog') {
+
+echo "[<a href=\"users.php?cmd=eventlog&limit=50\">Eventlog</a>] ";
+echo "<br>\n";
+
+echo "<table border=1>\n";
+echo "<tr><th>User<th>Realm<th>Remediation<th>Policy<th>Account type<th>Phase 2 method(s)<th>DevId\n";
+
+$res = $db->query('SELECT rowid,* FROM users WHERE phase2=1');
+foreach ($res as $row) {
+ echo "<tr><td><a href=\"users.php?id=" . $row['rowid'] . "\"> " .
+ $row['identity'] . " </a>";
+ echo "<td>" . $row['realm'];
+ $rem = $row['remediation'];
+ echo "<td>";
+ if ($rem == "") {
+ echo "Not required";
+ } else if ($rem == "user") {
+ echo "User";
+ } else if ($rem == "policy") {
+ echo "Policy";
+ } else if ($rem == "free") {
+ echo "Free";
+ } else {
+ echo "Machine";
+ }
+ echo "<td>" . $row['policy'];
+ if ($row['shared'] > 0)
+ echo "<td>shared";
+ else
+ echo "<td>default";
+ echo "<td>" . $row['methods'];
+ echo "<td>";
+ $xml = xml_parser_create();
+ xml_parse_into_struct($xml, $row['devinfo'], $devinfo);
+ foreach($devinfo as $k) {
+ if ($k['tag'] == 'DEVID') {
+ echo $k['value'];
+ break;
+ }
+ }
+ echo "\n";
+}
+echo "</table>\n";
+
+}
+
+?>
+
+</html>
diff --git a/src/Makefile b/src/Makefile
index d73a175..10e0171 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
all:
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 9c41962..adfd3df 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 9540531..6290d3f 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -10,7 +10,6 @@
#include "utils/common.h"
#include "utils/eloop.h"
-#include "drivers/driver.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "hostapd.h"
@@ -44,7 +43,7 @@
msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
radius_client_get_id(hapd->radius));
if (msg == NULL) {
- printf("Could not create net RADIUS packet\n");
+ wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
return NULL;
}
@@ -55,7 +54,7 @@
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))) {
- printf("Could not add Acct-Session-Id\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Session-Id");
goto fail;
}
} else {
@@ -64,7 +63,7 @@
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
status_type)) {
- printf("Could not add Acct-Status-Type\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
goto fail;
}
@@ -74,7 +73,7 @@
hapd->conf->ieee802_1x ?
RADIUS_ACCT_AUTHENTIC_RADIUS :
RADIUS_ACCT_AUTHENTIC_LOCAL)) {
- printf("Could not add Acct-Authentic\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
goto fail;
}
@@ -99,7 +98,7 @@
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
len)) {
- printf("Could not add User-Name\n");
+ wpa_printf(MSG_INFO, "Could not add User-Name");
goto fail;
}
}
@@ -117,7 +116,7 @@
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
val, len)) {
- printf("Could not add Class\n");
+ wpa_printf(MSG_INFO, "Could not add Class");
goto fail;
}
}
@@ -202,7 +201,6 @@
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
{
struct radius_msg *msg;
- struct os_time t;
int interval;
if (sta->acct_session_started)
@@ -213,8 +211,7 @@
"starting accounting session %08X-%08X",
sta->acct_session_id_hi, sta->acct_session_id_lo);
- os_get_time(&t);
- sta->acct_session_start = t.sec;
+ 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);
@@ -244,6 +241,7 @@
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;
@@ -254,14 +252,16 @@
stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
if (!msg) {
- printf("Could not create RADIUS Accounting message\n");
+ 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,
- now.sec - sta->acct_session_start)) {
- printf("Could not add Acct-Session-Time\n");
+ diff.sec)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
goto fail;
}
@@ -269,19 +269,19 @@
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_INPUT_PACKETS,
data.rx_packets)) {
- printf("Could not add Acct-Input-Packets\n");
+ 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)) {
- printf("Could not add Acct-Output-Packets\n");
+ 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)) {
- printf("Could not add Acct-Input-Octets\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
goto fail;
}
gigawords = sta->acct_input_gigawords;
@@ -292,13 +292,13 @@
!radius_msg_add_attr_int32(
msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
gigawords)) {
- printf("Could not add Acct-Input-Gigawords\n");
+ 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)) {
- printf("Could not add Acct-Output-Octets\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
goto fail;
}
gigawords = sta->acct_output_gigawords;
@@ -309,14 +309,14 @@
!radius_msg_add_attr_int32(
msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
gigawords)) {
- printf("Could not add Acct-Output-Gigawords\n");
+ 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)) {
- printf("Could not add Event-Timestamp\n");
+ wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
goto fail;
}
@@ -326,7 +326,7 @@
if (stop && cause &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
cause)) {
- printf("Could not add Acct-Terminate-Cause\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
goto fail;
}
@@ -400,13 +400,12 @@
void *data)
{
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
- printf("Unknown RADIUS message code\n");
+ wpa_printf(MSG_INFO, "Unknown RADIUS message code");
return RADIUS_RX_UNKNOWN;
}
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
- printf("Incoming RADIUS packet did not have correct "
- "Authenticator - dropped\n");
+ wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
}
@@ -432,7 +431,7 @@
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
{
- printf("Could not add Acct-Terminate-Cause\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
radius_msg_free(msg);
return;
}
diff --git a/src/ap/acs.c b/src/ap/acs.c
index d5e3f59..b94b8a4 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -13,6 +13,7 @@
#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"
@@ -279,10 +280,11 @@
}
-void acs_fail(struct hostapd_iface *iface)
+static void acs_fail(struct hostapd_iface *iface)
{
wpa_printf(MSG_ERROR, "ACS: Failed to start");
acs_cleanup(iface);
+ hostapd_disable_iface(iface);
}
@@ -352,23 +354,26 @@
}
-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;
- return 1;
-}
-
-
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 < sizeof(allowed) / sizeof(allowed[0]); i++)
+ for (i = 0; i < ARRAY_SIZE(allowed); i++)
+ if (chan->chan == allowed[i])
+ return 1;
+
+ return 0;
+}
+
+
+static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+{
+ const int allowed[] = { 36, 52, 100, 116, 132, 149 };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(allowed); i++)
if (chan->chan == allowed[i])
return 1;
@@ -398,28 +403,54 @@
}
+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;
- struct freq_survey *survey;
+ 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;
- 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;
- }
- }
+ 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;
}
@@ -456,7 +487,7 @@
for (i = 0; i < iface->current_mode->num_channels; i++) {
chan = &iface->current_mode->channels[i];
- if (!acs_usable_chan(chan))
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
if (chan->freq == freq)
@@ -476,7 +507,8 @@
static struct hostapd_channel_data *
acs_find_ideal_chan(struct hostapd_iface *iface)
{
- struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL;
+ 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;
@@ -508,9 +540,10 @@
for (i = 0; i < iface->current_mode->num_channels; i++) {
chan = &iface->current_mode->channels[i];
- if (!acs_usable_chan(chan))
+ 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 &&
@@ -522,14 +555,26 @@
continue;
}
- factor = chan->interference_factor;
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ iface->conf->ieee80211ac &&
+ iface->conf->vht_oper_chwidth == 1 &&
+ !acs_usable_vht80_chan(chan)) {
+ wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
+ 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;
- factor += adj_chan->interference_factor;
+ if (acs_usable_chan(adj_chan))
+ factor += adj_chan->interference_factor;
}
if (j != n_chans) {
@@ -548,22 +593,22 @@
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) - 5);
- if (adj_chan)
+ 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)
+ 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)
+ 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)
+ if (adj_chan && acs_usable_chan(adj_chan))
factor += adj_chan->interference_factor;
}
}
@@ -571,39 +616,49 @@
wpa_printf(MSG_DEBUG, "ACS: * channel %d: total interference = %Lg",
chan->chan, factor);
- if (!ideal_chan || factor < ideal_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)
+ 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 ideal_chan;
+ return rand_chan;
}
static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
{
+ int offset;
+
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;
+ offset = 2 * iface->conf->secondary_channel;
break;
case VHT_CHANWIDTH_80MHZ:
- iface->conf->vht_oper_centr_freq_seg0_idx =
- iface->conf->channel + 6;
+ offset = 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;
+ return;
}
+
+ iface->conf->vht_oper_centr_freq_seg0_idx =
+ iface->conf->channel + offset;
}
@@ -655,6 +710,7 @@
ideal_chan = acs_find_ideal_chan(iface);
if (!ideal_chan) {
wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+ err = -1;
goto fail;
}
@@ -663,24 +719,20 @@
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 :)
*/
- switch (hostapd_acs_completed(iface)) {
- case HOSTAPD_CHAN_VALID:
+ if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
acs_cleanup(iface);
return;
- case HOSTAPD_CHAN_INVALID:
- case HOSTAPD_CHAN_ACS:
- default:
- /* 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.");
- goto fail;
}
-fail:
+ /* 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);
}
@@ -697,21 +749,24 @@
err = hostapd_drv_get_survey(iface->bss[0], 0);
if (err) {
wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
- acs_fail(iface);
+ goto fail;
}
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");
- acs_fail(iface);
- return;
+ goto fail;
}
return;
}
acs_study(iface);
+ return;
+fail:
+ hostapd_acs_completed(iface, 1);
+ acs_fail(iface);
}
@@ -746,6 +801,7 @@
if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
acs_cleanup(iface);
+ os_free(params.freqs);
return -1;
}
@@ -766,5 +822,8 @@
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;
}
diff --git a/src/ap/acs.h b/src/ap/acs.h
index 0d1d0f1..fc85259 100644
--- a/src/ap/acs.h
+++ b/src/ap/acs.h
@@ -13,7 +13,6 @@
#ifdef CONFIG_ACS
enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
-int hostapd_acs_completed(struct hostapd_iface *iface);
#else /* CONFIG_ACS */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index caf75c4..d127550 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration helper functions
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -73,6 +73,7 @@
#ifdef CONFIG_IEEE80211W
bss->assoc_sa_query_max_timeout = 1000;
bss->assoc_sa_query_retry_timeout = 201;
+ bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
#endif /* CONFIG_IEEE80211W */
#ifdef EAP_SERVER_FAST
/* both anonymous and authenticated provisioning */
@@ -106,9 +107,9 @@
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 };
+ { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
- { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+ { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
const struct hostapd_tx_queue_params txq_bk =
{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
const struct hostapd_tx_queue_params txq_be =
@@ -130,9 +131,17 @@
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;
@@ -141,12 +150,13 @@
hostapd_config_defaults_bss(bss);
conf->num_bss = 1;
- conf->bss = bss;
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;
+ /* Set to invalid value means do not add Power Constraint IE */
+ conf->local_pwr_constraint = -1;
conf->wmm_ac_params[0] = ac_be;
conf->wmm_ac_params[1] = ac_bk;
@@ -164,11 +174,11 @@
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;
+ conf->ignore_probe_probability = 0.0;
+ conf->ignore_auth_probability = 0.0;
+ conf->ignore_assoc_probability = 0.0;
+ conf->ignore_reassoc_probability = 0.0;
+ conf->corrupt_gtk_rekey_mic_probability = 0.0;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_ACS
@@ -329,20 +339,6 @@
}
-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)
{
@@ -379,10 +375,11 @@
}
-static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
{
+ hostapd_config_free_radius_attr(user->accept_attr);
os_free(user->identity);
- os_free(user->password);
+ bin_clear_free(user->password, user->password_len);
os_free(user);
}
@@ -391,13 +388,13 @@
{
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
- os_free(keys->key[i]);
+ bin_clear_free(keys->key[i], keys->len[i]);
keys->key[i] = NULL;
}
}
-static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
struct hostapd_wpa_psk *psk, *prev;
struct hostapd_eap_user *user, *prev_user;
@@ -409,10 +406,10 @@
while (psk) {
prev = psk;
psk = psk->next;
- os_free(prev);
+ bin_clear_free(prev, sizeof(*prev));
}
- os_free(conf->ssid.wpa_passphrase);
+ str_clear_free(conf->ssid.wpa_passphrase);
os_free(conf->ssid.wpa_psk_file);
hostapd_config_free_wep(&conf->ssid.wep);
#ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -427,15 +424,16 @@
}
os_free(conf->eap_user_sqlite);
- os_free(conf->dump_log_name);
os_free(conf->eap_req_id_text);
os_free(conf->accept_mac);
os_free(conf->deny_mac);
os_free(conf->nas_identifier);
- 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);
+ 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);
@@ -518,13 +516,36 @@
os_free(conf->hs20_wan_metrics);
os_free(conf->hs20_connection_capability);
os_free(conf->hs20_operating_class);
+ os_free(conf->hs20_icons);
+ if (conf->hs20_osu_providers) {
+ size_t i;
+ for (i = 0; i < conf->hs20_osu_providers_count; i++) {
+ struct hs20_osu_provider *p;
+ size_t j;
+ p = &conf->hs20_osu_providers[i];
+ os_free(p->friendly_name);
+ os_free(p->server_uri);
+ os_free(p->method_list);
+ for (j = 0; j < p->icons_count; j++)
+ os_free(p->icons[j]);
+ os_free(p->icons);
+ os_free(p->osu_nai);
+ os_free(p->service_desc);
+ }
+ os_free(conf->hs20_osu_providers);
+ }
+ os_free(conf->subscr_remediation_url);
#endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements);
os_free(conf->sae_groups);
+ os_free(conf->wowlan_triggers);
+
os_free(conf->server_id);
+
+ os_free(conf);
}
@@ -540,10 +561,11 @@
return;
for (i = 0; i < conf->num_bss; i++)
- hostapd_config_free_bss(&conf->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->chanlist);
os_free(conf);
}
@@ -631,12 +653,11 @@
struct hostapd_wpa_psk *psk;
int next_ok = prev_psk == NULL;
- if (p2p_dev_addr) {
+ if (p2p_dev_addr && !is_zero_ether_addr(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 */
+ addr = NULL; /* Use P2P Device Address for matching */
} else {
wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
" prev_psk=%p",
@@ -658,3 +679,238 @@
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_WPS
+ 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_WPS */
+
+#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;
+ }
+
+ if (full_config && conf->local_pwr_constraint != -1 &&
+ !conf->ieee80211d) {
+ wpa_printf(MSG_ERROR, "Cannot add Power Constraint element without Country element");
+ return -1;
+ }
+
+ if (full_config && conf->spectrum_mgmt_required &&
+ conf->local_pwr_constraint == -1) {
+ wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements");
+ 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,
+ int full_config)
+{
+ 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);
+
+ if (full_config) {
+ 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 if (bss->osen) {
+ bss->ssid.security_policy = SECURITY_OSEN;
+ bss->wpa_group = WPA_CIPHER_CCMP;
+ bss->wpa_pairwise = 0;
+ bss->rsn_pairwise = WPA_CIPHER_CCMP;
+ } 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;
+ }
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c5531fa..2858c6e 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -45,7 +45,8 @@
SECURITY_STATIC_WEP = 1,
SECURITY_IEEE_802_1X = 2,
SECURITY_WPA_PSK = 3,
- SECURITY_WPA = 4
+ SECURITY_WPA = 4,
+ SECURITY_OSEN = 5
} secpolicy;
struct hostapd_ssid {
@@ -53,6 +54,8 @@
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;
@@ -123,7 +126,10 @@
unsigned int wildcard_prefix:1;
unsigned int password_hash:1; /* whether password is hashed with
* nt_password_hash() */
+ unsigned int remediation:1;
+ unsigned int macacl:1;
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+ struct hostapd_radius_attr *accept_attr;
};
struct hostapd_radius_attr {
@@ -187,8 +193,6 @@
unsigned int logger_syslog; /* module bitfield */
unsigned int logger_stdout; /* module bitfield */
- char *dump_log_name; /* file name for state dump (SIGUSR1) */
-
int max_num_sta; /* maximum number of STAs in station table */
int dtim_period;
@@ -251,6 +255,7 @@
int wpa_key_mgmt;
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
+ int group_mgmt_cipher;
/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
unsigned int assoc_sa_query_max_timeout;
/* dot11AssociationSAQueryRetryTimeout (in TUs) */
@@ -311,6 +316,7 @@
char *radius_server_clients;
int radius_server_auth_port;
+ int radius_server_acct_port;
int radius_server_ipv6;
char *test_socket; /* UNIX domain socket path for driver_test */
@@ -384,6 +390,12 @@
#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;
@@ -442,9 +454,14 @@
u16 gas_comeback_delay;
int gas_frag_limit;
+ u8 qos_map_set[16 + 2 * 21];
+ unsigned int qos_map_set_len;
+
+ int osen;
#ifdef CONFIG_HS20
int hs20;
int disable_dgaf;
+ u16 anqp_domain_id;
unsigned int hs20_oper_friendly_name_count;
struct hostapd_lang_string *hs20_oper_friendly_name;
u8 *hs20_wan_metrics;
@@ -452,6 +469,32 @@
size_t hs20_connection_capability_len;
u8 *hs20_operating_class;
u8 hs20_operating_class_len;
+ struct hs20_icon {
+ u16 width;
+ u16 height;
+ char language[3];
+ char type[256];
+ char name[256];
+ char file[256];
+ } *hs20_icons;
+ size_t hs20_icons_count;
+ u8 osu_ssid[HOSTAPD_MAX_SSID_LEN];
+ size_t osu_ssid_len;
+ struct hs20_osu_provider {
+ unsigned int friendly_name_count;
+ struct hostapd_lang_string *friendly_name;
+ char *server_uri;
+ int *method_list;
+ char **icons;
+ size_t icons_count;
+ char *osu_nai;
+ unsigned int service_desc_count;
+ struct hostapd_lang_string *service_desc;
+ } *hs20_osu_providers, *last_osu;
+ size_t hs20_osu_providers_count;
+ unsigned int hs20_deauth_req_timeout;
+ char *subscr_remediation_url;
+ u8 subscr_remediation_method;
#endif /* CONFIG_HS20 */
u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
@@ -464,6 +507,13 @@
unsigned int sae_anti_clogging_threshold;
int *sae_groups;
+
+ char *wowlan_triggers; /* Wake-on-WLAN triggers */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 bss_load_test[5];
+ u8 bss_load_test_set;
+#endif /* CONFIG_TESTING_OPTIONS */
};
@@ -471,7 +521,7 @@
* struct hostapd_config - Per-radio interface configuration
*/
struct hostapd_config {
- struct hostapd_bss_config *bss, *last_bss;
+ struct hostapd_bss_config **bss, *last_bss;
size_t num_bss;
u16 beacon_int;
@@ -479,6 +529,7 @@
int fragm_threshold;
u8 send_probe_response;
u8 channel;
+ int *chanlist;
enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
enum {
LONG_PREAMBLE = 0,
@@ -504,6 +555,16 @@
int ieee80211h; /* DFS */
+ /*
+ * Local power constraint is an octet encoded as an unsigned integer in
+ * units of decibels. Invalid value -1 indicates that Power Constraint
+ * element will not be added.
+ */
+ int local_pwr_constraint;
+
+ /* Control Spectrum Management bit */
+ int spectrum_mgmt_required;
+
struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
/*
@@ -520,6 +581,7 @@
int ieee80211n;
int secondary_channel;
int require_ht;
+ int obss_interval;
u32 vht_capab;
int ieee80211ac;
int require_vht;
@@ -545,12 +607,12 @@
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_eap_user(struct hostapd_eap_user *user);
+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);
@@ -560,5 +622,8 @@
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,
+ int full_config);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 3072562..cc4ac10 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -9,7 +9,6 @@
#include "utils/includes.h"
#include "utils/common.h"
-#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
#include "wps/wps.h"
#include "p2p/p2p.h"
@@ -129,14 +128,14 @@
}
#endif /* CONFIG_P2P_MANAGER */
-#ifdef CONFIG_WPS2
+#ifdef CONFIG_WPS
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 */
+#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P_MANAGER
if (hapd->conf->p2p & P2P_MANAGE) {
@@ -171,6 +170,17 @@
goto fail;
wpabuf_put_data(proberesp, buf, pos - buf);
}
+
+ pos = hostapd_eid_osen(hapd, buf);
+ 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) {
@@ -270,7 +280,8 @@
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_pairwise = hapd->conf->wpa_pairwise |
+ hapd->conf->rsn_pairwise;
params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
params.rsn_preauth = hapd->conf->rsn_preauth;
#ifdef CONFIG_IEEE80211W
@@ -286,7 +297,7 @@
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);
+ NULL, NULL, force_ifname, if_addr, NULL, 0);
}
@@ -347,7 +358,7 @@
u16 listen_interval,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
- u32 flags, u8 qosinfo)
+ u32 flags, u8 qosinfo, u8 vht_opmode)
{
struct hostapd_sta_add_params params;
@@ -365,6 +376,8 @@
params.listen_interval = listen_interval;
params.ht_capabilities = ht_capab;
params.vht_capabilities = vht_capab;
+ params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
+ params.vht_opmode = vht_opmode;
params.flags = hostapd_sta_flags_to_drv(flags);
params.qosinfo = qosinfo;
return hapd->driver->sta_add(hapd->drv_priv, ¶ms);
@@ -417,20 +430,21 @@
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)
+ 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);
+ 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->driver->if_remove == NULL)
+ 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);
}
@@ -463,44 +477,51 @@
}
-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_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)
{
- struct hostapd_freq_params data;
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;
+ 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) {
+ 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)
+ if (center_segment0 != 0 &&
+ 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;
+ data->center_freq2 = 5000 + center_segment1 * 5;
/* fall through */
case VHT_CHANWIDTH_80MHZ:
- data.bandwidth = 80;
+ data->bandwidth = 80;
if (vht_oper_chwidth == 1 && center_segment1)
return -1;
if (vht_oper_chwidth == 3 && !center_segment1)
@@ -510,13 +531,19 @@
/* primary 40 part must match the HT configuration */
tmp = (30 + freq - 5000 - center_segment0 * 5)/20;
tmp /= 2;
- if (data.center_freq1 != 5000 +
+ if (data->center_freq1 != 5000 +
center_segment0 * 5 - 20 + 40 * tmp)
return -1;
- data.center_freq1 = 5000 + center_segment0 * 5;
+ data->center_freq1 = 5000 + center_segment0 * 5;
break;
case VHT_CHANWIDTH_160MHZ:
- data.bandwidth = 160;
+ 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)
@@ -524,12 +551,31 @@
/* primary 40 part must match the HT configuration */
tmp = (70 + freq - 5000 - center_segment0 * 5)/20;
tmp /= 2;
- if (data.center_freq1 != 5000 +
+ if (data->center_freq1 != 5000 +
center_segment0 * 5 - 60 + 40 * tmp)
return -1;
- data.center_freq1 = 5000 + center_segment0 * 5;
+ 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)
@@ -683,7 +729,7 @@
const u8 *peer, u8 *buf, u16 *buf_len)
{
if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
- return 0;
+ return -1;
return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
buf_len);
}
@@ -699,3 +745,50 @@
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)) {
+ wpa_printf(MSG_ERROR, "Can't set freq params");
+ return -1;
+ }
+
+ res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+ if (!res) {
+ iface->cac_started = 1;
+ os_get_reltime(&iface->dfs_cac_start);
+ }
+
+ 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);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 23277b6..7cc9d7d 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -1,6 +1,6 @@
/*
* hostapd - Driver operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,6 +14,7 @@
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,
@@ -39,7 +40,7 @@
u16 listen_interval,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
- u32 flags, u8 qosinfo);
+ u32 flags, u8 qosinfo, u8 vht_opmode);
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);
@@ -48,7 +49,7 @@
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);
+ 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,
@@ -101,6 +102,15 @@
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"
@@ -109,6 +119,9 @@
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)
{
@@ -235,4 +248,47 @@
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);
+}
+
+static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+ int vendor_id, int subcmd,
+ const u8 *data, size_t data_len,
+ struct wpabuf *buf)
+{
+ if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
+ return -1;
+ return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
+ data_len, buf);
+}
+
#endif /* AP_DRV_OPS */
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 9f02151..287d520 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -14,7 +14,6 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -33,7 +32,8 @@
{
int i;
- if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+ if (iface->current_mode == NULL ||
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
iface->conf->channel != ap->channel)
return 0;
@@ -172,7 +172,6 @@
struct hostapd_frame_info *fi)
{
struct ap_info *ap;
- struct os_time now;
int new_ap = 0;
int set_beacon = 0;
@@ -210,8 +209,7 @@
else
ap->ht_support = 0;
- os_get_time(&now);
- ap->last_beacon = now.sec;
+ 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
@@ -252,7 +250,7 @@
static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_iface *iface = eloop_ctx;
- struct os_time now;
+ struct os_reltime now;
struct ap_info *ap;
int set_beacon = 0;
@@ -261,12 +259,12 @@
if (!iface->ap_list)
return;
- os_get_time(&now);
+ os_get_reltime(&now);
while (iface->ap_list) {
ap = iface->ap_list->prev;
- if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
- now.sec)
+ if (!os_reltime_expired(&now, &ap->last_beacon,
+ iface->conf->ap_table_expiration_time))
break;
ap_free_ap(iface, ap);
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
index d0529a1..93dc0ed 100644
--- a/src/ap/ap_list.h
+++ b/src/ap/ap_list.h
@@ -26,7 +26,7 @@
int ht_support;
- os_time_t last_beacon;
+ struct os_reltime last_beacon;
};
struct ieee802_11_elems;
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index a959694..13604ed 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -16,6 +16,7 @@
#include "wpa_auth.h"
#include "sta_info.h"
#include "ap_mlme.h"
+#include "hostapd.h"
#ifndef CONFIG_NO_HOSTAPD_LOGGER
@@ -80,7 +81,8 @@
HOSTAPD_LEVEL_DEBUG,
"MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
MAC2STR(sta->addr), reason_code);
- mlme_deletekeys_request(hapd, sta);
+ if (!hapd->iface->driver_ap_teardown)
+ mlme_deletekeys_request(hapd, sta);
}
@@ -118,8 +120,6 @@
* 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)
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 68ad4dc..86f1cbe 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -79,7 +79,10 @@
user->password_hash = eap_user->password_hash;
}
user->force_version = eap_user->force_version;
+ user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth;
+ user->remediation = eap_user->remediation;
+ user->accept_attr = eap_user->accept_attr;
return 0;
}
@@ -92,6 +95,7 @@
os_memset(&srv, 0, sizeof(srv));
srv.client_file = conf->radius_server_clients;
srv.auth_port = conf->radius_server_auth_port;
+ srv.acct_port = conf->radius_server_acct_port;
srv.conf_ctx = hapd;
srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
srv.ssl_ctx = hapd->ssl_ctx;
@@ -112,9 +116,14 @@
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";
+ srv.sqlite_file = conf->eap_user_sqlite;
#ifdef CONFIG_RADIUS_TEST
srv.dump_msk_file = conf->dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_HS20
+ srv.subscr_remediation_url = conf->subscr_remediation_url;
+ srv.subscr_remediation_method = conf->subscr_remediation_method;
+#endif /* CONFIG_HS20 */
hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) {
@@ -133,7 +142,7 @@
#ifdef EAP_TLS_FUNCS
if (hapd->conf->eap_server &&
(hapd->conf->ca_cert || hapd->conf->server_cert ||
- hapd->conf->dh_file)) {
+ hapd->conf->private_key || hapd->conf->dh_file)) {
struct tls_connection_params params;
hapd->ssl_ctx = tls_init(NULL);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 2f4ba23..b3b6149 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -4,14 +4,8 @@
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -21,7 +15,6 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
#include "wps/wps_defs.h"
#include "p2p/p2p.h"
#include "hostapd.h"
@@ -34,10 +27,27 @@
#include "ap_drv_ops.h"
#include "beacon.h"
#include "hs20.h"
+#include "dfs.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;
@@ -93,6 +103,70 @@
}
+static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 local_pwr_constraint = 0;
+ int dfs;
+
+ if (hapd->iface->current_mode == NULL ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ /*
+ * There is no DFS support and power constraint was not directly
+ * requested by config option.
+ */
+ if (!hapd->iconf->ieee80211h &&
+ hapd->iconf->local_pwr_constraint == -1)
+ return eid;
+
+ /* Check if DFS is required by regulatory. */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+ if (dfs < 0) {
+ wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+ dfs);
+ dfs = 0;
+ }
+
+ if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1)
+ return eid;
+
+ /*
+ * ieee80211h (DFS) is enabled so Power Constraint element shall
+ * be added when running on DFS channel whenever local_pwr_constraint
+ * is configured or not. In order to meet regulations when TPC is not
+ * implemented using a transmit power that is below the legal maximum
+ * (including any mitigation factor) should help. In this case,
+ * indicate 3 dB below maximum allowed transmit power.
+ */
+ if (hapd->iconf->local_pwr_constraint == -1)
+ local_pwr_constraint = 3;
+
+ /*
+ * A STA that is not an AP shall use a transmit power less than or
+ * equal to the local maximum transmit power level for the channel.
+ * The local maximum transmit power can be calculated from the formula:
+ * local max TX pwr = max TX pwr - local pwr constraint
+ * Where max TX pwr is maximum transmit power level specified for
+ * channel in Country element and local pwr constraint is specified
+ * for channel in this Power Constraint element.
+ */
+
+ /* Element ID */
+ *pos++ = WLAN_EID_PWR_CONSTRAINT;
+ /* Length */
+ *pos++ = 1;
+ /* Local Power Constraint */
+ if (local_pwr_constraint)
+ *pos++ = local_pwr_constraint;
+ else
+ *pos++ = hapd->iconf->local_pwr_constraint;
+
+ return pos;
+}
+
+
static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
struct hostapd_channel_data *start,
struct hostapd_channel_data *prev)
@@ -146,7 +220,7 @@
continue; /* can use same entry */
}
- if (start) {
+ if (start && prev) {
pos = hostapd_eid_country_add(pos, end, chan_spacing,
start, prev);
start = NULL;
@@ -187,6 +261,70 @@
}
+static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 chan;
+
+ if (!hapd->cs_freq_params.freq)
+ return eid;
+
+ if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
+ NUM_HOSTAPD_MODES)
+ return eid;
+
+ *eid++ = WLAN_EID_CHANNEL_SWITCH;
+ *eid++ = 3;
+ *eid++ = hapd->cs_block_tx;
+ *eid++ = chan;
+ *eid++ = hapd->cs_count;
+
+ return eid;
+}
+
+
+static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 sec_ch;
+
+ if (!hapd->cs_freq_params.sec_channel_offset)
+ return eid;
+
+ if (hapd->cs_freq_params.sec_channel_offset == -1)
+ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
+ else if (hapd->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,
@@ -242,6 +380,9 @@
pos = hostapd_eid_country(hapd, pos, epos - pos);
+ /* Power Constraint element */
+ pos = hostapd_eid_pwr_constraint(hapd, pos);
+
/* ERP Information element */
pos = hostapd_eid_erp_info(hapd, pos);
@@ -251,6 +392,8 @@
/* 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);
@@ -265,6 +408,8 @@
pos = hostapd_eid_adv_proto(hapd, pos);
pos = hostapd_eid_roaming_consortium(hapd, pos);
+ pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
+ &hapd->cs_c_off_proberesp);
#ifdef CONFIG_IEEE80211AC
pos = hostapd_eid_vht_capabilities(hapd, pos);
pos = hostapd_eid_vht_operation(hapd, pos);
@@ -297,6 +442,7 @@
#ifdef CONFIG_HS20
pos = hostapd_eid_hs20_indication(hapd, pos);
+ pos = hostapd_eid_osen(hapd, pos);
#endif /* CONFIG_HS20 */
if (hapd->conf->vendor_elements) {
@@ -443,12 +589,10 @@
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->sa),
+ wpa_ssid_txt(elems.ssid, elems.ssid_len),
MAC2STR(mgmt->da),
elems.ssid_list ? " (SSID list)" : "");
}
@@ -456,7 +600,8 @@
}
#ifdef CONFIG_INTERWORKING
- if (elems.interworking && elems.interworking_len >= 1) {
+ if (hapd->conf->interworking &&
+ elems.interworking && elems.interworking_len >= 1) {
u8 ant = elems.interworking[0] & 0x0f;
if (ant != INTERWORKING_ANT_WILDCARD &&
ant != hapd->conf->access_network_type) {
@@ -467,7 +612,7 @@
}
}
- if (elems.interworking &&
+ if (hapd->conf->interworking && elems.interworking &&
(elems.interworking_len == 7 || elems.interworking_len == 9)) {
const u8 *hessid;
if (elems.interworking_len == 7)
@@ -500,7 +645,7 @@
* with AP configuration */
#ifdef CONFIG_TESTING_OPTIONS
- if (hapd->iconf->ignore_probe_probability > 0.0d &&
+ if (hapd->iconf->ignore_probe_probability > 0.0 &&
drand48() < hapd->iconf->ignore_probe_probability) {
wpa_printf(MSG_INFO,
"TESTING: ignoring probe request from " MACSTR,
@@ -522,7 +667,7 @@
is_broadcast_ether_addr(mgmt->da));
if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
- perror("handle_probe_req: send");
+ wpa_printf(MSG_INFO, "handle_probe_req: send failed");
os_free(resp);
@@ -570,23 +715,17 @@
#endif /* NEED_AP_MLME */
-void ieee802_11_set_beacon(struct hostapd_data *hapd)
+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;
- struct wpa_driver_ap_params params;
- struct wpabuf *beacon, *proberesp, *assocresp;
#ifdef NEED_AP_MLME
u16 capab_info;
u8 *pos, *tailpos;
-#endif /* NEED_AP_MLME */
-
- hapd->beacon_set_done = 1;
-
-#ifdef NEED_AP_MLME
#define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512
@@ -607,7 +746,7 @@
wpa_printf(MSG_ERROR, "Failed to set beacon data");
os_free(head);
os_free(tail);
- return;
+ return -1;
}
head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -652,6 +791,9 @@
tailpos = hostapd_eid_country(hapd, tailpos,
tail + BEACON_TAIL_BUF_SIZE - tailpos);
+ /* Power Constraint element */
+ tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
+
/* ERP Information element */
tailpos = hostapd_eid_erp_info(hapd, tailpos);
@@ -662,6 +804,9 @@
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);
@@ -678,7 +823,8 @@
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->cs_c_off_beacon);
#ifdef CONFIG_IEEE80211AC
tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_vht_operation(hapd, tailpos);
@@ -710,6 +856,7 @@
#ifdef CONFIG_HS20
tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+ tailpos = hostapd_eid_osen(hapd, tailpos);
#endif /* CONFIG_HS20 */
if (hapd->conf->vendor_elements) {
@@ -723,94 +870,162 @@
resp = hostapd_probe_resp_offloads(hapd, &resp_len);
#endif /* NEED_AP_MLME */
- os_memset(¶ms, 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 ||
+ 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->wpa_pairwise |
+ hapd->conf->rsn_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;
+ params->hide_ssid = NO_SSID_HIDING;
break;
case 1:
- params.hide_ssid = HIDDEN_SSID_ZERO_LEN;
+ params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
break;
case 2:
- params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+ params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
break;
}
- hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp);
- params.beacon_ies = beacon;
- params.proberesp_ies = proberesp;
- params.assocresp_ies = assocresp;
- params.isolate = hapd->conf->isolate;
+ params->isolate = hapd->conf->isolate;
#ifdef NEED_AP_MLME
- params.cts_protect = !!(ieee802_11_erp_info(hapd) &
+ params->cts_protect = !!(ieee802_11_erp_info(hapd) &
ERP_INFO_USE_PROTECTION);
- params.preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
+ 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 =
+ params->short_slot_time =
hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
else
- params.short_slot_time = -1;
+ params->short_slot_time = -1;
if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
- params.ht_opmode = -1;
+ params->ht_opmode = -1;
else
- params.ht_opmode = hapd->iface->ht_op_mode;
+ params->ht_opmode = hapd->iface->ht_op_mode;
#endif /* NEED_AP_MLME */
- params.interworking = hapd->conf->interworking;
+ 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;
+ 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;
+ params->disable_dgaf = hapd->conf->disable_dgaf;
+ if (hapd->conf->osen) {
+ params->privacy = 1;
+ params->osen = 1;
+ }
#endif /* CONFIG_HS20 */
- if (hostapd_drv_set_ap(hapd, ¶ms))
- wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
- hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
-
- os_free(tail);
- os_free(head);
- os_free(resp);
+ return 0;
}
-void ieee802_11_set_beacons(struct hostapd_iface *iface)
+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 hostapd_freq_params freq;
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_config *iconf = iface->conf;
+ struct wpabuf *beacon, *proberesp, *assocresp;
+ int res, ret = -1;
+
+ if (hapd->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;
+
+ if (iface->current_mode &&
+ hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
+ iconf->channel, iconf->ieee80211n,
+ iconf->ieee80211ac,
+ iconf->secondary_channel,
+ iconf->vht_oper_chwidth,
+ iconf->vht_oper_centr_freq_seg0_idx,
+ iconf->vht_oper_centr_freq_seg1_idx,
+ iface->current_mode->vht_capab) == 0)
+ params.freq = &freq;
+
+ 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;
- for (i = 0; i < iface->num_bss; i++)
- ieee802_11_set_beacon(iface->bss[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 */
-void ieee802_11_update_beacons(struct hostapd_iface *iface)
+int ieee802_11_update_beacons(struct hostapd_iface *iface)
{
size_t i;
- for (i = 0; i < iface->num_bss; i++)
- if (iface->bss[i]->beacon_set_done)
- ieee802_11_set_beacon(iface->bss[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 */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index 37f10d2..722159a 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -3,14 +3,8 @@
* Copyright (c) 2002-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef BEACON_H
@@ -21,8 +15,11 @@
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal);
-void ieee802_11_set_beacon(struct hostapd_data *hapd);
-void ieee802_11_set_beacons(struct hostapd_iface *iface);
-void ieee802_11_update_beacons(struct hostapd_iface *iface);
+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 */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 1cb7e73..39edbd7 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1,6 +1,6 @@
/*
* Control interface for shared AP commands
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +10,7 @@
#include "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"
@@ -21,25 +22,61 @@
#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_time now, age;
- int len = 0, ret;
+ struct os_reltime age;
+ int ret;
if (!sta->connected_time.sec)
return 0;
- os_get_time(&now);
- os_time_sub(&now, &sta->connected_time, &age);
+ os_reltime_age(&sta->connected_time, &age);
- ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n",
+ ret = os_snprintf(buf, buflen, "connected_time=%u\n",
(unsigned int) age.sec);
- if (ret < 0 || (size_t) ret >= buflen - len)
- return len;
- len += ret;
+ if (ret < 0 || (size_t) ret >= buflen)
+ return 0;
+ return ret;
+}
- return len;
+
+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 "?";
}
@@ -47,22 +84,45 @@
struct sta_info *sta,
char *buf, size_t buflen)
{
- int len, res, ret;
+ int len, res, ret, i;
- if (sta == NULL) {
- ret = os_snprintf(buf, buflen, "FAIL\n");
- if (ret < 0 || (size_t) ret >= buflen)
- return 0;
- return ret;
- }
+ if (!sta)
+ return 0;
len = 0;
- ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
+ 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;
@@ -80,9 +140,8 @@
if (res >= 0)
len += res;
- res = hostapd_get_sta_conn_time(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;
}
@@ -100,6 +159,8 @@
{
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");
@@ -107,8 +168,28 @@
return 0;
return ret;
}
- return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
- buf, buflen);
+
+ 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);
}
@@ -125,7 +206,11 @@
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);
}
@@ -145,11 +230,12 @@
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);
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
+ " with minor reason code %u (stype=%u (%s))",
+ MAC2STR(addr), minor_reason_code, stype,
+ fc2str(mgmt->frame_control));
+
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);
@@ -165,9 +251,8 @@
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4 + 3 + 1;
- WPA_PUT_BE24(pos, OUI_WFA);
- pos += 3;
- *pos++ = P2P_OUI_TYPE;
+ WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
+ pos += 4;
*pos++ = P2P_ATTR_MINOR_REASON_CODE;
WPA_PUT_LE16(pos, 1);
@@ -197,6 +282,10 @@
if (hwaddr_aton(txtaddr, addr))
return -1;
+ pos = os_strstr(txtaddr, " reason=");
+ if (pos)
+ reason = atoi(pos + 8);
+
pos = os_strstr(txtaddr, " test=");
if (pos) {
struct ieee80211_mgmt mgmt;
@@ -211,8 +300,7 @@
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);
+ mgmt.u.deauth.reason_code = host_to_le16(reason);
if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth),
@@ -229,10 +317,6 @@
}
#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)
@@ -258,6 +342,10 @@
if (hwaddr_aton(txtaddr, addr))
return -1;
+ pos = os_strstr(txtaddr, " reason=");
+ if (pos)
+ reason = atoi(pos + 8);
+
pos = os_strstr(txtaddr, " test=");
if (pos) {
struct ieee80211_mgmt mgmt;
@@ -272,8 +360,7 @@
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);
+ mgmt.u.disassoc.reason_code = host_to_le16(reason);
if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth),
@@ -290,10 +377,6 @@
}
#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)
@@ -303,3 +386,144 @@
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"
+ "num_sta_ht40_intolerant=%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->num_sta_ht40_intolerant,
+ iface->olbc_ht,
+ iface->ht_op_mode);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ if (!iface->cac_started || !iface->dfs_cac_ms) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "cac_time_seconds=%d\n"
+ "cac_time_left_seconds=N/A\n",
+ iface->dfs_cac_ms / 1000);
+ } else {
+ /* CAC started and CAC time set - calculate remaining time */
+ struct os_reltime now;
+ unsigned int left_time;
+
+ os_reltime_age(&iface->dfs_cac_start, &now);
+ left_time = iface->dfs_cac_ms / 1000 - now.sec;
+ ret = os_snprintf(buf + len, buflen - len,
+ "cac_time_seconds=%u\n"
+ "cac_time_left_seconds=%u\n",
+ iface->dfs_cac_ms / 1000,
+ left_time);
+ }
+ 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;
+
+ 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;
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index e83f894..ee58b4c 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -1,6 +1,6 @@
/*
* Control interface for shared AP commands
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -19,5 +19,10 @@
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 */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
new file mode 100644
index 0000000..20419f3
--- /dev/null
+++ b/src/ap/dfs.c
@@ -0,0 +1,947 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * 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, unless they are available for immediate use.
+ */
+ if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+ ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+ HOSTAPD_CHAN_DFS_AVAILABLE))
+ 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 };
+ /*
+ * VHT160 valid channels based on center frequency:
+ * 50, 114
+ */
+ int allowed_160[] = { 36, 100 };
+ 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;
+ case 8:
+ allowed = allowed_160;
+ allowed_no = ARRAY_SIZE(allowed_160);
+ 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;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ int *entry;
+
+ if (!iface->conf->chanlist)
+ return 1;
+
+ for (entry = iface->conf->chanlist; *entry != -1; entry++) {
+ if (*entry == chan->chan)
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * 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 (!is_in_chanlist(iface, chan))
+ 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");
+ *vht_oper_centr_freq_seg0_idx = 0;
+ 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; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
+ mode->num_channels, channel_no, iface->conf->channel,
+ iface->conf->ieee80211n,
+ iface->conf->secondary_channel,
+ iface->conf->vht_oper_chwidth);
+
+ for (i = 0; i < mode->num_channels; i++) {
+ wpa_printf(MSG_DEBUG, "Available channel: %d",
+ mode->channels[i].chan);
+ }
+ }
+
+ 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_DISABLED)
+ break;
+
+ if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+ continue;
+
+ 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");
+ *secondary_channel = 0;
+ *vht_oper_centr_freq_seg0_idx = 0;
+ *vht_oper_centr_freq_seg1_idx = 0;
+
+ 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;
+}
+
+
+static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i;
+ unsigned int cac_time_ms = 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))
+ continue;
+ if (channel->dfs_cac_ms > cac_time_ms)
+ cac_time_ms = channel->dfs_cac_ms;
+ }
+
+ return cac_time_ms;
+}
+
+
+/*
+ * 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);
+
+ /* Setup CAC time */
+ iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
+ n_chans);
+
+ /* 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 = 0;
+ u8 cf1 = 0, cf2 = 0;
+
+ 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, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
+ iface->freq,
+ iface->conf->channel, 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,
+ iface->dfs_cac_ms / 1000);
+
+ res = 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);
+
+ if (res) {
+ wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+ 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 = 0;
+ u8 vht_oper_centr_freq_seg1_idx = 0;
+ 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_csa_in_progress(struct hostapd_iface *iface)
+{
+ unsigned int i;
+ for (i = 0; i < iface->num_bss; i++)
+ if (iface->bss[i]->csa_in_progress)
+ return 1;
+ return 0;
+}
+
+
+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;
+ unsigned int i;
+ int err = 1;
+
+ wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
+ __func__, iface->cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ 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) {
+ /*
+ * If there is no channel to switch immediately to, check if
+ * there is another channel where we can switch even if it
+ * requires to perform a CAC first.
+ */
+ skip_radar = 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) {
+ /* FIXME: Wait for channel(s) to become available */
+ hostapd_disable_iface(iface);
+ return 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;
+ }
+
+ 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;
+ }
+
+ for (i = 0; i < iface->num_bss; i++) {
+ err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+ if (err)
+ break;
+ }
+
+ 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 */
+ 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;
+}
+
+
+int hostapd_is_dfs_required(struct hostapd_iface *iface)
+{
+ int n_chans, start_chan_idx;
+
+ if (!iface->conf->ieee80211h || !iface->current_mode ||
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ /* 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 */
+ return dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+}
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
new file mode 100644
index 0000000..a619c55
--- /dev/null
+++ b/src/ap/dfs.h
@@ -0,0 +1,26 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * 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);
+int hostapd_is_dfs_required(struct hostapd_iface *iface);
+
+#endif /* DFS_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index d6bc98d..93804de 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -9,6 +9,7 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
#include "radius/radius.h"
#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
@@ -29,6 +30,8 @@
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "hw_features.h"
+#include "dfs.h"
+#include "beacon.h"
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -77,6 +80,12 @@
ie = elems.wpa_ie - 2;
ielen = elems.wpa_ie_len + 2;
wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+#ifdef CONFIG_HS20
+ } else if (elems.osen) {
+ ie = elems.osen - 2;
+ ielen = elems.osen_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
+#endif /* CONFIG_HS20 */
} else {
ie = NULL;
ielen = 0;
@@ -114,6 +123,31 @@
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+ if (elems.ht_capabilities &&
+ elems.ht_capabilities_len >=
+ sizeof(struct ieee80211_ht_capabilities) &&
+ (hapd->iface->conf->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ struct ieee80211_ht_capabilities *ht_cap =
+ (struct ieee80211_ht_capabilities *)
+ elems.ht_capabilities;
+
+ if (le_to_host16(ht_cap->ht_capabilities_info) &
+ HT_CAP_INFO_40MHZ_INTOLERANT)
+ ht40_intolerant_add(hapd->iface, sta);
+ }
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+
+#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) {
@@ -273,6 +307,29 @@
sta->flags |= WLAN_STA_MAYBE_WPS;
wpabuf_free(wps);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_HS20
+ } else if (hapd->conf->osen) {
+ if (elems.osen == NULL) {
+ hostapd_logger(
+ hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "No HS 2.0 OSEN element in association request");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+ 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_WARNING, "Failed to initialize WPA "
+ "state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+ elems.osen - 2, elems.osen_len + 2) < 0)
+ return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
}
#ifdef CONFIG_WPS
skip_wpa_check:
@@ -291,6 +348,7 @@
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);
@@ -373,14 +431,15 @@
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- int offset)
+ int offset, int width, int cf1, int cf2)
{
#ifdef NEED_AP_MLME
- int channel;
+ 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", freq, ht, offset);
+ "freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d",
+ freq, ht, offset, width, cf1, cf2);
hapd->iface->freq = freq;
@@ -392,9 +451,52 @@
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->csa_in_progress &&
+ freq == hapd->cs_freq_params.freq) {
+ hostapd_cleanup_cs_params(hapd);
+ ieee802_11_set_beacon(hapd);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d",
+ freq);
+ }
#endif /* NEED_AP_MLME */
}
@@ -506,39 +608,48 @@
static void hostapd_action_rx(struct hostapd_data *hapd,
- struct rx_action *action)
+ 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",
- action->category, (int) action->len);
+ mgmt->u.action.category, (int) plen);
- sta = ap_get_sta(hapd, action->sa);
+ sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
return;
}
#ifdef CONFIG_IEEE80211R
- if (action->category == WLAN_ACTION_FT) {
- wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d",
- __func__, (int) action->len);
- wpa_ft_action_rx(sta->wpa_sm, action->data, action->len);
+ 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 (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) {
- wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d",
- __func__, (int) action->len);
- ieee802_11_sa_query_action(hapd, action->sa,
- *(action->data + 1),
- action->data + 2);
+ 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 (action->category == WLAN_ACTION_WNM) {
- wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
- __func__, (int) action->len);
- ieee802_11_rx_wnm_action_ap(hapd, action);
+ if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+ ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
}
#endif /* CONFIG_WNM */
}
@@ -580,17 +691,32 @@
}
-static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+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;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_mgmt_frame_handling) {
+ size_t hex_len = 2 * rx_mgmt->frame_len + 1;
+ char *hex = os_malloc(hex_len);
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
+ rx_mgmt->frame_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex);
+ os_free(hex);
+ }
+ return 1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
if (bssid == NULL)
- return;
+ return 0;
hapd = get_hapd_bssid(iface, bssid);
if (hapd == NULL) {
@@ -605,7 +731,7 @@
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
hapd = iface->bss[0];
else
- return;
+ return 0;
}
os_memset(&fi, 0, sizeof(fi));
@@ -614,53 +740,25 @@
if (hapd == HAPD_BROADCAST) {
size_t i;
- for (i = 0; i < iface->num_bss; i++)
- ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
- rx_mgmt->frame_len, &fi);
+ ret = 0;
+ for (i = 0; i < iface->num_bss; i++) {
+ /* if bss is set, driver will call this function for
+ * each bss individually. */
+ if (rx_mgmt->drv_priv &&
+ (iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
+ continue;
+
+ if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+ rx_mgmt->frame_len, &fi) > 0)
+ ret = 1;
+ }
} else
- ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+ ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
+ &fi);
random_add_randomness(&fi, sizeof(fi));
-}
-
-static void hostapd_rx_action(struct hostapd_data *hapd,
- struct rx_action *rx_action)
-{
- struct rx_mgmt rx_mgmt;
- u8 *buf;
- struct ieee80211_hdr *hdr;
-
- wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR
- " BSSID=" MACSTR " category=%u",
- MAC2STR(rx_action->da), MAC2STR(rx_action->sa),
- MAC2STR(rx_action->bssid), rx_action->category);
- wpa_hexdump(MSG_MSGDUMP, "Received action frame contents",
- rx_action->data, rx_action->len);
-
- buf = os_zalloc(24 + 1 + rx_action->len);
- if (buf == NULL)
- return;
- hdr = (struct ieee80211_hdr *) buf;
- hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- if (rx_action->category == WLAN_ACTION_SA_QUERY) {
- /*
- * Assume frame was protected; it would have been dropped if
- * not.
- */
- hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
- }
- os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN);
- os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN);
- os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN);
- buf[24] = rx_action->category;
- os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len);
- os_memset(&rx_mgmt, 0, sizeof(rx_mgmt));
- rx_mgmt.frame = buf;
- rx_mgmt.frame_len = 24 + 1 + rx_action->len;
- hostapd_mgmt_rx(hapd, &rx_mgmt);
- os_free(buf);
+ return ret;
}
@@ -785,6 +883,64 @@
}
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped",
+ hapd->conf->iface);
+
+ if (hapd->csa_in_progress) {
+ wpa_printf(MSG_INFO, "CSA failed (%s was stopped)",
+ hapd->conf->iface);
+ hostapd_switch_channel_fallback(hapd->iface,
+ &hapd->cs_freq_params);
+ }
+}
+
+
+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)
{
@@ -858,10 +1014,16 @@
data->rx_from_unknown.addr,
data->rx_from_unknown.wds);
break;
- case EVENT_RX_MGMT:
- hostapd_mgmt_rx(hapd, &data->rx_mgmt);
- break;
#endif /* NEED_AP_MLME */
+ case EVENT_RX_MGMT:
+ if (!data->rx_mgmt.frame)
+ break;
+#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)
@@ -900,15 +1062,6 @@
break;
hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
break;
- case EVENT_RX_ACTION:
- if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
- data->rx_action.bssid == NULL)
- break;
-#ifdef NEED_AP_MLME
- hostapd_rx_action(hapd, &data->rx_action);
-#endif /* NEED_AP_MLME */
- hostapd_action_rx(hapd, &data->rx_action);
- break;
case EVENT_AUTH:
hostapd_notif_auth(hapd, &data->auth);
break;
@@ -917,7 +1070,10 @@
break;
hostapd_event_ch_switch(hapd, data->ch_switch.freq,
data->ch_switch.ht_enabled,
- data->ch_switch.ch_offset);
+ 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)
@@ -929,6 +1085,39 @@
case EVENT_SURVEY:
hostapd_event_get_survey(hapd, &data->survey_results);
break;
+#ifdef NEED_AP_MLME
+ case EVENT_INTERFACE_UNAVAILABLE:
+ hostapd_event_iface_unavailable(hapd);
+ break;
+ 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;
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index 79d50e5..559d77f 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -83,12 +83,14 @@
for (i = 0; i < argc; i++) {
if (os_strcmp(col[i], "password") == 0 && argv[i]) {
- os_free(user->password);
+ bin_clear_free(user->password, user->password_len);
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]);
+ } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
+ user->remediation = strlen(argv[i]) > 0;
}
}
@@ -116,7 +118,7 @@
if (len <= user->identity_len &&
os_memcmp(argv[id], user->identity, len) == 0 &&
(user->password == NULL || len > user->password_len)) {
- os_free(user->password);
+ bin_clear_free(user->password, user->password_len);
user->password_len = os_strlen(argv[id]);
user->password = (u8 *) os_strdup(argv[id]);
user->next = (void *) 1;
@@ -156,8 +158,10 @@
return NULL;
}
- os_free(hapd->tmp_eap_user.identity);
- os_free(hapd->tmp_eap_user.password);
+ bin_clear_free(hapd->tmp_eap_user.identity,
+ hapd->tmp_eap_user.identity_len);
+ bin_clear_free(hapd->tmp_eap_user.password,
+ hapd->tmp_eap_user.password_len);
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);
@@ -173,8 +177,8 @@
}
os_snprintf(cmd, sizeof(cmd),
- "SELECT password,methods FROM users WHERE "
- "identity='%s' AND phase2=%d;", id_str, phase2);
+ "SELECT * 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) {
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index b3574ba..ad07107 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1,6 +1,6 @@
/*
* Generic advertisement service (GAS) server
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -19,6 +19,13 @@
#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)
{
@@ -46,6 +53,8 @@
* 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) {
@@ -62,7 +71,6 @@
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;
@@ -150,6 +158,10 @@
wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
if (hapd->conf->hs20_operating_class)
wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+ if (hapd->conf->hs20_osu_providers_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+ if (hapd->conf->hs20_icons_count)
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
gas_anqp_set_element_len(buf, len);
}
#endif /* CONFIG_HS20 */
@@ -402,7 +414,7 @@
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) {
+ } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
home_realm_len);
}
@@ -505,18 +517,188 @@
}
}
+
+static void anqp_add_osu_provider(struct wpabuf *buf,
+ struct hostapd_bss_config *bss,
+ struct hs20_osu_provider *p)
+{
+ u8 *len, *len2, *count;
+ unsigned int i;
+
+ len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
+
+ /* OSU Friendly Name Duples */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->friendly_name_count; i++) {
+ struct hostapd_lang_string *s = &p->friendly_name[i];
+ wpabuf_put_u8(buf, 3 + s->name_len);
+ wpabuf_put_data(buf, s->lang, 3);
+ wpabuf_put_data(buf, s->name, s->name_len);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU Server URI */
+ if (p->server_uri) {
+ wpabuf_put_u8(buf, os_strlen(p->server_uri));
+ wpabuf_put_str(buf, p->server_uri);
+ } else
+ wpabuf_put_u8(buf, 0);
+
+ /* OSU Method List */
+ count = wpabuf_put(buf, 1);
+ for (i = 0; p->method_list[i] >= 0; i++)
+ wpabuf_put_u8(buf, p->method_list[i]);
+ *count = i;
+
+ /* Icons Available */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->icons_count; i++) {
+ size_t j;
+ struct hs20_icon *icon = NULL;
+
+ for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+ if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
+ 0)
+ icon = &bss->hs20_icons[j];
+ }
+ if (!icon)
+ continue; /* icon info not found */
+
+ wpabuf_put_le16(buf, icon->width);
+ wpabuf_put_le16(buf, icon->height);
+ wpabuf_put_data(buf, icon->language, 3);
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_u8(buf, os_strlen(icon->name));
+ wpabuf_put_str(buf, icon->name);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU_NAI */
+ if (p->osu_nai) {
+ wpabuf_put_u8(buf, os_strlen(p->osu_nai));
+ wpabuf_put_str(buf, p->osu_nai);
+ } else
+ wpabuf_put_u8(buf, 0);
+
+ /* OSU Service Description Duples */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->service_desc_count; i++) {
+ struct hostapd_lang_string *s = &p->service_desc[i];
+ wpabuf_put_u8(buf, 3 + s->name_len);
+ wpabuf_put_data(buf, s->lang, 3);
+ wpabuf_put_data(buf, s->name, s->name_len);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_osu_providers_count) {
+ size_t i;
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ /* OSU SSID */
+ wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
+ wpabuf_put_data(buf, hapd->conf->osu_ssid,
+ hapd->conf->osu_ssid_len);
+
+ /* Number of OSU Providers */
+ wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
+
+ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+ anqp_add_osu_provider(
+ buf, hapd->conf,
+ &hapd->conf->hs20_osu_providers[i]);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
+ struct wpabuf *buf,
+ const u8 *name, size_t name_len)
+{
+ struct hs20_icon *icon;
+ size_t i;
+ u8 *len;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
+ name, name_len);
+ for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
+ icon = &hapd->conf->hs20_icons[i];
+ if (name_len == os_strlen(icon->name) &&
+ os_memcmp(name, icon->name, name_len) == 0)
+ break;
+ }
+
+ if (i < hapd->conf->hs20_icons_count)
+ icon = &hapd->conf->hs20_icons[i];
+ else
+ icon = NULL;
+
+ 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_ICON_BINARY_FILE);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ if (icon) {
+ char *data;
+ size_t data_len;
+
+ data = os_readfile(icon->file, &data_len);
+ if (data == NULL || data_len > 65535) {
+ wpabuf_put_u8(buf, 2); /* Download Status:
+ * Unspecified file error */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0);
+ } else {
+ wpabuf_put_u8(buf, 0); /* Download Status: Success */
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_le16(buf, data_len);
+ wpabuf_put_data(buf, data, data_len);
+ }
+ os_free(data);
+ } else {
+ wpabuf_put_u8(buf, 1); /* Download Status: File not found */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0);
+ }
+
+ 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)
+ const u8 *home_realm, size_t home_realm_len,
+ const u8 *icon_name, size_t icon_name_len)
{
struct wpabuf *buf;
+ size_t len;
- buf = wpabuf_alloc(1400);
+ len = 1400;
+ if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+ len += 1000;
+ if (request & ANQP_REQ_ICON_REQUEST)
+ len += 65536;
+
+ buf = wpabuf_alloc(len);
if (buf == NULL)
return NULL;
@@ -550,44 +732,31 @@
anqp_add_connection_capability(hapd, buf);
if (request & ANQP_REQ_OPERATING_CLASS)
anqp_add_operating_class(hapd, buf);
+ if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
+ anqp_add_osu_providers_list(hapd, buf);
+ if (request & ANQP_REQ_ICON_REQUEST)
+ anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
#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;
+ const u8 *icon_name;
+ size_t icon_name_len;
};
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);
}
@@ -599,43 +768,38 @@
{
switch (info_id) {
case ANQP_CAPABILITY_LIST:
- set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
- 0, qi);
+ set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
+ qi);
break;
case ANQP_VENUE_NAME:
set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
- hapd->conf->venue_name != NULL, 0, 0, qi);
+ hapd->conf->venue_name != NULL, 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);
+ hapd->conf->network_auth_type != NULL, qi);
break;
case ANQP_ROAMING_CONSORTIUM:
set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
- hapd->conf->roaming_consortium != NULL, 0, 0, qi);
+ hapd->conf->roaming_consortium != NULL, 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);
+ hapd->conf->ipaddr_type_configured, qi);
break;
case ANQP_NAI_REALM:
set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
- hapd->conf->nai_realm_data != NULL,
- 0, 0, qi);
+ hapd->conf->nai_realm_data != NULL, 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);
+ hapd->conf->anqp_3gpp_cell_net != NULL, qi);
break;
case ANQP_DOMAIN_NAME:
set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
- hapd->conf->domain_name != NULL,
- 0, 0, qi);
+ hapd->conf->domain_name != NULL, qi);
break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
@@ -667,29 +831,30 @@
switch (subtype) {
case HS20_STYPE_CAPABILITY_LIST:
set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
- 1, 0, 0, qi);
+ 1, 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);
+ hapd->conf->hs20_oper_friendly_name != NULL, 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);
+ hapd->conf->hs20_wan_metrics != NULL, 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);
+ 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);
+ hapd->conf->hs20_operating_class != NULL, qi);
+ break;
+ case HS20_STYPE_OSU_PROVIDERS_LIST:
+ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+ hapd->conf->hs20_osu_providers_count, qi);
break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
@@ -716,6 +881,23 @@
}
+static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ qi->request |= ANQP_REQ_ICON_REQUEST;
+ qi->icon_name = pos;
+ qi->icon_name_len = end - pos;
+ if (hapd->conf->hs20_icons_count) {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
+ "(local)");
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
+ "available");
+ }
+}
+
+
static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
const u8 *pos, const u8 *end,
struct anqp_query_info *qi)
@@ -760,6 +942,9 @@
case HS20_STYPE_NAI_HOME_REALM_QUERY:
rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
break;
+ case HS20_STYPE_ICON_REQUEST:
+ rx_anqp_hs_icon_request(hapd, pos, end, qi);
+ break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
"%u", subtype);
@@ -772,13 +957,14 @@
static void gas_serv_req_local_processing(struct hostapd_data *hapd,
const u8 *sa, u8 dialog_token,
- struct anqp_query_info *qi)
+ struct anqp_query_info *qi, int prot)
{
struct wpabuf *buf, *tx_buf;
- buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
+ buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
qi->home_realm_query,
- qi->home_realm_query_len);
+ qi->home_realm_query_len,
+ qi->icon_name, qi->icon_name_len);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
buf);
if (!buf)
@@ -802,13 +988,17 @@
"for " MACSTR " (dialog token %u)",
MAC2STR(sa), dialog_token);
wpabuf_free(buf);
- return;
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ 0, NULL);
+ } else {
+ 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);
}
- 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(
@@ -817,7 +1007,8 @@
}
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);
@@ -826,7 +1017,7 @@
static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
const u8 *sa,
- const u8 *data, size_t len)
+ const u8 *data, size_t len, int prot)
{
const u8 *pos = data;
const u8 *end = data + len;
@@ -876,6 +1067,8 @@
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);
@@ -927,100 +1120,13 @@
pos += elen;
}
- gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
-}
-
-
-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)");
- 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++;
-
- 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);
+ gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
}
static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
const u8 *sa,
- const u8 *data, size_t len)
+ const u8 *data, size_t len, int prot)
{
struct gas_dialog_info *dialog;
struct wpabuf *buf, *tx_buf;
@@ -1051,33 +1157,6 @@
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;
@@ -1090,15 +1169,18 @@
if (buf == NULL) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
"buffer");
- goto rx_gas_comeback_req_done;
+ gas_serv_dialog_clear(dialog);
+ return;
}
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;
+ if (tx_buf == NULL) {
+ gas_serv_dialog_clear(dialog);
+ return;
+ }
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);
@@ -1118,13 +1200,11 @@
}
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);
}
@@ -1133,24 +1213,30 @@
{
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)
+ if (len < IEEE80211_HDRLEN + 2)
return;
- if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+ 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;
+ len -= IEEE80211_HDRLEN + 1;
+ data = buf + IEEE80211_HDRLEN + 1;
switch (data[0]) {
case WLAN_PA_GAS_INITIAL_REQ:
- gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
+ 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);
+ gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
break;
}
}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 4213cf6..4ec3201 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -1,6 +1,6 @@
/*
* Generic advertisement service (GAS) server
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -37,29 +37,22 @@
(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 */
+#define ANQP_REQ_OSU_PROVIDERS_LIST \
+ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
+#define ANQP_REQ_ICON_REQUEST \
+ (0x10000 << HS20_STYPE_ICON_REQUEST)
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;
-
- unsigned int requested;
- unsigned int received;
- unsigned int all_requested;
+ int prot; /* whether Protected Dual of Public Action frame is used */
};
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);
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index fd1ca2b..26aca2b 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,6 +1,6 @@
/*
* hostapd / Initialization and configuration
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -11,9 +11,10 @@
#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 "drivers/driver.h"
+#include "eap_server/tncs.h"
#include "hostapd.h"
#include "authsrv.h"
#include "sta_info.h"
@@ -32,14 +33,15 @@
#include "ap_config.h"
#include "p2p_hostapd.h"
#include "gas_serv.h"
+#include "dfs.h"
+#include "ieee802_11.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);
-
-extern int wpa_debug_level;
-extern struct wpa_driver_ops *wpa_drivers[];
+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,
@@ -61,10 +63,22 @@
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");
@@ -75,7 +89,7 @@
else
hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
- if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
+ if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
hostapd_setup_wpa(hapd);
if (hapd->wpa_auth)
wpa_init_keys(hapd->wpa_auth);
@@ -159,7 +173,18 @@
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
hapd->iconf = newconf;
- hapd->conf = &newconf->bss[j];
+ hapd->iconf->channel = oldconf->channel;
+ hapd->iconf->secondary_channel = oldconf->secondary_channel;
+ hapd->iconf->ieee80211n = oldconf->ieee80211n;
+ hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
+ hapd->iconf->ht_capab = oldconf->ht_capab;
+ hapd->iconf->vht_capab = oldconf->vht_capab;
+ hapd->iconf->vht_oper_chwidth = oldconf->vht_oper_chwidth;
+ hapd->iconf->vht_oper_centr_freq_seg0_idx =
+ oldconf->vht_oper_centr_freq_seg0_idx;
+ hapd->iconf->vht_oper_centr_freq_seg1_idx =
+ oldconf->vht_oper_centr_freq_seg1_idx;
+ hapd->conf = newconf->bss[j];
hostapd_reload_bss(hapd);
}
@@ -227,6 +252,14 @@
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);
@@ -244,10 +277,21 @@
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);
+ if (hapd->interface_added) {
+ hapd->interface_added = 0;
+ if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+ wpa_printf(MSG_WARNING,
+ "Failed to remove BSS interface %s",
+ hapd->conf->iface);
+ hapd->interface_added = 1;
+ } else {
+ /*
+ * Since this was a dynamically added interface, the
+ * driver wrapper may have removed its internal instance
+ * and hapd->drv_priv is not valid anymore.
+ */
+ hapd->drv_priv = NULL;
+ }
}
os_free(hapd->probereq_cb);
@@ -267,8 +311,10 @@
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_SQLITE
- os_free(hapd->tmp_eap_user.identity);
- os_free(hapd->tmp_eap_user.password);
+ bin_clear_free(hapd->tmp_eap_user.identity,
+ hapd->tmp_eap_user.identity_len);
+ bin_clear_free(hapd->tmp_eap_user.password,
+ hapd->tmp_eap_user.password_len);
#endif /* CONFIG_SQLITE */
}
@@ -278,13 +324,13 @@
* @hapd: Pointer to BSS data
*
* This function is used to free all per-BSS data structures and resources.
- * This gets called in a loop for each BSS between calls to
- * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
- * is deinitialized. Most of the modules that are initialized in
- * hostapd_setup_bss() are deinitialized here.
+ * 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);
@@ -292,20 +338,9 @@
}
-/**
- * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup
- * @iface: Pointer to interface data
- *
- * This function is called before per-BSS data structures are deinitialized
- * with hostapd_cleanup().
- */
-static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface)
-{
-}
-
-
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);
@@ -325,19 +360,23 @@
*/
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) {
+ if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) {
hostapd_set_privacy(hapd, 0);
hostapd_broadcast_wep_clear(hapd);
}
@@ -388,11 +427,15 @@
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;
+ if (!hapd->iface->driver_ap_teardown) {
+ 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);
@@ -403,6 +446,14 @@
}
+static void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+{
+ hostapd_free_stas(hapd);
+ hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+ hostapd_clear_wep(hapd);
+}
+
+
/**
* hostapd_validate_bssid_configuration - Validate BSSID configuration
* @iface: Pointer to interface data
@@ -429,7 +480,7 @@
/* 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 (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) {
if (j)
auto_addr++;
continue;
@@ -437,7 +488,7 @@
for (i = 0; i < ETH_ALEN; i++) {
mask[i] |=
- iface->conf->bss[j].bssid[i] ^
+ iface->conf->bss[j]->bssid[i] ^
hapd->own_addr[i];
}
}
@@ -502,7 +553,7 @@
size_t i;
for (i = 0; i < conf->num_bss; i++) {
- if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) {
+ if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) {
return 1;
}
}
@@ -516,7 +567,34 @@
static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
struct radius_das_attrs *attr)
{
- /* TODO */
+ if (attr->nas_identifier &&
+ (!hapd->conf->nas_identifier ||
+ os_strlen(hapd->conf->nas_identifier) !=
+ attr->nas_identifier_len ||
+ os_memcmp(hapd->conf->nas_identifier, attr->nas_identifier,
+ attr->nas_identifier_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-Identifier mismatch");
+ return 1;
+ }
+
+ if (attr->nas_ip_addr &&
+ (hapd->conf->own_ip_addr.af != AF_INET ||
+ os_memcmp(&hapd->conf->own_ip_addr.u.v4, attr->nas_ip_addr, 4) !=
+ 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IP-Address mismatch");
+ return 1;
+ }
+
+#ifdef CONFIG_IPV6
+ if (attr->nas_ipv6_addr &&
+ (hapd->conf->own_ip_addr.af != AF_INET6 ||
+ os_memcmp(&hapd->conf->own_ip_addr.u.v6, attr->nas_ipv6_addr, 16)
+ != 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IPv6-Address mismatch");
+ return 1;
+ }
+#endif /* CONFIG_IPV6 */
+
return 0;
}
@@ -583,6 +661,8 @@
if (sta == NULL)
return RADIUS_DAS_SESSION_NOT_FOUND;
+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
hostapd_drv_sta_deauth(hapd, sta->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -596,7 +676,8 @@
/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
- * @first: Whether this BSS is the first BSS of an interface
+ * @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
@@ -611,7 +692,24 @@
char force_ifname[IFNAMSIZ];
u8 if_addr[ETH_ALEN];
- if (!first) {
+ wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+ __func__, hapd, hapd->conf->iface, first);
+
+#ifdef EAP_SERVER_TNC
+ if (hapd->conf->tnc && tncs_global_init() < 0) {
+ wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
+ return -1;
+ }
+#endif /* EAP_SERVER_TNC */
+
+ 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 {
@@ -636,9 +734,10 @@
hapd->conf->iface, hapd->own_addr, hapd,
&hapd->drv_priv, force_ifname, if_addr,
hapd->conf->bridge[0] ? hapd->conf->bridge :
- NULL)) {
+ NULL, first == -1)) {
wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
MACSTR ")", MAC2STR(hapd->own_addr));
+ hapd->interface_added = 0;
return -1;
}
}
@@ -702,7 +801,7 @@
return -1;
}
- if (wpa_debug_level == MSG_MSGDUMP)
+ if (wpa_debug_level <= MSG_MSGDUMP)
conf->radius->msg_dumps = 1;
#ifndef CONFIG_NO_RADIUS
hapd->radius = radius_client_init(hapd, conf->radius);
@@ -748,7 +847,7 @@
return -1;
}
- if (hapd->conf->wpa && hostapd_setup_wpa(hapd))
+ if ((hapd->conf->wpa || hapd->conf->osen) && hostapd_setup_wpa(hapd))
return -1;
if (accounting_init(hapd)) {
@@ -768,22 +867,22 @@
wpa_printf(MSG_ERROR, "GAS server initialization failed");
return -1;
}
-#endif /* CONFIG_INTERWORKING */
- if (hapd->iface->interfaces &&
- hapd->iface->interfaces->ctrl_iface_init &&
- hapd->iface->interfaces->ctrl_iface_init(hapd)) {
- wpa_printf(MSG_ERROR, "Failed to setup control interface");
+ 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);
+ 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;
@@ -849,44 +948,117 @@
if (hapd->iface->drv_max_acl_mac_addrs == 0)
return;
- if (!(conf->bss->num_accept_mac || conf->bss->num_deny_mac))
- return;
- if (conf->bss->macaddr_acl == DENY_UNLESS_ACCEPTED) {
- if (conf->bss->num_accept_mac) {
- accept_acl = 1;
- err = hostapd_set_acl_list(hapd, conf->bss->accept_mac,
- conf->bss->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");
+ if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+ 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 if (conf->bss->macaddr_acl == ACCEPT_UNLESS_DENIED) {
- if (conf->bss->num_deny_mac) {
- accept_acl = 0;
- err = hostapd_set_acl_list(hapd, conf->bss->deny_mac,
- conf->bss->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");
+ } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+ 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;
}
}
}
+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;
- char country[4];
+
+ /*
+ * It is possible that setup_interface() is called after the interface
+ * was disabled etc., in which case driver_ap_teardown is possibly set
+ * to 1. Clear it here so any other key/station deletion, which is not
+ * part of a teardown flow, would also call the relevant driver
+ * callbacks.
+ */
+ iface->driver_ap_teardown = 0;
+
+ 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
@@ -900,15 +1072,49 @@
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. */
@@ -917,7 +1123,7 @@
if (ret < 0) {
wpa_printf(MSG_ERROR, "Could not select hw_mode and "
"channel. (%d)", ret);
- return -1;
+ goto fail;
}
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
@@ -925,36 +1131,65 @@
}
ret = hostapd_check_ht_capab(iface);
if (ret < 0)
- return -1;
+ goto fail;
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);
+
+fail:
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ if (iface->interfaces && iface->interfaces->terminate_on_error)
+ eloop_terminate();
+ return -1;
}
+/**
+ * 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");
- eloop_terminate();
- return -1;
- }
+ if (err)
+ goto fail;
wpa_printf(MSG_DEBUG, "Completing interface initialization");
- if (hapd->iconf->channel) {
- iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
+ 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(hapd->iconf->hw_mode),
- hapd->iconf->channel, iface->freq);
+ 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) {
+ if (res < 0)
+ goto fail;
+ return res;
+ }
+#endif /* NEED_AP_MLME */
if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
hapd->iconf->channel,
@@ -966,7 +1201,7 @@
hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
wpa_printf(MSG_ERROR, "Could not set channel for "
"kernel driver");
- return -1;
+ goto fail;
}
}
@@ -977,7 +1212,7 @@
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Failed to prepare rates table.");
- return -1;
+ goto fail;
}
}
@@ -985,14 +1220,14 @@
hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
"kernel driver");
- return -1;
+ goto fail;
}
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;
+ goto fail;
}
prev_addr = hapd->own_addr;
@@ -1001,11 +1236,18 @@
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_setup_bss(hapd, j == 0)) {
+ do {
+ hapd = iface->bss[j];
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_free_hapd_data(hapd);
+ } while (j-- > 0);
+ goto fail;
+ }
if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
prev_addr = hapd->own_addr;
}
+ hapd = iface->bss[0];
hostapd_tx_queue_params(iface);
@@ -1016,7 +1258,7 @@
if (hostapd_driver_commit(hapd) < 0) {
wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
"configuration", __func__);
- return -1;
+ goto fail;
}
/*
@@ -1027,16 +1269,28 @@
*/
for (j = 0; j < iface->num_bss; j++) {
if (hostapd_init_wps_complete(iface->bss[j]))
- return -1;
+ goto fail;
}
+ 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;
+
+fail:
+ wpa_printf(MSG_ERROR, "Interface initialization failed");
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ if (iface->interfaces && iface->interfaces->terminate_on_error)
+ eloop_terminate();
+ return -1;
}
@@ -1049,6 +1303,12 @@
* 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)
{
@@ -1098,68 +1358,324 @@
}
+static void hostapd_bss_deinit(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
+ hapd->conf->iface);
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_cleanup(hapd);
+}
+
+
void hostapd_interface_deinit(struct hostapd_iface *iface)
{
- size_t j;
+ int j;
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
if (iface == NULL)
return;
- hostapd_cleanup_iface_pre(iface);
- for (j = 0; j < iface->num_bss; j++) {
- struct hostapd_data *hapd = iface->bss[j];
- hostapd_free_stas(hapd);
- hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
- hostapd_clear_wep(hapd);
- hostapd_cleanup(hapd);
- }
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ iface->wait_channel_update = 0;
+
+ 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;
- for (j = 0; j < iface->num_bss; 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);
}
-#ifdef HOSTAPD
+/**
+ * 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);
- if (driver && driver->hapd_deinit && 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);
+ iface->bss[0]->drv_priv = NULL;
+ }
hostapd_interface_free(iface);
}
+static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
+ void *drv_priv,
+ struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+
+ 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);
+ for (j = 0; j < hapd_iface->num_bss; j++) {
+ wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
+ __func__, (int) j,
+ hapd_iface->bss[j]->drv_priv);
+ if (hapd_iface->bss[j]->drv_priv == drv_priv)
+ hapd_iface->bss[j]->drv_priv = NULL;
+ }
+ }
+}
+
+
int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
{
+ size_t j;
+
if (hapd_iface->bss[0]->drv_priv != NULL) {
wpa_printf(MSG_ERROR, "Interface %s already enabled",
- hapd_iface->conf->bss[0].iface);
+ hapd_iface->conf->bss[0]->iface);
return -1;
}
wpa_printf(MSG_DEBUG, "Enable interface %s",
- hapd_iface->conf->bss[0].iface);
+ hapd_iface->conf->bss[0]->iface);
+
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+ 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) ||
- hostapd_setup_interface(hapd_iface)) {
- hostapd_interface_deinit_free(hapd_iface);
+ hapd_iface->interfaces->driver_init(hapd_iface))
+ return -1;
+
+ if (hostapd_setup_interface(hapd_iface)) {
+ hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+ hapd_iface->bss[0]->drv_priv,
+ hapd_iface);
return -1;
}
+
return 0;
}
@@ -1169,19 +1685,17 @@
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_flush_old_stations(hapd_iface->bss[j],
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-
-#ifndef CONFIG_NO_RADIUS
- /* TODO: update dynamic data based on changed configuration
- * items (e.g., open/close sockets, etc.) */
- radius_client_flush(hapd_iface->bss[j]->radius, 0);
-#endif /* CONFIG_NO_RADIUS */
-
- hostapd_reload_bss(hapd_iface->bss[j]);
+ hapd_iface->conf->bss[0]->iface);
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+ 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;
}
@@ -1189,39 +1703,43 @@
int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
- struct hostapd_bss_config *bss;
const struct wpa_driver_ops *driver;
void *drv_priv;
if (hapd_iface == NULL)
return -1;
- bss = hapd_iface->bss[0]->conf;
+
+ if (hapd_iface->bss[0]->drv_priv == NULL) {
+ wpa_printf(MSG_INFO, "Interface %s already disabled",
+ hapd_iface->conf->bss[0]->iface);
+ 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 */
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+ /* same as hostapd_interface_deinit without deinitializing ctrl-iface */
for (j = 0; j < hapd_iface->num_bss; j++) {
struct hostapd_data *hapd = hapd_iface->bss[j];
- hostapd_free_stas(hapd);
- hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
- hostapd_clear_wep(hapd);
+ hostapd_bss_deinit_no_free(hapd);
hostapd_free_hapd_data(hapd);
}
- if (driver && driver->hapd_deinit && drv_priv) {
- driver->hapd_deinit(drv_priv);
- hapd_iface->bss[0]->drv_priv = NULL;
- }
+ hostapd_deinit_driver(driver, drv_priv, hapd_iface);
/* From hostapd_cleanup_iface: These were initialized in
* hostapd_setup_interface and hostapd_setup_interface_complete
*/
hostapd_cleanup_iface_partial(hapd_iface);
- bss->wpa = 0;
- bss->wpa_key_mgmt = -1;
- bss->wpa_pairwise = -1;
- wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface);
+ wpa_printf(MSG_DEBUG, "Interface %s disabled",
+ hapd_iface->bss[0]->conf->iface);
+ hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
return 0;
}
@@ -1272,7 +1790,7 @@
return NULL;
}
- bss = conf->last_bss = conf->bss;
+ bss = conf->last_bss = conf->bss[0];
os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
bss->ctrl_interface = os_strdup(ctrl_iface);
@@ -1307,8 +1825,7 @@
for (i = 0; i < conf->num_bss; i++) {
hapd = hapd_iface->bss[i] =
- hostapd_alloc_bss_data(hapd_iface, conf,
- &conf->bss[i]);
+ hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
if (hapd == NULL)
return NULL;
hapd->msg_ctx = hapd;
@@ -1323,17 +1840,85 @@
int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
{
struct hostapd_config *conf = NULL;
- struct hostapd_iface *hapd_iface = NULL;
+ struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
+ struct hostapd_data *hapd;
char *ptr;
- size_t i;
+ 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))) {
+ hostapd_cleanup(hapd);
+ hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
+ 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,
+ if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface,
buf)) {
wpa_printf(MSG_INFO, "Cannot add interface - it "
"already exists");
@@ -1348,8 +1933,14 @@
goto fail;
}
- conf = hostapd_config_alloc(interfaces, buf, ptr);
- if (conf == NULL) {
+ 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;
@@ -1362,14 +1953,10 @@
goto fail;
}
- if (hapd_iface->interfaces &&
- hapd_iface->interfaces->ctrl_iface_init &&
- hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) {
- wpa_printf(MSG_ERROR, "%s: Failed to setup control "
- "interface", __func__);
+ if (start_ctrl_iface(hapd_iface) < 0)
goto fail;
- }
- wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface);
+
+ wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0]->iface);
return 0;
@@ -1377,24 +1964,79 @@
if (conf)
hostapd_config_free(conf);
if (hapd_iface) {
- os_free(hapd_iface->bss[interfaces->count]);
+ if (hapd_iface->bss) {
+ for (i = 0; i < hapd_iface->num_bss; i++) {
+ hapd = hapd_iface->bss[i];
+ if (!hapd)
+ continue;
+ if (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->conf->iface);
+ os_free(hapd);
+ hapd_iface->bss[i] = NULL;
+ }
+ 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, k = 0;
+ 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)) {
+ if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
hostapd_interface_deinit_free(hapd_iface);
k = i;
while (k < (interfaces->count - 1)) {
@@ -1405,12 +2047,19 @@
interfaces->count--;
return 0;
}
+
+ for (j = 0; j < hapd_iface->conf->num_bss; j++) {
+ if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) {
+ hapd_iface->driver_ap_teardown =
+ !(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+ return hostapd_remove_bss(hapd_iface, j);
+ }
+ }
}
return -1;
}
-#endif /* HOSTAPD */
-
/**
* hostapd_new_assoc_sta - Notify that a new station associated with the AP
@@ -1450,8 +2099,9 @@
/* 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_time(&sta->connected_time);
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
+ ap_sta_set_authorized(hapd, sta, 1);
+ os_get_reltime(&sta->connected_time);
accounting_sta_start(hapd, sta);
}
@@ -1464,11 +2114,329 @@
} else
wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
- 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);
+ 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_data *hapd,
+ struct beacon_data *beacon)
+{
+ struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
+ struct wpa_driver_ap_params params;
+ int ret;
+
+ 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 */
+ params->channel = hostapd_hw_get_channel(hapd, params->freq);
+ }
+
+ channel = params->channel;
+ if (!channel)
+ return -1;
+
+ /* 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_data *hapd,
+ struct csa_settings *settings)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_freq_params old_freq;
+ int ret;
+
+ os_memset(&old_freq, 0, sizeof(old_freq));
+ if (!iface || !iface->freq || hapd->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(hapd, &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 */
+ hapd->cs_freq_params = settings->freq_params;
+ hapd->cs_count = settings->cs_count;
+ hapd->cs_block_tx = settings->block_tx;
+
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
+ if (ret) {
+ free_beacon_data(&settings->beacon_after);
+ return ret;
+ }
+
+ settings->counter_offset_beacon = hapd->cs_c_off_beacon;
+ settings->counter_offset_presp = hapd->cs_c_off_proberesp;
+
+ return 0;
+}
+
+
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
+{
+ os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params));
+ hapd->cs_count = 0;
+ hapd->cs_block_tx = 0;
+ hapd->cs_c_off_beacon = 0;
+ hapd->cs_c_off_proberesp = 0;
+ hapd->csa_in_progress = 0;
+}
+
+
+int hostapd_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+{
+ int ret;
+ ret = hostapd_fill_csa_settings(hapd, 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->csa_in_progress = 1;
+ return 0;
+}
+
+
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ const struct hostapd_freq_params *freq_params)
+{
+ int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
+
+ if (freq_params->center_freq1)
+ vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+ if (freq_params->center_freq2)
+ vht_seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+
+ switch (freq_params->bandwidth) {
+ case 0:
+ case 20:
+ case 40:
+ vht_bw = VHT_CHANWIDTH_USE_HT;
+ break;
+ case 80:
+ if (freq_params->center_freq2)
+ vht_bw = VHT_CHANWIDTH_80P80MHZ;
+ else
+ vht_bw = VHT_CHANWIDTH_80MHZ;
+ break;
+ case 160:
+ vht_bw = VHT_CHANWIDTH_160MHZ;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
+ freq_params->bandwidth);
+ break;
+ }
+
+ iface->freq = freq_params->freq;
+ iface->conf->channel = freq_params->channel;
+ iface->conf->secondary_channel = freq_params->sec_channel_offset;
+ iface->conf->vht_oper_centr_freq_seg0_idx = vht_seg0_idx;
+ iface->conf->vht_oper_centr_freq_seg1_idx = vht_seg1_idx;
+ iface->conf->vht_oper_chwidth = vht_bw;
+ iface->conf->ieee80211n = freq_params->ht_enabled;
+ iface->conf->ieee80211ac = freq_params->vht_enabled;
+
+ /*
+ * cs_params must not be cleared earlier because the freq_params
+ * argument may actually point to one of these.
+ */
+ for (i = 0; i < iface->num_bss; i++)
+ hostapd_cleanup_cs_params(iface->bss[i]);
+
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+}
+
+#endif /* NEED_AP_MLME */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index dbf1b52..3c8727b 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -1,6 +1,6 @@
/*
* hostapd / Initialization and configuration
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -11,21 +11,19 @@
#include "common/defs.h"
#include "ap_config.h"
+#include "drivers/driver.h"
-struct wpa_driver_ops;
struct wpa_ctrl_dst;
struct radius_server_data;
struct upnp_wps_device_sm;
struct hostapd_data;
struct sta_info;
-struct hostap_sta_driver_data;
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);
@@ -38,13 +36,15 @@
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 {
@@ -100,6 +100,7 @@
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];
@@ -139,7 +140,7 @@
struct eapol_authenticator *eapol_auth;
struct rsn_preauth_interface *preauth_iface;
- time_t michael_mic_failure;
+ struct os_reltime michael_mic_failure;
int michael_mic_failures;
int tkip_countermeasures;
@@ -209,6 +210,14 @@
size_t psk_len);
void *new_psk_cb_ctx;
+ /* 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_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
@@ -234,8 +243,12 @@
#ifdef CONFIG_SAE
/** Key used for generating SAE anti-clogging tokens */
u8 sae_token_key[8];
- os_time_t last_sae_token_key_update;
+ struct os_reltime last_sae_token_key_update;
#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ int ext_mgmt_frame_handling;
+#endif /* CONFIG_TESTING_OPTIONS */
};
@@ -247,10 +260,30 @@
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;
+
+ /*
+ * When set, indicates that the driver will handle the AP
+ * teardown: delete global keys, station keys, and stations.
+ */
+ unsigned int driver_ap_teardown: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];
@@ -302,6 +335,9 @@
/* Number of HT associated stations 20 MHz */
int num_sta_ht_20mhz;
+ /* Number of HT40 intolerant stations */
+ int num_sta_ht40_intolerant;
+
/* Overlapping BSS information */
int olbc_ht;
@@ -315,21 +351,19 @@
/* lowest observed noise floor in dBm */
s8 lowest_nf;
+ unsigned int dfs_cac_ms;
+ struct os_reltime dfs_cac_start;
+
+ /* Latched with the actual secondary channel information and will be
+ * used while juggling between HT20 and HT40 modes. */
+ int secondary_ch;
+
#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;
+ int num_ht40_scan_tries;
};
/* hostapd.c */
@@ -345,6 +379,11 @@
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);
@@ -353,6 +392,15 @@
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_switch_channel_fallback(struct hostapd_iface *iface,
+ const struct hostapd_freq_params *freq_params);
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -374,7 +422,7 @@
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 offset, int width, int cf1, int cf2);
const struct hostapd_eap_user *
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 45d518b..d7909fa 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -1,7 +1,7 @@
/*
* Hotspot 2.0 AP ANQP processing
* Copyright (c) 2009, Atheros Communications, Inc.
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,19 +13,165 @@
#include "common/ieee802_11_defs.h"
#include "hostapd.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "hs20.h"
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
{
+ u8 conf;
if (!hapd->conf->hs20)
return eid;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
- *eid++ = 5;
+ *eid++ = 7;
WPA_PUT_BE24(eid, OUI_WFA);
eid += 3;
*eid++ = HS20_INDICATION_OUI_TYPE;
- /* Hotspot Configuration: DGAF Enabled */
- *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00;
+ conf = HS20_VERSION; /* Release Number */
+ conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+ if (hapd->conf->disable_dgaf)
+ conf |= HS20_DGAF_DISABLED;
+ *eid++ = conf;
+ WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+ eid += 2;
+
return eid;
}
+
+
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *len;
+ u16 capab;
+
+ if (!hapd->conf->osen)
+ return eid;
+
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = eid++; /* to be filled */
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ eid += RSN_SELECTOR_LEN;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+ eid += RSN_SELECTOR_LEN;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+ eid += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+ if (hapd->conf->wmm_enabled) {
+ /* 4 PTKSA replay counters when using WMM */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+ }
+#ifdef CONFIG_IEEE80211W
+ if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ capab |= WPA_CAPABILITY_MFPC;
+ if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+ *len = eid - len - 1;
+
+ return eid;
+}
+
+
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+ u8 osu_method, const char *url)
+{
+ struct wpabuf *buf;
+ size_t len = 0;
+ int ret;
+
+ /* TODO: should refuse to send notification if the STA is not associated
+ * or if the STA did not indicate support for WNM-Notification */
+
+ if (url) {
+ len = 1 + os_strlen(url);
+ if (5 + len > 255) {
+ wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
+ "WNM-Notification: '%s'", url);
+ return -1;
+ }
+ }
+
+ buf = wpabuf_alloc(4 + 7 + len);
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Subscription Remediation subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 5 + len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
+ if (url) {
+ wpabuf_put_u8(buf, len - 1);
+ wpabuf_put_data(buf, url, len - 1);
+ wpabuf_put_u8(buf, osu_method);
+ } else {
+ /* Server URL and Server Method fields not included */
+ wpabuf_put_u8(buf, 0);
+ }
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+ int ret;
+
+ /* TODO: should refuse to send notification if the STA is not associated
+ * or if the STA did not indicate support for WNM-Notification */
+
+ buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Deauthentication Imminent Notice subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
+ wpabuf_put_buf(buf, payload);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
diff --git a/src/ap/hs20.h b/src/ap/hs20.h
index 98698ce..152439f 100644
--- a/src/ap/hs20.h
+++ b/src/ap/hs20.h
@@ -1,6 +1,6 @@
/*
* Hotspot 2.0 AP ANQP processing
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -12,5 +12,11 @@
struct hostapd_data;
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+ u8 osu_method, const char *url);
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload);
#endif /* HS20_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8a239f4..4e66d1b 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -4,14 +4,8 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -20,11 +14,13 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
+#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
#include "acs.h"
+#include "ieee802_11.h"
+#include "beacon.h"
#include "hw_features.h"
@@ -45,6 +41,36 @@
}
+#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];
@@ -71,30 +97,43 @@
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. In addition,
- * disable all channels that require radar detection,
- * since that (in addition to full DFS) is not yet
- * supported.
+ * IBSS operation or active scanning.
+ * Use radar channels only if the driver supports DFS.
*/
- if (feature->channels[j].flag &
- (HOSTAPD_CHAN_NO_IBSS |
- HOSTAPD_CHAN_PASSIVE_SCAN |
- HOSTAPD_CHAN_RADAR))
+ if ((feature->channels[j].flag &
+ HOSTAPD_CHAN_RADAR) && dfs_enabled) {
+ dfs = 1;
+ } else if (((feature->channels[j].flag &
+ HOSTAPD_CHAN_RADAR) &&
+ !(iface->drv_flags &
+ WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
+ (feature->channels[j].flag &
+ (HOSTAPD_CHAN_NO_IBSS |
+ HOSTAPD_CHAN_PASSIVE_SCAN))) {
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",
+ "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);
+ feature->channels[j].max_tx_power,
+ dfs ? dfs_info(&feature->channels[j]) : "");
}
}
@@ -230,7 +269,7 @@
first = sec_chan;
ok = 0;
- for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) {
+ for (k = 0; k < ARRAY_SIZE(allowed); k++) {
if (first == allowed[k]) {
ok = 1;
break;
@@ -271,8 +310,8 @@
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) {
+ *pri_chan = oper->primary_chan;
+ if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
int sec = oper->ht_param &
HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
@@ -293,7 +332,7 @@
int match;
pri_chan = iface->conf->channel;
- sec_chan = iface->conf->secondary_channel * 4;
+ sec_chan = pri_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;
@@ -317,6 +356,7 @@
"channel to get secondary channel with no Beacons "
"from other BSSes");
ieee80211n_switch_pri_sec(iface);
+ return 1;
}
/*
@@ -355,6 +395,36 @@
}
+static int ieee80211n_check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq,
+ int start, int end)
+{
+ struct ieee802_11_elems elems;
+ struct ieee80211_ht_operation *oper;
+
+ if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
+ return 0;
+
+ ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+ if (!elems.ht_capabilities) {
+ wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
+ MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+ return 1;
+ }
+
+ if (elems.ht_operation &&
+ elems.ht_operation_len >= sizeof(*oper)) {
+ oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+ if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
+ MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+ return 1;
+ }
+ return 0;
+}
+
+
static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
struct wpa_scan_results *scan_res)
{
@@ -376,6 +446,15 @@
int pri = bss->freq;
int sec = pri;
int sec_chan, pri_chan;
+ struct ieee802_11_elems elems;
+
+ /* Check for overlapping 20 MHz BSS */
+ if (ieee80211n_check_20mhz_bss(bss, pri_freq, affected_start,
+ affected_end)) {
+ wpa_printf(MSG_DEBUG,
+ "Overlapping 20 MHz BSS is found");
+ return 0;
+ }
ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
@@ -407,7 +486,23 @@
}
}
- /* TODO: 40 MHz intolerant */
+ ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
+ 0);
+ if (elems.ht_capabilities &&
+ elems.ht_capabilities_len >=
+ sizeof(struct ieee80211_ht_capabilities)) {
+ struct ieee80211_ht_capabilities *ht_cap =
+ (struct ieee80211_ht_capabilities *)
+ elems.ht_capabilities;
+
+ if (le_to_host16(ht_cap->ht_capabilities_info) &
+ HT_CAP_INFO_40MHZ_INTOLERANT) {
+ wpa_printf(MSG_DEBUG,
+ "40 MHz Intolerant is set on channel %d in BSS "
+ MACSTR, pri, MAC2STR(bss->bssid));
+ return 0;
+ }
+ }
}
return 1;
@@ -437,6 +532,7 @@
oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
wpa_scan_results_free(scan_res);
+ iface->secondary_ch = iface->conf->secondary_channel;
if (!oper40) {
wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
"channel pri=%d sec=%d based on overlapping BSSes",
@@ -444,9 +540,21 @@
iface->conf->channel +
iface->conf->secondary_channel * 4);
iface->conf->secondary_channel = 0;
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
+ /*
+ * TODO: Could consider scheduling another scan to check
+ * if channel width can be changed if no coex reports
+ * are received from associating stations.
+ */
+ }
}
res = ieee80211n_allowed_ht40_channel_pair(iface);
+ if (!res) {
+ iface->conf->secondary_channel = 0;
+ wpa_printf(MSG_INFO, "Fallback to 20 MHz");
+ }
+
hostapd_setup_interface_complete(iface, !res);
}
@@ -491,25 +599,128 @@
}
+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 void ap_ht40_scan_retry(void *eloop_data, void *user_data)
+{
+#define HT2040_COEX_SCAN_RETRY 15
+ struct hostapd_iface *iface = eloop_data;
+ struct wpa_driver_scan_params params;
+ int ret;
+
+ 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);
+
+ ret = hostapd_driver_scan(iface->bss[0], ¶ms);
+ iface->num_ht40_scan_tries++;
+ os_free(params.freqs);
+
+ if (ret == -EBUSY &&
+ iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
+ ret, strerror(-ret), iface->num_ht40_scan_tries);
+ eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+ return;
+ }
+
+ if (ret == 0) {
+ iface->scan_cb = ieee80211n_check_scan;
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "Failed to request a scan in device, bringing up in HT20 mode");
+ iface->conf->secondary_channel = 0;
+ iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ hostapd_setup_interface_complete(iface, 0);
+}
+
+
+void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+ eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+}
+
+
static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
{
struct wpa_driver_scan_params params;
+ int ret;
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);
- 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);
+ else
+ ieee80211n_scan_channels_5g(iface, ¶ms);
+
+ ret = hostapd_driver_scan(iface->bss[0], ¶ms);
+ os_free(params.freqs);
+
+ if (ret == -EBUSY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
+ ret, strerror(-ret));
+ iface->num_ht40_scan_tries = 1;
+ eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+ eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+ return 1;
+ }
+
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s)",
+ ret, strerror(-ret));
return -1;
}
- os_free(params.freqs);
iface->scan_cb = ieee80211n_check_scan;
return 1;
@@ -597,12 +808,6 @@
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 "
@@ -613,6 +818,92 @@
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_MAX);
+ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
+ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+ VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+ VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
+
+#undef VHT_CAP_CHECK
+#undef VHT_CAP_CHECK_MAX
+
+ return 1;
+}
+#endif /* CONFIG_IEEE80211AC */
+
#endif /* CONFIG_IEEE80211N */
@@ -624,6 +915,10 @@
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;
@@ -718,33 +1013,45 @@
}
-int hostapd_acs_completed(struct hostapd_iface *iface)
+int hostapd_acs_completed(struct hostapd_iface *iface, int err)
{
- int ret;
+ 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);
- return -1;
+ 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);
- return -1;
+ goto out;
}
ret = hostapd_check_ht_capab(iface);
if (ret < 0)
- return -1;
+ goto out;
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
return 0;
}
- return hostapd_setup_interface_complete(iface, 0);
+ ret = 0;
+out:
+ return hostapd_setup_interface_complete(iface, ret);
}
@@ -763,6 +1070,15 @@
if (iface->num_hw_features < 1)
return -1;
+ if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ iface->conf->ieee80211n || iface->conf->ieee80211ac) &&
+ iface->conf->channel == 14) {
+ wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14");
+ iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+ iface->conf->ieee80211n = 0;
+ iface->conf->ieee80211ac = 0;
+ }
+
iface->current_mode = NULL;
for (i = 0; i < iface->num_hw_features; i++) {
struct hostapd_hw_modes *mode = &iface->hw_features[i];
@@ -793,8 +1109,6 @@
hostapd_notify_bad_chans(iface);
return -3;
}
-
- return 0;
}
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index abadcd1..0f67ab8 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -4,14 +4,8 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef HW_FEATURES_H
@@ -21,6 +15,7 @@
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);
@@ -28,6 +23,7 @@
int hostapd_check_ht_capab(struct hostapd_iface *iface);
int hostapd_prepare_rates(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode);
+void hostapd_stop_setup_timers(struct hostapd_iface *iface);
#else /* NEED_AP_MLME */
static inline void
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -66,6 +62,10 @@
return 0;
}
+static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+}
+
#endif /* NEED_AP_MLME */
#endif /* HW_FEATURES_H */
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
index be55c69..9b2900f 100644
--- a/src/ap/iapp.c
+++ b/src/ap/iapp.c
@@ -204,7 +204,7 @@
addr.sin_port = htons(IAPP_UDP_PORT);
if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
(struct sockaddr *) &addr, sizeof(addr)) < 0)
- perror("sendto[IAPP-ADD]");
+ wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
}
@@ -231,7 +231,7 @@
* FIX: what is correct RW with 802.11? */
if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
- perror("send[L2 Update]");
+ wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
}
@@ -242,29 +242,22 @@
*/
void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
{
- struct ieee80211_mgmt *assoc;
- u16 seq;
+ u16 seq = 0; /* TODO */
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 */
- }
+ /* TODO: If this was reassociation:
+ * 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 */
}
@@ -276,8 +269,8 @@
struct sta_info *sta;
if (len != sizeof(*add)) {
- printf("Invalid IAPP-ADD packet length %d (expected %lu)\n",
- len, (unsigned long) sizeof(*add));
+ wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
+ len, (unsigned long) sizeof(*add));
return;
}
@@ -326,7 +319,8 @@
len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
(struct sockaddr *) &from, &fromlen);
if (len < 0) {
- perror("recvfrom");
+ wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
+ strerror(errno));
return;
}
@@ -350,17 +344,18 @@
hdr->version, hdr->command,
be_to_host16(hdr->identifier), hlen);
if (hdr->version != IAPP_VERSION) {
- printf("Dropping IAPP frame with unknown version %d\n",
- hdr->version);
+ wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
+ hdr->version);
return;
}
if (hlen > len) {
- printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len);
+ wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
+ hlen, len);
return;
}
if (hlen < len) {
- printf("Ignoring %d extra bytes from IAPP frame\n",
- len - hlen);
+ wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
+ len - hlen);
len = hlen;
}
@@ -376,7 +371,7 @@
/* TODO: process */
break;
default:
- printf("Unknown IAPP command %d\n", hdr->command);
+ wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
break;
}
}
@@ -403,7 +398,8 @@
iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (iapp->udp_sock < 0) {
- perror("socket[PF_INET,SOCK_DGRAM]");
+ wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
@@ -411,35 +407,38 @@
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
- perror("ioctl(SIOCGIFINDEX)");
+ 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) {
- perror("ioctl(SIOCGIFADDR)");
+ 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) {
- printf("Invalid address family %i (SIOCGIFADDR)\n",
- paddr->sin_family);
+ 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) {
- perror("ioctl(SIOCGIFBRDADDR)");
+ 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) {
- printf("Invalid address family %i (SIOCGIFBRDADDR)\n",
- paddr->sin_family);
+ wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
+ paddr->sin_family);
iapp_deinit(iapp);
return NULL;
}
@@ -450,7 +449,8 @@
uaddr.sin_port = htons(IAPP_UDP_PORT);
if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
sizeof(uaddr)) < 0) {
- perror("bind[UDP]");
+ wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
@@ -461,14 +461,16 @@
mreq.imr_ifindex = 0;
if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(mreq)) < 0) {
- perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]");
+ 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) {
- perror("socket[PF_PACKET,SOCK_RAW]");
+ wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
@@ -478,19 +480,20 @@
addr.sll_ifindex = ifindex;
if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
- perror("bind[PACKET]");
+ 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)) {
- printf("Could not register read socket for IAPP.\n");
+ wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
iapp_deinit(iapp);
return NULL;
}
- printf("IEEE 802.11F (IAPP) using interface %s\n", iface);
+ 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
@@ -515,7 +518,8 @@
mreq.imr_ifindex = 0;
if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0) {
- perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]");
+ wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
+ strerror(errno));
}
eloop_unregister_read_sock(iapp->udp_sock);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 781f826..de1ee5e 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -15,7 +15,6 @@
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
-#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
@@ -39,6 +38,7 @@
#include "ap_drv_ops.h"
#include "wnm_ap.h"
#include "ieee802_11.h"
+#include "dfs.h"
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -62,7 +62,6 @@
}
*pos++ = num;
- count = 0;
for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
i++) {
count++;
@@ -105,7 +104,6 @@
*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++;
@@ -138,6 +136,15 @@
{
int capab = WLAN_CAPABILITY_ESS;
int privacy;
+ int dfs;
+
+ /* Check if any of configured channels require DFS */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+ if (dfs < 0) {
+ wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+ dfs);
+ dfs = 0;
+ }
if (hapd->iface->num_sta_no_short_preamble == 0 &&
hapd->iconf->preamble == SHORT_PREAMBLE)
@@ -153,6 +160,11 @@
if (hapd->conf->wpa)
privacy = 1;
+#ifdef CONFIG_HS20
+ if (hapd->conf->osen)
+ privacy = 1;
+#endif /* CONFIG_HS20 */
+
if (sta) {
int policy, def_klen;
if (probe && sta->ssid_probe) {
@@ -175,25 +187,21 @@
hapd->iface->num_sta_no_short_slot_time == 0)
capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+ /*
+ * Currently, Spectrum Management capability bit is set when directly
+ * requested in configuration by spectrum_mgmt_required or when AP is
+ * running on DFS channel.
+ * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
+ */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ (hapd->iconf->spectrum_mgmt_required || dfs))
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
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)
@@ -228,7 +236,8 @@
/* Transaction 3 */
if (!iswep || !sta->challenge || !challenge ||
- os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) {
+ os_memcmp_const(sta->challenge, challenge,
+ WLAN_AUTH_CHALLENGE_LEN)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"shared key authentication - invalid "
@@ -239,13 +248,8 @@
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication OK (shared key)");
-#ifdef IEEE80211_REQUIRE_AUTH_ACK
- /* Station will be marked authenticated if it ACKs the
- * authentication reply. */
-#else
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-#endif
os_free(sta->challenge);
sta->challenge = NULL;
@@ -286,7 +290,7 @@
MAC2STR(dst), auth_alg, auth_transaction,
resp, (unsigned long) ies_len);
if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
- perror("send_auth_reply: send");
+ wpa_printf(MSG_INFO, "send_auth_reply: send");
os_free(buf);
}
@@ -399,7 +403,7 @@
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)
+ os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
return -1;
return 0;
@@ -411,17 +415,17 @@
{
struct wpabuf *buf;
u8 *token;
- struct os_time t;
+ struct os_reltime now;
- os_get_time(&t);
- if (hapd->last_sae_token_key_update == 0 ||
- t.sec > hapd->last_sae_token_key_update + 60) {
+ 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 = t.sec;
+ hapd->last_sae_token_key_update = now;
}
buf = wpabuf_alloc(SHA256_MAC_LEN);
@@ -492,6 +496,7 @@
HOSTAPD_LEVEL_DEBUG,
"SAE confirm before commit");
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ goto failed;
}
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -523,6 +528,7 @@
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
}
+failed:
sta->auth_alg = WLAN_AUTH_SAE;
send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
@@ -552,13 +558,13 @@
char *radius_cui = NULL;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- printf("handle_auth - too short payload (len=%lu)\n",
- (unsigned long) len);
+ 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 &&
+ if (hapd->iconf->ignore_auth_probability > 0.0 &&
drand48() < hapd->iconf->ignore_auth_probability) {
wpa_printf(MSG_INFO,
"TESTING: ignoring auth frame from " MACSTR,
@@ -601,23 +607,23 @@
#endif /* CONFIG_SAE */
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
auth_alg == WLAN_AUTH_SHARED_KEY))) {
- printf("Unsupported authentication algorithm (%d)\n",
- auth_alg);
+ 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))) {
- printf("Unknown authentication transaction number (%d)\n",
- auth_transaction);
+ 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) {
- printf("Station " MACSTR " not allowed to authenticate.\n",
- MAC2STR(mgmt->sa));
+ wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+ MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -628,8 +634,8 @@
&psk, &identity, &radius_cui);
if (res == HOSTAPD_ACL_REJECT) {
- printf("Station " MACSTR " not allowed to authenticate.\n",
- MAC2STR(mgmt->sa));
+ wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+ MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -691,15 +697,10 @@
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication OK (open system)");
-#ifdef IEEE80211_REQUIRE_AUTH_ACK
- /* Station will be marked authenticated if it ACKs the
- * authentication reply. */
-#else
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);
-#endif
break;
case WLAN_AUTH_SHARED_KEY:
resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
@@ -792,12 +793,10 @@
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);
+ "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -859,6 +858,21 @@
}
+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)
{
@@ -881,6 +895,9 @@
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;
@@ -903,12 +920,17 @@
elems.vht_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+
+ resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
+ 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_UNSPECIFIED_FAILURE;
+ return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
}
#endif /* CONFIG_IEEE80211AC */
@@ -1050,7 +1072,9 @@
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sta->wpa_sm) &&
- sta->auth_alg != WLAN_AUTH_SAE) {
+ 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);
@@ -1069,6 +1093,29 @@
return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
}
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_HS20
+ } else if (hapd->conf->osen) {
+ if (elems.osen == NULL) {
+ hostapd_logger(
+ hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "No HS 2.0 OSEN element in association request");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+ 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_WARNING, "Failed to initialize WPA "
+ "state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+ elems.osen - 2, elems.osen_len + 2) < 0)
+ return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
} else
wpa_auth_sta_no_wpa(sta->wpa_sm);
@@ -1135,8 +1182,7 @@
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));
+ reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
/* Supported rates */
p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
/* Extended supported rates */
@@ -1169,6 +1215,8 @@
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);
@@ -1235,14 +1283,14 @@
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
- printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)"
- "\n", reassoc, (unsigned long) len);
+ 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 &&
+ if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
drand48() < hapd->iconf->ignore_reassoc_probability) {
wpa_printf(MSG_INFO,
"TESTING: ignoring reassoc request from "
@@ -1250,7 +1298,7 @@
return;
}
} else {
- if (hapd->iconf->ignore_assoc_probability > 0.0d &&
+ if (hapd->iconf->ignore_assoc_probability > 0.0 &&
drand48() < hapd->iconf->ignore_assoc_probability) {
wpa_printf(MSG_INFO,
"TESTING: ignoring assoc request from "
@@ -1396,17 +1444,6 @@
}
#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;
@@ -1422,8 +1459,8 @@
struct sta_info *sta;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
- printf("handle_disassoc - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)",
+ (unsigned long) len);
return;
}
@@ -1433,8 +1470,8 @@
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
- printf("Station " MACSTR " trying to disassociate, but it "
- "is not associated.\n", MAC2STR(mgmt->sa));
+ wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated",
+ MAC2STR(mgmt->sa));
return;
}
@@ -1508,8 +1545,8 @@
struct ieee802_11_elems elems;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
- printf("handle_beacon - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
+ (unsigned long) len);
return;
}
@@ -1524,9 +1561,9 @@
#ifdef CONFIG_IEEE80211W
-static void hostapd_sa_query_action(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt,
- size_t len)
+static int hostapd_sa_query_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
{
const u8 *end;
@@ -1535,12 +1572,13 @@
if (((u8 *) mgmt) + len < end) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
"frame (len=%lu)", (unsigned long) len);
- return;
+ 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;
}
@@ -1552,29 +1590,8 @@
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
-static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta,
- const struct ieee80211_mgmt *mgmt,
- size_t len)
-{
- struct rx_action action;
- if (len < IEEE80211_HDRLEN + 2)
- return;
- os_memset(&action, 0, sizeof(action));
- action.da = mgmt->da;
- action.sa = mgmt->sa;
- action.bssid = mgmt->bssid;
- action.category = mgmt->u.action.category;
- action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action;
- action.len = len - IEEE80211_HDRLEN - 1;
- action.freq = hapd->iface->freq;
- ieee802_11_rx_wnm_action_ap(hapd, &action);
-}
-#endif /* CONFIG_WNM */
-
-
-static void handle_action(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+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);
@@ -1584,7 +1601,7 @@
HOSTAPD_LEVEL_DEBUG,
"handle_action - too short payload (len=%lu)",
(unsigned long) len);
- return;
+ return 0;
}
if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
@@ -1592,43 +1609,53 @@
wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
"frame (category=%u) from unassociated STA " MACSTR,
MAC2STR(mgmt->sa), mgmt->u.action.category);
- return;
+ 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))) {
+ !(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;
+ 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,
+ if (!sta ||
+ wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
len - IEEE80211_HDRLEN))
break;
- return;
+ return 1;
#endif /* CONFIG_IEEE80211R */
case WLAN_ACTION_WMM:
hostapd_wmm_action(hapd, mgmt, len);
- return;
+ return 1;
#ifdef CONFIG_IEEE80211W
case WLAN_ACTION_SA_QUERY:
- hostapd_sa_query_action(hapd, mgmt, len);
- return;
+ return hostapd_sa_query_action(hapd, mgmt, len);
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WNM
case WLAN_ACTION_WNM:
- hostapd_wnm_action(hapd, sta, mgmt, len);
- return;
+ ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
+ return 1;
#endif /* CONFIG_WNM */
case WLAN_ACTION_PUBLIC:
+ case WLAN_ACTION_PROTECTED_DUAL:
+#ifdef CONFIG_IEEE80211N
+ if (mgmt->u.action.u.public_action.action ==
+ WLAN_PA_20_40_BSS_COEX) {
+ wpa_printf(MSG_DEBUG,
+ "HT20/40 coex mgmt frame received from STA "
+ MACSTR, MAC2STR(mgmt->sa));
+ hostapd_2040_coex_action(hapd, mgmt, len);
+ }
+#endif /* CONFIG_IEEE80211N */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len,
@@ -1640,14 +1667,14 @@
hapd->iface->freq);
}
if (hapd->public_action_cb || hapd->public_action_cb2)
- return;
+ 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;
+ return 1;
}
break;
}
@@ -1670,7 +1697,7 @@
"frame back to sender");
resp = os_malloc(len);
if (resp == NULL)
- return;
+ return 0;
os_memcpy(resp, mgmt, len);
os_memcpy(resp->da, resp->sa, ETH_ALEN);
os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
@@ -1683,6 +1710,8 @@
}
os_free(resp);
}
+
+ return 1;
}
@@ -1699,15 +1728,16 @@
* addition, it can be called to re-inserted pending frames (e.g., when using
* external RADIUS server as an MAC ACL).
*/
-void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
- struct hostapd_frame_info *fi)
+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;
if (len < 24)
- return;
+ return 0;
mgmt = (struct ieee80211_mgmt *) buf;
fc = le_to_host16(mgmt->frame_control);
@@ -1715,7 +1745,7 @@
if (stype == WLAN_FC_STYPE_BEACON) {
handle_beacon(hapd, mgmt, len, fi);
- return;
+ return 1;
}
broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
@@ -1729,15 +1759,15 @@
stype == WLAN_FC_STYPE_ACTION) &&
#endif /* CONFIG_P2P */
os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
- printf("MGMT: BSSID=" MACSTR " not our address\n",
- MAC2STR(mgmt->bssid));
- return;
+ 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;
+ return 1;
}
if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
@@ -1745,33 +1775,38 @@
HOSTAPD_LEVEL_DEBUG,
"MGMT: DA=" MACSTR " not our address",
MAC2STR(mgmt->da));
- return;
+ 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");
- handle_action(hapd, mgmt, len);
+ ret = handle_action(hapd, mgmt, len);
break;
default:
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1779,6 +1814,8 @@
"unknown mgmt frame subtype %d", stype);
break;
}
+
+ return ret;
}
@@ -1797,8 +1834,8 @@
}
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- printf("handle_auth_cb - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+ (unsigned long) len);
return;
}
@@ -1808,8 +1845,8 @@
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
- printf("handle_auth_cb: STA " MACSTR " not found\n",
- MAC2STR(mgmt->da));
+ wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+ MAC2STR(mgmt->da));
return;
}
@@ -1859,15 +1896,15 @@
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
sizeof(mgmt->u.assoc_resp))) {
- printf("handle_assoc_cb(reassoc=%d) - too short payload "
- "(len=%lu)\n", reassoc, (unsigned long) len);
+ 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) {
- printf("handle_assoc_cb: STA " MACSTR " not found\n",
- MAC2STR(mgmt->da));
+ wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
+ MAC2STR(mgmt->da));
return;
}
@@ -1885,7 +1922,7 @@
status = le_to_host16(mgmt->u.assoc_resp.status_code);
if (status != WLAN_STATUS_SUCCESS)
- goto fail;
+ return;
/* Stop previous accounting session, if one is started, and allocate
* new session id for the new session. */
@@ -1899,7 +1936,8 @@
if (sta->flags & WLAN_STA_ASSOC)
new_assoc = 0;
sta->flags |= WLAN_STA_ASSOC;
- if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
sta->auth_alg == WLAN_AUTH_FT) {
/*
* Open, static WEP, or FT protocol; no separate authorization
@@ -1938,7 +1976,7 @@
sta->listen_interval,
sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
- sta->flags, sta->qosinfo)) {
+ sta->flags, sta->qosinfo, sta->vht_opmode)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
"Could not add STA to kernel driver");
@@ -1946,7 +1984,7 @@
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_DISASSOC_AP_BUSY);
- goto fail;
+ return;
}
if (sta->flags & WLAN_STA_WDS) {
@@ -1966,11 +2004,11 @@
* interface selection is not going to change anymore.
*/
if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
- goto fail;
+ return;
} 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;
+ return;
}
hostapd_set_sta_flags(hapd, sta);
@@ -1982,13 +2020,6 @@
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;
- }
}
@@ -2055,6 +2086,14 @@
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");
@@ -2083,7 +2122,7 @@
wpa_printf(MSG_DEBUG, "mgmt::action cb");
break;
default:
- printf("unknown mgmt cb frame subtype %d\n", stype);
+ wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
break;
}
}
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 2aab56d..cf0d3f2 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -14,12 +14,14 @@
struct sta_info;
struct hostapd_frame_info;
struct ieee80211_ht_capabilities;
+struct ieee80211_mgmt;
-void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
- struct hostapd_frame_info *fi);
+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);
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t 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,
@@ -40,7 +42,9 @@
#endif /* NEED_AP_MLME */
u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
int probe);
+void ap_ht2040_timeout(void *eloop_data, void *user_data);
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);
@@ -59,8 +63,12 @@
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);
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
+void ht40_intolerant_remove(struct hostapd_iface *iface, 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);
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_opmode);
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,
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index c311e55..56c3ce0 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -29,7 +29,7 @@
struct hostapd_cached_radius_acl {
- os_time_t timestamp;
+ struct os_reltime timestamp;
macaddr addr;
int accepted; /* HOSTAPD_ACL_* */
struct hostapd_cached_radius_acl *next;
@@ -43,7 +43,7 @@
struct hostapd_acl_query_data {
- os_time_t timestamp;
+ struct os_reltime timestamp;
u8 radius_id;
macaddr addr;
u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
@@ -104,15 +104,16 @@
char **identity, char **radius_cui)
{
struct hostapd_cached_radius_acl *entry;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&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 (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
+ 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)
@@ -265,7 +266,6 @@
return HOSTAPD_ACL_REJECT;
#else /* CONFIG_NO_RADIUS */
struct hostapd_acl_query_data *query;
- struct os_time t;
/* Check whether ACL cache has an entry for this station */
int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
@@ -305,8 +305,7 @@
wpa_printf(MSG_ERROR, "malloc for query data failed");
return HOSTAPD_ACL_REJECT;
}
- os_get_time(&t);
- query->timestamp = t.sec;
+ 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 "
@@ -338,7 +337,8 @@
#ifndef CONFIG_NO_RADIUS
-static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
+ struct os_reltime *now)
{
struct hostapd_cached_radius_acl *prev, *entry, *tmp;
@@ -346,7 +346,8 @@
entry = hapd->acl_cache;
while (entry) {
- if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+ 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)
@@ -367,7 +368,7 @@
static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
- os_time_t now)
+ struct os_reltime *now)
{
struct hostapd_acl_query_data *prev, *entry, *tmp;
@@ -375,7 +376,8 @@
entry = hapd->acl_queries;
while (entry) {
- if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+ 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)
@@ -403,11 +405,11 @@
static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
- hostapd_acl_expire_cache(hapd, now.sec);
- hostapd_acl_expire_queries(hapd, now.sec);
+ 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);
}
@@ -480,7 +482,6 @@
struct hostapd_acl_query_data *query, *prev;
struct hostapd_cached_radius_acl *cache;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
- struct os_time t;
query = hapd->acl_queries;
prev = NULL;
@@ -515,8 +516,7 @@
wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
goto done;
}
- os_get_time(&t);
- cache->timestamp = t.sec;
+ os_get_reltime(&cache->timestamp);
os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
u8 *buf;
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 6483e1c..fe87883 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -3,26 +3,22 @@
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2008, Intel Corporation
*
- * 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.
+ * 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 "drivers/driver.h"
#include "hostapd.h"
#include "ap_config.h"
#include "sta_info.h"
#include "beacon.h"
#include "ieee802_11.h"
+#include "hw_features.h"
+#include "ap_drv_ops.h"
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
@@ -50,6 +46,35 @@
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);
+
+ /* Fill in default values for remaining parameters
+ * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */
+ scan_params->scan_passive_dwell =
+ host_to_le16(20);
+ scan_params->scan_active_dwell =
+ host_to_le16(10);
+ scan_params->scan_passive_total_per_channel =
+ host_to_le16(200);
+ scan_params->scan_active_total_per_channel =
+ host_to_le16(20);
+ scan_params->channel_transition_delay_factor =
+ host_to_le16(5);
+ scan_params->scan_activity_threshold =
+ host_to_le16(25);
+
+ pos += sizeof(*scan_params);
+ }
+
return pos;
}
@@ -68,14 +93,14 @@
oper = (struct ieee80211_ht_operation *) pos;
os_memset(oper, 0, sizeof(*oper));
- oper->control_chan = hapd->iconf->channel;
+ oper->primary_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;
+ HT_INFO_HT_PARAM_STA_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;
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
pos += sizeof(*oper);
@@ -105,44 +130,40 @@
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)
+ if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT)
&& iface->num_sta_ht_no_gf) {
- iface->ht_op_mode |=
- HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+ iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
op_mode_changes++;
} else if ((iface->ht_op_mode &
- HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) &&
+ HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
iface->num_sta_ht_no_gf == 0) {
- iface->ht_op_mode &=
- ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
op_mode_changes++;
}
- if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+ if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
(iface->num_sta_no_ht || iface->olbc_ht)) {
- iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+ iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
op_mode_changes++;
} else if ((iface->ht_op_mode &
- HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+ HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
(iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
- iface->ht_op_mode &=
- ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
op_mode_changes++;
}
- new_op_mode = 0;
if (iface->num_sta_no_ht)
- new_op_mode = OP_MODE_MIXED;
+ new_op_mode = HT_PROT_NON_HT_MIXED;
else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
- new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
+ new_op_mode = HT_PROT_20MHZ_PROTECTION;
else if (iface->olbc_ht)
- new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
+ new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
else
- new_op_mode = OP_MODE_PURE;
+ new_op_mode = HT_PROT_NO_PROTECTION;
- cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+ cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK;
if (cur_op_mode != new_op_mode) {
- iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK;
iface->ht_op_mode |= new_op_mode;
op_mode_changes++;
}
@@ -154,6 +175,115 @@
}
+static int is_40_allowed(struct hostapd_iface *iface, int channel)
+{
+ int pri_freq, sec_freq;
+ int affected_start, affected_end;
+ int pri = 2407 + 5 * channel;
+
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return 1;
+
+ 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;
+ if ((pri < affected_start || pri > affected_end))
+ return 1; /* not within affected channel range */
+
+ wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
+ affected_start, affected_end);
+ wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
+ return 0;
+}
+
+
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct ieee80211_2040_bss_coex_ie *bc_ie;
+ struct ieee80211_2040_intol_chan_report *ic_report;
+ int is_ht_allowed = 1;
+ int i;
+ const u8 *data = ((const u8 *) mgmt) + 1;
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
+ mgmt->u.action.u.public_action.action);
+
+ if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ return;
+
+ if (len < IEEE80211_HDRLEN + 1)
+ return;
+ data++;
+
+ bc_ie = (struct ieee80211_2040_bss_coex_ie *) &data[0];
+ ic_report = (struct ieee80211_2040_intol_chan_report *)
+ (&data[0] + sizeof(*bc_ie));
+
+ if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "20 MHz BSS width request bit is set in BSS coexistence information field");
+ is_ht_allowed = 0;
+ }
+
+ if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "40 MHz intolerant bit is set in BSS coexistence information field");
+ is_ht_allowed = 0;
+ }
+
+ if (ic_report &&
+ (ic_report->element_id == WLAN_EID_20_40_BSS_INTOLERANT)) {
+ /* Go through the channel report to find any BSS there in the
+ * affected channel range */
+ for (i = 0; i < ic_report->length - 1; i++) {
+ if (is_40_allowed(iface, ic_report->variable[i]))
+ continue;
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "20_40_INTOLERANT channel %d reported",
+ ic_report->variable[i]);
+ is_ht_allowed = 0;
+ break;
+ }
+ }
+
+ if (!is_ht_allowed &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ if (iface->conf->secondary_channel) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Switching to 20 MHz operation");
+ iface->conf->secondary_channel = 0;
+ ieee802_11_set_beacons(iface);
+ }
+ if (!iface->num_sta_ht40_intolerant) {
+ unsigned int delay_time;
+ delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+ iface->conf->obss_interval;
+ eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
+ NULL);
+ eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+ hapd->iface, NULL);
+ }
+ }
+}
+
+
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab, size_t ht_capab_len)
{
@@ -182,6 +312,52 @@
}
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+{
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return;
+
+ wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+ " in Association Request", MAC2STR(sta->addr));
+
+ if (sta->ht40_intolerant_set)
+ return;
+
+ sta->ht40_intolerant_set = 1;
+ iface->num_sta_ht40_intolerant++;
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+
+ if (iface->conf->secondary_channel &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ iface->conf->secondary_channel = 0;
+ ieee802_11_set_beacons(iface);
+ }
+}
+
+
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
+{
+ if (!sta->ht40_intolerant_set)
+ return;
+
+ sta->ht40_intolerant_set = 0;
+ iface->num_sta_ht40_intolerant--;
+
+ if (iface->num_sta_ht40_intolerant == 0 &&
+ (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+ iface->conf->obss_interval;
+ wpa_printf(MSG_DEBUG,
+ "HT: Start 20->40 MHz transition timer (%d seconds)",
+ delay_time);
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+ eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+ iface, NULL);
+ }
+}
+
+
static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
{
u16 ht_capab;
@@ -209,6 +385,9 @@
__func__, MAC2STR(sta->addr),
hapd->iface->num_sta_ht_20mhz);
}
+
+ if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
+ ht40_intolerant_add(hapd->iface, sta);
}
@@ -270,3 +449,14 @@
neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
}
+
+
+void ap_ht2040_timeout(void *eloop_data, void *user_data)
+{
+ struct hostapd_iface *iface = eloop_data;
+
+ wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
+
+ iface->conf->secondary_channel = iface->secondary_ch;
+ ieee802_11_set_beacons(iface);
+}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index c36bbe3..12403f9 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -24,13 +24,13 @@
{
u8 *pos = eid;
u32 timeout, tu;
- struct os_time now, passed;
+ struct os_reltime now, passed;
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
- os_get_time(&now);
- os_time_sub(&now, &sta->sa_query_start, &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)
timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
@@ -69,7 +69,7 @@
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)
- perror("ieee802_11_send_sa_query_req: send");
+ wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
}
@@ -107,7 +107,7 @@
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)
- perror("ieee80211_mgmt_sa_query_request: send");
+ wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
}
@@ -170,6 +170,8 @@
switch (idx) {
case 0: /* Bits 0-7 */
+ if (hapd->iconf->obss_interval)
+ *pos |= 0x01; /* Bit 0 - Coexistence management */
break;
case 1: /* Bits 8-15 */
break;
@@ -189,6 +191,8 @@
*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) {
@@ -197,6 +201,10 @@
}
break;
case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
break;
case 6: /* Bits 48-55 */
if (hapd->conf->ssid.utf8_ssid)
@@ -217,12 +225,18 @@
len = 4;
if (len < 3 && hapd->conf->wnm_sleep_mode)
len = 3;
+ if (len < 1 && hapd->iconf->obss_interval)
+ len = 1;
if (len < 7 && hapd->conf->ssid.utf8_ssid)
len = 7;
#ifdef CONFIG_WNM
if (len < 4)
len = 4;
#endif /* CONFIG_WNM */
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20 && len < 6)
+ len = 6;
+#endif /* CONFIG_HS20 */
if (len < hapd->iface->extended_capa_len)
len = hapd->iface->extended_capa_len;
if (len == 0)
@@ -250,6 +264,23 @@
}
+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;
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 0012c0f..437cf50 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -12,7 +12,6 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
-#include "drivers/driver.h"
#include "hostapd.h"
#include "ap_config.h"
#include "sta_info.h"
@@ -35,7 +34,7 @@
cap = (struct ieee80211_vht_capabilities *) pos;
os_memset(cap, 0, sizeof(*cap));
cap->vht_capabilities_info = host_to_le32(
- hapd->iface->current_mode->vht_capab);
+ hapd->iface->conf->vht_capab);
/* Supported MCS set comes from hw */
os_memcpy(&cap->vht_supported_mcs_set,
@@ -109,13 +108,79 @@
return WLAN_STATUS_SUCCESS;
}
+
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper_notif)
+{
+ if (!vht_oper_notif) {
+ sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
+ sta->vht_opmode = *vht_oper_notif;
+ 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));
- /* TODO: mask own capabilities, like get_ht_capab() */
+ 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);
}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 9867581..e4681e9 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -29,11 +29,14 @@
#include "pmksa_cache_auth.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "wps_hostapd.h"
+#include "hs20.h"
#include "ieee802_1x.h"
static void ieee802_1x_finished(struct hostapd_data *hapd,
- struct sta_info *sta, int success);
+ struct sta_info *sta, int success,
+ int remediation);
static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
@@ -96,12 +99,13 @@
}
if (res && errno != ENOENT) {
- printf("Could not set station " MACSTR " flags for kernel "
- "driver (errno=%d).\n", MAC2STR(sta->addr), errno);
+ wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
+ " flags for kernel driver (errno=%d).",
+ MAC2STR(sta->addr), errno);
}
if (authorized) {
- os_get_time(&sta->connected_time);
+ os_get_reltime(&sta->connected_time);
accounting_sta_start(hapd, sta);
}
}
@@ -302,6 +306,67 @@
}
+static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ u32 suite;
+ int ver, val;
+
+ ver = wpa_auth_sta_wpa_version(sta->wpa_sm);
+ val = wpa_auth_get_pairwise(sta->wpa_sm);
+ suite = wpa_cipher_to_suite(ver, val);
+ if (val != -1 &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher");
+ return -1;
+ }
+
+ suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ?
+ WPA_PROTO_RSN : WPA_PROTO_WPA,
+ hapd->conf->wpa_group);
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_GROUP_CIPHER) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher");
+ return -1;
+ }
+
+ val = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ suite = wpa_akm_to_suite(val);
+ if (val != -1 &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_AKM_SUITE) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite");
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN,
+ hapd->conf->group_mgmt_cipher);
+ if (!hostapd_config_get_radius_attr(
+ req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) &&
+ !radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) {
+ wpa_printf(MSG_ERROR,
+ "Could not add WLAN-Group-Mgmt-Cipher");
+ return -1;
+ }
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ return 0;
+}
+
+
static int add_common_radius_sta_attr(struct hostapd_data *hapd,
struct hostapd_radius_attr *req_attr,
struct sta_info *sta,
@@ -353,6 +418,25 @@
}
}
+#ifdef CONFIG_IEEE80211R
+ if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+ sta->wpa_sm &&
+ (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
+ sta->auth_alg == WLAN_AUTH_FT) &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_MOBILITY_DOMAIN_ID) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID,
+ WPA_GET_BE16(
+ hapd->conf->mobility_domain))) {
+ wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (hapd->conf->wpa && sta->wpa_sm &&
+ add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
+ return -1;
+
return 0;
}
@@ -416,6 +500,22 @@
return -1;
}
+#ifdef CONFIG_INTERWORKING
+ if (hapd->conf->interworking &&
+ !is_zero_ether_addr(hapd->conf->hessid)) {
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(hapd->conf->hessid));
+ buf[sizeof(buf) - 1] = '\0';
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_HESSID) &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID");
+ return -1;
+ }
+ }
+#endif /* CONFIG_INTERWORKING */
+
if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
return -1;
@@ -452,7 +552,7 @@
msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
sm->radius_identifier);
if (msg == NULL) {
- printf("Could not create net RADIUS packet\n");
+ wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
return;
}
@@ -461,7 +561,7 @@
if (sm->identity &&
!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
sm->identity, sm->identity_len)) {
- printf("Could not add User-Name\n");
+ wpa_printf(MSG_INFO, "Could not add User-Name");
goto fail;
}
@@ -475,12 +575,12 @@
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)) {
- printf("Could not add Framed-MTU\n");
+ wpa_printf(MSG_INFO, "Could not add Framed-MTU");
goto fail;
}
if (eap && !radius_msg_add_eap(msg, eap, len)) {
- printf("Could not add EAP-Message\n");
+ wpa_printf(MSG_INFO, "Could not add EAP-Message");
goto fail;
}
@@ -492,8 +592,7 @@
int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
RADIUS_ATTR_STATE);
if (res < 0) {
- printf("Could not copy State attribute from previous "
- "Access-Challenge\n");
+ wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge");
goto fail;
}
if (res > 0) {
@@ -520,6 +619,41 @@
}
}
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20) {
+ u8 ver = 1; /* Release 2 */
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
+ &ver, 1)) {
+ wpa_printf(MSG_ERROR, "Could not add HS 2.0 AP "
+ "version");
+ goto fail;
+ }
+
+ if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) {
+ const u8 *pos;
+ u8 buf[3];
+ u16 id;
+ pos = wpabuf_head_u8(sta->hs20_ie);
+ buf[0] = (*pos) >> 4;
+ if (((*pos) & HS20_PPS_MO_ID_PRESENT) &&
+ wpabuf_len(sta->hs20_ie) >= 3)
+ id = WPA_GET_LE16(pos + 1);
+ else
+ id = 0;
+ WPA_PUT_BE16(buf + 1, id);
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION,
+ buf, sizeof(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add HS 2.0 "
+ "STA version");
+ goto fail;
+ }
+ }
+ }
+#endif /* CONFIG_HS20 */
+
if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
goto fail;
@@ -543,7 +677,7 @@
data = (u8 *) (eap + 1);
if (len < sizeof(*eap) + 1) {
- printf("handle_eap_response: too short response data\n");
+ wpa_printf(MSG_INFO, "handle_eap_response: too short response data");
return;
}
@@ -571,7 +705,7 @@
u16 eap_len;
if (len < sizeof(*eap)) {
- printf(" too short EAP packet\n");
+ wpa_printf(MSG_INFO, " too short EAP packet");
return;
}
@@ -649,7 +783,7 @@
struct rsn_pmksa_cache_entry *pmksa;
int key_mgmt;
- if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
!hapd->conf->wps_state)
return;
@@ -664,7 +798,7 @@
}
if (len < sizeof(*hdr)) {
- printf(" too short IEEE 802.1X packet\n");
+ wpa_printf(MSG_INFO, " too short IEEE 802.1X packet");
return;
}
@@ -674,7 +808,7 @@
hdr->version, hdr->type, datalen);
if (len - sizeof(*hdr) < datalen) {
- printf(" frame too short for this IEEE 802.1X packet\n");
+ wpa_printf(MSG_INFO, " frame too short for this IEEE 802.1X packet");
if (sta->eapol_sm)
sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
return;
@@ -700,7 +834,7 @@
return;
}
- if (!hapd->conf->ieee802_1x &&
+ if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
!(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");
@@ -720,7 +854,7 @@
return;
#ifdef CONFIG_WPS
- if (!hapd->conf->ieee802_1x) {
+ if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
u32 wflags = sta->flags & (WLAN_STA_WPS |
WLAN_STA_WPS2 |
WLAN_STA_MAYBE_WPS);
@@ -838,7 +972,7 @@
}
#endif /* CONFIG_WPS */
- if (!force_1x && !hapd->conf->ieee802_1x) {
+ if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
"802.1X not enabled or forced for WPS");
/*
@@ -876,7 +1010,8 @@
#ifdef CONFIG_WPS
sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
- if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) {
+ if (!hapd->conf->ieee802_1x && hapd->conf->wps_state &&
+ !(sta->flags & WLAN_STA_WPS2)) {
/*
* Delay EAPOL frame transmission until a possible WPS STA
* initiates the handshake with EAPOL-Start. Only allow the
@@ -1202,6 +1337,147 @@
}
+#ifdef CONFIG_HS20
+
+static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
+{
+ sta->remediation = 1;
+ os_free(sta->remediation_url);
+ if (len > 2) {
+ sta->remediation_url = os_malloc(len);
+ if (!sta->remediation_url)
+ return;
+ sta->remediation_method = pos[0];
+ os_memcpy(sta->remediation_url, pos + 1, len - 1);
+ sta->remediation_url[len - 1] = '\0';
+ wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed "
+ "for " MACSTR " - server method %u URL %s",
+ MAC2STR(sta->addr), sta->remediation_method,
+ sta->remediation_url);
+ } else {
+ sta->remediation_url = NULL;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed "
+ "for " MACSTR, MAC2STR(sta->addr));
+ }
+ /* TODO: assign the STA into remediation VLAN or add filtering */
+}
+
+
+static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len)
+{
+ if (len < 3)
+ return; /* Malformed information */
+ sta->hs20_deauth_requested = 1;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Deauthentication request - Code %u "
+ "Re-auth Delay %u",
+ *pos, WPA_GET_LE16(pos + 1));
+ wpabuf_free(sta->hs20_deauth_req);
+ sta->hs20_deauth_req = wpabuf_alloc(len + 1);
+ if (sta->hs20_deauth_req) {
+ wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
+ wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
+ wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+ }
+ ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+}
+
+
+static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len, int session_timeout)
+{
+ unsigned int swt;
+ int warning_time, beacon_int;
+
+ if (len < 1)
+ return; /* Malformed information */
+ os_free(sta->hs20_session_info_url);
+ sta->hs20_session_info_url = os_malloc(len);
+ if (sta->hs20_session_info_url == NULL)
+ return;
+ swt = pos[0];
+ os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1);
+ sta->hs20_session_info_url[len - 1] = '\0';
+ wpa_printf(MSG_DEBUG, "HS 2.0: Session Information URL='%s' SWT=%u "
+ "(session_timeout=%d)",
+ sta->hs20_session_info_url, swt, session_timeout);
+ if (session_timeout < 0) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: No Session-Timeout set - ignore session info URL");
+ return;
+ }
+ if (swt == 255)
+ swt = 1; /* Use one minute as the AP selected value */
+
+ if ((unsigned int) session_timeout < swt * 60)
+ warning_time = 0;
+ else
+ warning_time = session_timeout - swt * 60;
+
+ beacon_int = hapd->iconf->beacon_int;
+ if (beacon_int < 1)
+ beacon_int = 100; /* best guess */
+ sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128;
+ if (sta->hs20_disassoc_timer > 65535)
+ sta->hs20_disassoc_timer = 65535;
+
+ ap_sta_session_warning_timeout(hapd, sta, warning_time);
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg,
+ int session_timeout)
+{
+#ifdef CONFIG_HS20
+ u8 *buf, *pos, *end, type, sublen;
+ size_t len;
+
+ buf = NULL;
+ sta->remediation = 0;
+ sta->hs20_deauth_requested = 0;
+
+ for (;;) {
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ &buf, &len, buf) < 0)
+ break;
+ if (len < 6)
+ continue;
+ pos = buf;
+ end = buf + len;
+ if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+ continue;
+ pos += 4;
+
+ type = *pos++;
+ sublen = *pos++;
+ if (sublen < 2)
+ continue; /* invalid length */
+ sublen -= 2; /* skip header */
+ if (pos + sublen > end)
+ continue; /* invalid WFA VSA */
+
+ switch (type) {
+ case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
+ ieee802_1x_hs20_sub_rem(sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
+ ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL:
+ ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
+ session_timeout);
+ break;
+ }
+ }
+#endif /* CONFIG_HS20 */
+}
+
+
struct sta_id_search {
u8 identifier;
struct eapol_state_machine *sm;
@@ -1276,15 +1552,14 @@
"EAP-Message");
} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
req, 1)) {
- printf("Incoming RADIUS packet did not have correct "
- "Message-Authenticator - dropped\n");
+ 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) {
- printf("Unknown RADIUS message code\n");
+ wpa_printf(MSG_INFO, "Unknown RADIUS message code");
return RADIUS_RX_UNKNOWN;
}
@@ -1361,7 +1636,11 @@
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 &&
+ ieee802_1x_check_hs20(hapd, sta, msg,
+ session_timeout_set ?
+ (int) session_timeout : -1);
+ if (sm->eap_if->eapKeyAvailable && !sta->remediation &&
+ !sta->hs20_deauth_requested &&
wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
session_timeout_set ?
(int) session_timeout : -1, sm) == 0) {
@@ -1448,7 +1727,7 @@
if (eapol->default_wep_key == NULL ||
random_get_bytes(eapol->default_wep_key,
hapd->conf->default_wep_key_len)) {
- printf("Could not generate random WEP key.\n");
+ wpa_printf(MSG_INFO, "Could not generate random WEP key");
os_free(eapol->default_wep_key);
eapol->default_wep_key = NULL;
return -1;
@@ -1564,14 +1843,14 @@
static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
- int preauth)
+ int preauth, int remediation)
{
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);
+ ieee802_1x_finished(hapd, sta, success, remediation);
}
@@ -1604,7 +1883,9 @@
user->password_hash = eap_user->password_hash;
}
user->force_version = eap_user->force_version;
+ user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth;
+ user->remediation = eap_user->remediation;
return 0;
}
@@ -1952,7 +2233,9 @@
{
int len = 0, ret;
struct eapol_state_machine *sm = sta->eapol_sm;
- struct os_time t;
+ struct os_reltime diff;
+ const char *name1;
+ const char *name2;
if (sm == NULL)
return 0;
@@ -2067,7 +2350,7 @@
len += ret;
/* dot1xAuthSessionStatsTable */
- os_get_time(&t);
+ os_reltime_age(&sta->acct_session_start, &diff);
ret = os_snprintf(buf + len, buflen - len,
/* TODO: dot1xAuthSessionOctetsRx */
/* TODO: dot1xAuthSessionOctetsTx */
@@ -2082,26 +2365,73 @@
(wpa_key_mgmt_wpa_ieee8021x(
wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
1 : 2,
- (unsigned int) (t.sec - sta->acct_session_start),
+ (unsigned int) diff.sec,
sm->identity);
if (ret < 0 || (size_t) ret >= buflen - len)
return len;
len += ret;
+ name1 = eap_server_get_name(0, sm->eap_type_authsrv);
+ name2 = eap_server_get_name(0, sm->eap_type_supp);
+ 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,
+ name1 ? name1 : "",
+ sm->eap_type_supp,
+ name2 ? name2 : "");
+ 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)
+ struct sta_info *sta, int success,
+ int remediation)
{
const u8 *key;
size_t len;
/* TODO: get PMKLifetime from WPA parameters */
static const int dot11RSNAConfigPMKLifetime = 43200;
+#ifdef CONFIG_HS20
+ if (remediation && !sta->remediation) {
+ sta->remediation = 1;
+ os_free(sta->remediation_url);
+ sta->remediation_url =
+ os_strdup(hapd->conf->subscr_remediation_url);
+ sta->remediation_method = 1; /* SOAP-XML SPP */
+ }
+
+ if (success) {
+ if (sta->remediation) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+ "to " MACSTR " to indicate Subscription "
+ "Remediation",
+ MAC2STR(sta->addr));
+ hs20_send_wnm_notification(hapd, sta->addr,
+ sta->remediation_method,
+ sta->remediation_url);
+ os_free(sta->remediation_url);
+ sta->remediation_url = NULL;
+ }
+
+ if (sta->hs20_deauth_req) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+ "to " MACSTR " to indicate imminent "
+ "deauthentication", MAC2STR(sta->addr));
+ hs20_send_wnm_notification_deauth_req(
+ hapd, sta->addr, sta->hs20_deauth_req);
+ }
+ }
+#endif /* CONFIG_HS20 */
+
key = ieee802_1x_get_key(sta->eapol_sm, &len);
- if (success && key && len >= PMK_LEN &&
+ if (success && key && len >= PMK_LEN && !sta->remediation &&
+ !sta->hs20_deauth_requested &&
wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime,
sta->eapol_sm) == 0) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
@@ -2127,12 +2457,8 @@
* driver reorder operations.
*/
os_sleep(0, 10000);
-#ifndef ANDROID_P2P
- /* We need not do this for driver. For AP-SME flags if we send this disassoc,
- * the p2p_client is gettig disassoc after it has completed the assoc
- */
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
-#endif
+ hostapd_wps_eap_completed(hapd);
}
}
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
index 795d313..9be640c 100644
--- a/src/ap/p2p_hostapd.c
+++ b/src/ap/p2p_hostapd.c
@@ -96,9 +96,8 @@
u8 bitmap;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
*eid++ = 4 + 3 + 1;
- WPA_PUT_BE24(eid, OUI_WFA);
- eid += 3;
- *eid++ = P2P_OUI_TYPE;
+ WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
+ eid += 4;
*eid++ = P2P_ATTR_MANAGEABILITY;
WPA_PUT_LE16(eid, 1);
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
index ba5c606..612babc 100644
--- a/src/ap/peerkey_auth.c
+++ b/src/ap/peerkey_auth.c
@@ -221,8 +221,8 @@
return;
/* Peer RSN IE */
- os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len);
- pos = buf + kde->rsn_ie_len;
+ os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
+ pos += kde->rsn_ie_len;
/* Peer MAC Address */
pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 40972e9..4720b59 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -91,9 +91,9 @@
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&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));
@@ -107,12 +107,12 @@
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
{
int sec;
- struct os_time now;
+ struct os_reltime now;
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
if (pmksa->pmksa == NULL)
return;
- os_get_time(&now);
+ os_get_reltime(&now);
sec = pmksa->pmksa->expiration - now.sec;
if (sec < 0)
sec = 0;
@@ -241,7 +241,7 @@
struct eapol_state_machine *eapol, int akmp)
{
struct rsn_pmksa_cache_entry *entry, *pos;
- struct os_time now;
+ struct os_reltime now;
if (pmk_len > PMK_LEN)
return NULL;
@@ -253,7 +253,7 @@
entry->pmk_len = pmk_len;
rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
wpa_key_mgmt_sha256(akmp));
- os_get_time(&now);
+ os_get_reltime(&now);
entry->expiration = now.sec;
if (session_timeout > 0)
entry->expiration += session_timeout;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 6704c09..60f0768 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -15,7 +15,6 @@
#include "common/sae.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
-#include "drivers/driver.h"
#include "p2p/p2p.h"
#include "hostapd.h"
#include "accounting.h"
@@ -31,11 +30,13 @@
#include "p2p_hostapd.h"
#include "ap_drv_ops.h"
#include "gas_serv.h"
+#include "wnm_ap.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_handle_session_warning_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
@@ -155,7 +156,8 @@
if (sta->flags & WLAN_STA_WDS)
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
- if (!(sta->flags & WLAN_STA_PREAUTH))
+ if (!hapd->iface->driver_ap_teardown &&
+ !(sta->flags & WLAN_STA_PREAUTH))
hostapd_drv_sta_remove(hapd, sta->addr);
ap_sta_hash_del(hapd, sta);
@@ -204,6 +206,10 @@
hapd->iface->num_sta_ht_20mhz--;
}
+#ifdef CONFIG_IEEE80211N
+ ht40_intolerant_remove(hapd->iface, sta);
+#endif /* CONFIG_IEEE80211N */
+
#ifdef CONFIG_P2P
if (sta->no_p2p_set) {
sta->no_p2p_set = 0;
@@ -225,6 +231,7 @@
__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_handle_session_warning_timer, hapd, sta);
eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
@@ -232,10 +239,10 @@
wpa_auth_sta_deinit(sta->wpa_sm);
rsn_preauth_free_station(hapd, sta);
#ifndef CONFIG_NO_RADIUS
- radius_client_flush_auth(hapd->radius, sta->addr);
+ 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
@@ -261,9 +268,13 @@
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);
+ os_free(sta->remediation_url);
+ wpabuf_free(sta->hs20_deauth_req);
+ os_free(sta->hs20_session_info_url);
#ifdef CONFIG_SAE
sae_clear_data(sta->sae);
@@ -465,7 +476,6 @@
{
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) {
@@ -476,6 +486,8 @@
return;
}
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
mlme_deauthenticate_indication(hapd, sta,
WLAN_REASON_PREV_AUTH_NOT_VALID);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -483,9 +495,19 @@
"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);
+ }
}
@@ -507,6 +529,32 @@
}
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
+{
+#ifdef CONFIG_WNM
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR,
+ MAC2STR(sta->addr));
+ if (sta->hs20_session_info_url == NULL)
+ return;
+
+ wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
+ sta->hs20_disassoc_timer);
+#endif /* CONFIG_WNM */
+}
+
+
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int warning_time)
+{
+ eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+ eloop_register_timeout(warning_time, 0, ap_handle_session_warning_timer,
+ hapd, sta);
+}
+
+
struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
@@ -531,13 +579,16 @@
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 */
- 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);
os_memcpy(sta->addr, addr, ETH_ALEN);
sta->next = hapd->sta_list;
hapd->sta_list = sta;
@@ -808,9 +859,9 @@
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
{
u32 tu;
- struct os_time now, passed;
- os_get_time(&now);
- os_time_sub(&now, &sta->sa_query_start, &passed);
+ 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,
@@ -847,7 +898,7 @@
return;
if (sta->sa_query_count == 0) {
/* Starting a new SA Query procedure */
- os_get_time(&sta->sa_query_start);
+ 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;
@@ -892,6 +943,7 @@
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))
@@ -913,12 +965,25 @@
os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
if (authorized) {
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s", buf);
+ 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", buf);
+ AP_STA_CONNECTED "%s%s",
+ buf, ip_addr);
sta->flags |= WLAN_STA_AUTHORIZED;
} else {
@@ -995,3 +1060,33 @@
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;
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index e5b5069..03db98f 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -12,9 +12,6 @@
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
#define WLAN_STA_ASSOC BIT(1)
-#define WLAN_STA_PS BIT(2)
-#define WLAN_STA_TIM BIT(3)
-#define WLAN_STA_PERM BIT(4)
#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)
@@ -29,6 +26,8 @@
#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_VHT_OPMODE_ENABLED BIT(20)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -55,11 +54,14 @@
unsigned int no_short_preamble_set:1;
unsigned int no_ht_gf_set:1;
unsigned int no_ht_set:1;
+ unsigned int ht40_intolerant_set:1;
unsigned int ht_20mhz_set:1;
unsigned int no_p2p_set:1;
+ unsigned int qos_map_enabled:1;
+ unsigned int remediation:1;
+ unsigned int hs20_deauth_requested:1;
u16 auth_alg;
- u8 previous_ap[6];
enum {
STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
@@ -72,12 +74,9 @@
/* 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;
- time_t acct_session_start;
+ struct os_reltime acct_session_start;
int acct_session_started;
int acct_terminate_cause; /* Acct-Terminate-Cause */
int acct_interim_interval; /* Acct-Interim-Interval */
@@ -104,6 +103,7 @@
struct ieee80211_ht_capabilities *ht_capabilities;
struct ieee80211_vht_capabilities *vht_capabilities;
+ u8 vht_opmode;
#ifdef CONFIG_IEEE80211W
int sa_query_count; /* number of pending SA Query requests;
@@ -112,7 +112,7 @@
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_time sa_query_start;
+ struct os_reltime sa_query_start;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_INTERWORKING
@@ -124,8 +124,13 @@
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 */
+ u8 remediation_method;
+ char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+ struct wpabuf *hs20_deauth_req;
+ char *hs20_session_info_url;
+ int hs20_disassoc_timer;
- struct os_time connected_time;
+ struct os_reltime connected_time;
#ifdef CONFIG_SAE
struct sae_data *sae;
@@ -161,10 +166,14 @@
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);
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int warning_time);
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);
@@ -192,4 +201,6 @@
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 */
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 4a2ea06..4725e2b 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -68,7 +68,7 @@
int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
{
- struct os_time now;
+ struct os_reltime now;
int ret = 0;
if (addr && local) {
@@ -89,8 +89,8 @@
}
}
- os_get_time(&now);
- if (now.sec > hapd->michael_mic_failure + 60) {
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
hapd->michael_mic_failures = 1;
} else {
hapd->michael_mic_failures++;
@@ -99,7 +99,7 @@
ret = 1;
}
}
- hapd->michael_mic_failure = now.sec;
+ hapd->michael_mic_failure = now;
return ret;
}
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 746af40..4e4a352 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -4,14 +4,8 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -480,123 +474,6 @@
#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];
@@ -604,7 +481,6 @@
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);
@@ -624,9 +500,7 @@
"brvlan%d", vlan->vlan_id);
}
- ret = br_addbr(br_name);
- if (hapd_get_dynamic_iface(NULL, br_name, ret == 0,
- hapd))
+ if (!br_addbr(br_name))
vlan->clean |= DVLAN_CLEAN_BR;
ifconfig_up(br_name);
@@ -644,24 +518,17 @@
"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))
+ if (!vlan_add(tagged_interface, vlan->vlan_id,
+ vlan_ifname))
vlan->clean |= DVLAN_CLEAN_VLAN;
- ret = br_addif(br_name, vlan_ifname);
- if (hapd_get_dynamic_iface(br_name,
- vlan_ifname,
- ret == 0, hapd))
+ if (!br_addif(br_name, vlan_ifname))
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))
+ if (!br_addif(br_name, ifname))
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
ifconfig_up(ifname);
@@ -700,8 +567,7 @@
"brvlan%d", vlan->vlan_id);
}
- if ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) &&
- hapd_put_dynamic_iface(br_name, vlan->ifname, hapd))
+ if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
br_delif(br_name, vlan->ifname);
if (tagged_interface) {
@@ -715,20 +581,15 @@
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))
+ if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
br_delif(br_name, vlan_ifname);
ifconfig_down(vlan_ifname);
- if ((vlan->clean & DVLAN_CLEAN_VLAN) &&
- hapd_put_dynamic_iface(NULL, vlan_ifname,
- hapd))
+ if (vlan->clean & DVLAN_CLEAN_VLAN)
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);
@@ -1021,6 +882,7 @@
#ifdef CONFIG_FULL_DYNAMIC_VLAN
full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+ hapd->full_dynamic_vlan = NULL;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
}
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
index 382d5de..781eaac 100644
--- a/src/ap/vlan_init.h
+++ b/src/ap/vlan_init.h
@@ -3,14 +3,8 @@
* Copyright 2003, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef VLAN_INIT_H
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index d21c82f..6d4177c 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -4,14 +4,8 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -157,7 +151,7 @@
len = ((u8 *) (t + 1)) - buf;
if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
- perror("wmm_send_action: send");
+ wpa_printf(MSG_INFO, "wmm_send_action: send failed");
}
diff --git a/src/ap/wmm.h b/src/ap/wmm.h
index 96b04e8..b70b863 100644
--- a/src/ap/wmm.h
+++ b/src/ap/wmm.h
@@ -3,14 +3,8 @@
* Copyright 2002-2003, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WME_H
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 54a6b85..cf25dbb 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -1,6 +1,6 @@
/*
* hostapd - WNM
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,6 +9,7 @@
#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"
@@ -72,7 +73,7 @@
wnmsleep_ie.len = wnmsleep_ie_len - 2;
wnmsleep_ie.action_type = action_type;
wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
- wnmsleep_ie.intval = intval;
+ wnmsleep_ie.intval = host_to_le16(intval);
/* TFS IE(s) */
wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
@@ -154,6 +155,7 @@
*/
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);
@@ -167,6 +169,7 @@
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);
@@ -233,7 +236,7 @@
ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
wnmsleep_ie->action_type,
- wnmsleep_ie->intval);
+ le_to_host16(wnmsleep_ie->intval));
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
/* clear the tfs after sending the resp frame */
@@ -243,29 +246,262 @@
}
-int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
- struct rx_action *action)
+static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ const u8 *addr,
+ u8 dialog_token,
+ const char *url)
{
- if (action->len < 1 || action->data == NULL)
+ 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;
- switch (action->data[0]) {
+ payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+ action = *payload++;
+ plen = len - IEEE80211_HDRLEN - 2;
+
+ switch (action) {
case WNM_BSS_TRANS_MGMT_QUERY:
- wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query");
- /* TODO */
- return -1;
+ ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
case WNM_BSS_TRANS_MGMT_RESP:
- wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
- "Response");
- /* TODO */
- return -1;
+ 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, action->sa, action->data + 1,
- action->len - 1);
+ ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
return 0;
}
wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
- action->data[0], MAC2STR(action->sa));
+ 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;
+}
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index f05726e..eeaf5ec 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -1,6 +1,6 @@
/*
* IEEE 802.11v WNM related functions and structures
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,9 +9,14 @@
#ifndef WNM_AP_H
#define WNM_AP_H
-struct rx_action;
+struct sta_info;
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
- struct rx_action *action);
+ 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 */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 0286c5b..2bb8aab 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -11,6 +11,7 @@
#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"
@@ -210,6 +211,8 @@
if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
ret = 1;
#endif /* CONFIG_IEEE80211W */
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+ ret = 1;
return ret;
}
@@ -424,6 +427,17 @@
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;
}
@@ -437,6 +451,8 @@
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;
}
@@ -464,6 +480,11 @@
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;
@@ -516,6 +537,9 @@
{
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;
@@ -541,6 +565,8 @@
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"FT authentication already completed - do not "
"start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
return 0;
}
#endif /* CONFIG_IEEE80211R */
@@ -578,12 +604,26 @@
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);
+ wpabuf_free(sm->ft_pending_req_ies);
#endif /* CONFIG_IEEE80211R */
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
@@ -843,6 +883,7 @@
if (sm->pairwise == WPA_CIPHER_CCMP ||
sm->pairwise == WPA_CIPHER_GCMP) {
if (wpa_use_aes_cmac(sm) &&
+ sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
@@ -966,6 +1007,9 @@
if (kde.rsn_ie) {
eapol_key_ie = kde.rsn_ie;
eapol_key_ie_len = kde.rsn_ie_len;
+ } else if (kde.osen) {
+ eapol_key_ie = kde.osen;
+ eapol_key_ie_len = kde.osen_len;
} else {
eapol_key_ie = kde.wpa_ie;
eapol_key_ie_len = kde.wpa_ie_len;
@@ -995,6 +1039,26 @@
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 ||
@@ -1231,6 +1295,8 @@
if (force_version)
version = force_version;
+ else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+ version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_use_aes_cmac(sm))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise != WPA_CIPHER_TKIP)
@@ -1253,6 +1319,7 @@
key_data_len = kde_len;
if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
pad_len = key_data_len % 8;
if (pad_len)
@@ -1321,6 +1388,7 @@
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf,
(u8 *) (key + 1))) {
@@ -1355,7 +1423,7 @@
key->key_mic);
#ifdef CONFIG_TESTING_OPTIONS
if (!pairwise &&
- wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0d &&
+ wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
drand48() <
wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -1422,7 +1490,7 @@
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)
+ os_memcmp_const(mic, key->key_mic, 16) != 0)
ret = -1;
os_memcpy(key->key_mic, mic, 16);
return ret;
@@ -1719,7 +1787,8 @@
* one possible PSK for this STA.
*/
if (sm->wpa == WPA_VERSION_WPA2 &&
- wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
+ wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+ sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
pmkid = buf;
pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
@@ -1747,7 +1816,7 @@
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;
+ size_t ptk_len = wpa_cipher_key_len(sm->pairwise) + 32;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
@@ -1808,8 +1877,8 @@
* 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) {
+ if (os_memcmp_const(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");
@@ -1852,7 +1921,9 @@
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);
+ size_t len;
+ len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len;
}
return 0;
@@ -1863,6 +1934,8 @@
{
struct wpa_igtk_kde igtk;
struct wpa_group *gsm = sm->group;
+ u8 rsc[WPA_KEY_RSC_LEN];
+ size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
if (!sm->mgmt_frame_prot)
return pos;
@@ -1870,19 +1943,22 @@
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, igtk.pn) < 0)
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0)
os_memset(igtk.pn, 0, sizeof(igtk.pn));
- os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+ else
+ os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+ os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
if (sm->wpa_auth->conf.disable_gtk) {
/*
* 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)
+ if (random_get_bytes(igtk.igtk, len) < 0)
return pos;
}
pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
- (const u8 *) &igtk, sizeof(igtk), NULL, 0);
+ (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+ NULL, 0);
return pos;
}
@@ -1987,6 +2063,10 @@
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;
@@ -2048,6 +2128,16 @@
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 |
@@ -2374,15 +2464,16 @@
#ifdef CONFIG_IEEE80211W
if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ size_t len;
+ len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
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)
+ group->IGTK[group->GN_igtk - 4], len) < 0)
ret = -1;
wpa_hexdump_key(MSG_DEBUG, "IGTK",
- group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
+ group->IGTK[group->GN_igtk - 4], len);
}
#endif /* CONFIG_IEEE80211W */
@@ -2449,7 +2540,7 @@
/* update GTK when exiting WNM-Sleep Mode */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
{
- if (sm->is_wnmsleep)
+ if (sm == NULL || sm->is_wnmsleep)
return;
wpa_group_update_sta(sm, NULL);
@@ -2458,7 +2549,8 @@
void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
{
- sm->is_wnmsleep = !!flag;
+ if (sm)
+ sm->is_wnmsleep = !!flag;
}
@@ -2498,26 +2590,27 @@
{
struct wpa_group *gsm = sm->group;
u8 *start = pos;
+ size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/*
* 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;
+ *pos++ = 2 + 6 + 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;
+ os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+ pos += 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);
+ gsm->IGTK[gsm->GN_igtk - 4], len);
return pos - start;
}
@@ -2572,18 +2665,48 @@
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;
+ if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ enum wpa_alg alg;
+ size_t len;
+
+ alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher);
+ len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+ if (ret == 0 &&
+ wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ broadcast_ether_addr, group->GN_igtk,
+ group->IGTK[group->GN_igtk - 4], 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)
{
@@ -2592,8 +2715,10 @@
group->changed = TRUE;
group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
- if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+ if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+ wpa_group_fatal_failure(wpa_auth, group);
return -1;
+ }
return 0;
}
@@ -2604,6 +2729,8 @@
{
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);
@@ -3012,6 +3139,9 @@
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);
@@ -3056,3 +3186,22 @@
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 */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 47503d0..929a253 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -42,6 +42,7 @@
#define FT_R0KH_R1KH_PULL_DATA_LEN 44
#define FT_R0KH_R1KH_RESP_DATA_LEN 76
#define FT_R0KH_R1KH_PUSH_DATA_LEN 88
+#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
struct ft_r0kh_r1kh_pull_frame {
u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
@@ -49,7 +50,7 @@
le16 data_length; /* little endian length of data (44) */
u8 ap_address[ETH_ALEN];
- u8 nonce[16];
+ u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
u8 r1kh_id[FT_R1KH_ID_LEN];
u8 s1kh_id[ETH_ALEN];
@@ -63,7 +64,7 @@
le16 data_length; /* little endian length of data (76) */
u8 ap_address[ETH_ALEN];
- u8 nonce[16]; /* copied from pull */
+ u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
u8 s1kh_id[ETH_ALEN]; /* copied from pull */
u8 pmk_r1[PMK_LEN];
@@ -142,6 +143,7 @@
int tx_status;
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
+ int group_mgmt_cipher;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R
#define SSID_LEN 32
@@ -163,6 +165,12 @@
#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 {
@@ -203,7 +211,7 @@
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);
+ size_t tspec_ielen);
#endif /* CONFIG_IEEE80211R */
};
@@ -226,6 +234,9 @@
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *mdie, size_t mdie_len);
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_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,
@@ -295,5 +306,8 @@
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 */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 29d9d29..8a6ca71 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,6 +9,7 @@
#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 "crypto/aes_wrap.h"
@@ -22,6 +23,12 @@
#ifdef CONFIG_IEEE80211R
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+ const u8 *current_ap, const u8 *sta_addr,
+ u16 status, const u8 *resp_ies,
+ size_t resp_ies_len);
+
+
static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
const u8 *data, size_t data_len)
{
@@ -57,7 +64,7 @@
u8 *tspec_ie, size_t tspec_ielen)
{
if (wpa_auth->cb.add_tspec == NULL) {
- wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+ 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,
@@ -228,8 +235,8 @@
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_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
+ WPA_PMK_NAME_LEN) == 0) {
os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
if (pairwise)
*pairwise = r0->pairwise;
@@ -278,8 +285,8 @@
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_memcmp_const(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;
@@ -293,22 +300,26 @@
}
-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)
+static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *pmk_r0_name)
{
struct ft_remote_r0kh *r0kh;
struct ft_r0kh_r1kh_pull_frame frame, f;
- r0kh = wpa_auth->conf.r0kh_list;
+ r0kh = sm->wpa_auth->conf.r0kh_list;
while (r0kh) {
- if (r0kh->id_len == r0kh_id_len &&
- os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0)
+ if (r0kh->id_len == sm->r0kh_id_len &&
+ os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
+ 0)
break;
r0kh = r0kh->next;
}
- if (r0kh == NULL)
+ if (r0kh == NULL) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
return -1;
+ }
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
"address " MACSTR, MAC2STR(r0kh->addr));
@@ -317,25 +328,32 @@
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);
+ os_memcpy(frame.ap_address, sm->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))) {
+ if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce");
return -1;
}
+ os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
+ FT_R0KH_R1KH_PULL_NONCE_LEN);
os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
- os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
- os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+ os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+ os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
os_memset(f.pad, 0, sizeof(f.pad));
if (aes_wrap(r0kh->key, (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));
+ wpabuf_free(sm->ft_pending_req_ies);
+ sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+ if (sm->ft_pending_req_ies == NULL)
+ return -1;
+
+ wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
return 0;
}
@@ -570,8 +588,8 @@
else {
/* TSPEC accepted; include updated TSPEC in
* response */
- rdie->descr_count = 1;
- pos += sizeof(*tspec);
+ rdie->descr_count = 1;
+ pos += sizeof(*tspec);
}
return pos;
}
@@ -633,8 +651,7 @@
conf = &sm->wpa_auth->conf;
- if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK)
+ if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return pos;
end = pos + max_len;
@@ -778,7 +795,7 @@
}
-static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
u8 **resp_ies, size_t *resp_ies_len)
{
@@ -849,19 +866,13 @@
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) {
+ if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
wpa_printf(MSG_DEBUG, "FT: Did not have matching "
"PMK-R1 and unknown R0KH-ID");
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;
+ return -1; /* Status pending */
}
wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
@@ -888,6 +899,7 @@
wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
sm->pairwise = pairwise;
+ sm->PTK_valid = TRUE;
wpa_ft_install_ptk(sm);
buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
@@ -941,6 +953,7 @@
u16 status;
u8 *resp_ies;
size_t resp_ies_len;
+ int res;
if (sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
@@ -951,8 +964,16 @@
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);
+ sm->ft_pending_cb = cb;
+ sm->ft_pending_cb_ctx = ctx;
+ sm->ft_pending_auth_transaction = auth_transaction;
+ res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+ &resp_ies_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
+ return;
+ }
+ status = res;
wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
" auth_transaction=%d status=%d",
@@ -993,8 +1014,8 @@
return WLAN_STATUS_INVALID_PMKID;
}
- if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
- {
+ if (os_memcmp_const(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;
@@ -1040,7 +1061,8 @@
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
- os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+ os_memcmp_const(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",
@@ -1055,8 +1077,8 @@
return -1;
}
- if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
- FT_R1KH_ID_LEN) != 0) {
+ if (os_memcmp_const(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",
@@ -1067,7 +1089,8 @@
}
if (parse.rsn_pmkid == NULL ||
- os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+ os_memcmp_const(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;
@@ -1093,7 +1116,7 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ if (os_memcmp_const(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));
@@ -1183,15 +1206,27 @@
}
+static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 resp,
+ const u8 *ies, size_t ies_len)
+{
+ struct wpa_state_machine *sm = ctx;
+ wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
+ MAC2STR(sm->addr));
+ wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
+ WLAN_STATUS_SUCCESS, ies, ies_len);
+}
+
+
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;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+ int res;
sm = wpa_ft_add_sta(wpa_auth, sta_addr);
if (sm == NULL) {
@@ -1202,8 +1237,33 @@
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);
+ sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
+ sm->ft_pending_cb_ctx = sm;
+ os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+ res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+ &resp_ies_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
+ return 0;
+ }
+ status = res;
+
+ res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
+ resp_ies, resp_ies_len);
+ os_free(resp_ies);
+ return res;
+}
+
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+ const u8 *current_ap, const u8 *sta_addr,
+ u16 status, const u8 *resp_ies,
+ size_t resp_ies_len)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ size_t rlen;
+ struct ft_rrb_frame *frame;
+ u8 *pos;
wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
" CurrentAP=" MACSTR " status=%d",
@@ -1219,10 +1279,8 @@
rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
frame = os_malloc(sizeof(*frame) + rlen);
- if (frame == NULL) {
- os_free(resp_ies);
+ if (frame == NULL)
return -1;
- }
frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
frame->packet_type = FT_PACKET_RESPONSE;
frame->action_length = host_to_le16(rlen);
@@ -1236,10 +1294,8 @@
pos += ETH_ALEN;
WPA_PUT_LE16(pos, status);
pos += 2;
- if (resp_ies) {
+ 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);
@@ -1291,7 +1347,7 @@
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="
+ 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));
@@ -1334,13 +1390,58 @@
}
+static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_state_machine *sm = eloop_ctx;
+ int res;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+ u16 status;
+
+ res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
+ wpabuf_len(sm->ft_pending_req_ies),
+ &resp_ies, &resp_ies_len);
+ wpabuf_free(sm->ft_pending_req_ies);
+ sm->ft_pending_req_ies = NULL;
+ if (res < 0)
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ status = res;
+ wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
+ " - status %u", MAC2STR(sm->addr), status);
+
+ sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+ sm->ft_pending_auth_transaction + 1, status,
+ resp_ies, resp_ies_len);
+ os_free(resp_ies);
+}
+
+
+static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
+{
+ struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+
+ if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
+ return 0;
+ if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
+ FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
+ return 0;
+ if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
+ MACSTR " - process from timeout", MAC2STR(sm->addr));
+ eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+ return 1;
+}
+
+
static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *data, size_t data_len)
{
struct ft_r0kh_r1kh_resp_frame *frame, f;
struct ft_remote_r0kh *r0kh;
- int pairwise;
+ int pairwise, res;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
@@ -1370,21 +1471,17 @@
return -1;
}
- if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
- != 0) {
+ if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
"matching R1KH-ID");
return -1;
}
- /* TODO: verify that <nonce,s1kh_id> 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="
+ 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",
@@ -1392,11 +1489,13 @@
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);
+ res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+ pairwise);
+ wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
+ wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
os_memset(f.pmk_r1, 0, PMK_LEN);
- return 0;
+ return res ? 0 : -1;
}
@@ -1448,8 +1547,8 @@
return -1;
}
- if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
- != 0) {
+ if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
"R1KH-ID (received " MACSTR " own " MACSTR ")",
MAC2STR(f.r1kh_id),
@@ -1590,6 +1689,11 @@
return -1;
}
+ if (end > pos) {
+ wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end",
+ pos, end - pos);
+ }
+
return 0;
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index d977b42..6ee9a4f 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -15,7 +15,6 @@
#include "eapol_auth/eapol_auth_sm_i.h"
#include "eap_server/eap.h"
#include "l2_packet/l2_packet.h"
-#include "drivers/driver.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "preauth_auth.h"
@@ -50,6 +49,7 @@
wconf->okc = conf->okc;
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
+ wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R
wconf->ssid_len = conf->ssid.ssid_len;
@@ -74,11 +74,30 @@
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_HS20
wconf->disable_gtk = conf->disable_dgaf;
+ if (conf->osen) {
+ wconf->disable_gtk = 1;
+ wconf->wpa = WPA_PROTO_OSEN;
+ wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+ wconf->wpa_pairwise = 0;
+ wconf->wpa_group = WPA_CIPHER_CCMP;
+ wconf->rsn_pairwise = WPA_CIPHER_CCMP;
+ wconf->rsn_preauth = 0;
+ wconf->disable_pmksa_caching = 1;
+#ifdef CONFIG_IEEE80211W
+ wconf->ieee80211w = 1;
+#endif /* CONFIG_IEEE80211W */
+ }
#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 */
}
@@ -618,5 +637,6 @@
#ifdef CONFIG_IEEE80211R
l2_packet_deinit(hapd->l2);
+ hapd->l2 = NULL;
#endif /* CONFIG_IEEE80211R */
}
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 12e59bc..6960ff3 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -118,9 +118,22 @@
u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
* message 2/4 */
u8 *assoc_resp_ftie;
+
+ void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len);
+ void *ft_pending_cb_ctx;
+ struct wpabuf *ft_pending_req_ies;
+ u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+ u8 ft_pending_auth_transaction;
+ u8 ft_pending_current_ap[ETH_ALEN];
#endif /* CONFIG_IEEE80211R */
int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+ u8 ip_addr[4];
+#endif /* CONFIG_P2P */
};
@@ -139,7 +152,8 @@
enum {
WPA_GROUP_GTK_INIT = 0,
- WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE
+ WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
+ WPA_GROUP_FATAL_FAILURE
} wpa_group_state;
u8 GMK[WPA_GMK_LEN];
@@ -149,7 +163,7 @@
Boolean first_sta_seen;
Boolean reject_4way_hs_for_entropy;
#ifdef CONFIG_IEEE80211W
- u8 IGTK[2][WPA_IGTK_LEN];
+ u8 IGTK[2][WPA_IGTK_MAX_LEN];
int GN_igtk, GM_igtk;
#endif /* CONFIG_IEEE80211W */
};
@@ -184,6 +198,10 @@
struct rsn_pmksa_cache *pmksa;
struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+ struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
};
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index cdfcca1..1e4defc 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -261,7 +261,25 @@
}
/* Management Group Cipher Suite */
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ switch (conf->group_mgmt_cipher) {
+ case WPA_CIPHER_AES_128_CMAC:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ break;
+ case WPA_CIPHER_BIP_GMAC_128:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
+ break;
+ case WPA_CIPHER_BIP_GMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
+ break;
+ case WPA_CIPHER_BIP_CMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Invalid group management cipher (0x%x)",
+ conf->group_mgmt_cipher);
+ return -1;
+ }
pos += RSN_SELECTOR_LEN;
}
#endif /* CONFIG_IEEE80211W */
@@ -295,6 +313,55 @@
}
+static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
+{
+ u8 *len;
+ u16 capab;
+
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = eid++; /* to be filled */
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ eid += RSN_SELECTOR_LEN;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+ eid += RSN_SELECTOR_LEN;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+ eid += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+ 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 */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+ *len = eid - len - 1;
+
+ return eid;
+}
+
+
int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
{
u8 *pos, buf[128];
@@ -302,6 +369,9 @@
pos = buf;
+ if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
+ pos = wpa_write_osen(&wpa_auth->conf, pos);
+ }
if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
res = wpa_write_rsn_ie(&wpa_auth->conf,
pos, buf + sizeof(buf) - pos, NULL);
@@ -534,7 +604,8 @@
return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
}
- if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+ if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
+ {
wpa_printf(MSG_DEBUG, "Unsupported management group "
"cipher %d", data.mgmt_group_cipher);
return WPA_INVALID_MGMT_GROUP_CIPHER;
@@ -604,7 +675,7 @@
break;
}
}
- if (sm->pmksa) {
+ if (sm->pmksa && pmkid) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PMKID found from PMKSA cache "
"eap_type=%d vlan_id=%d",
@@ -626,6 +697,36 @@
}
+#ifdef CONFIG_HS20
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_len)
+{
+ if (wpa_auth == NULL || sm == NULL)
+ return -1;
+
+ /* TODO: parse OSEN element */
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+ sm->mgmt_frame_prot = 1;
+ sm->pairwise = WPA_CIPHER_CCMP;
+ sm->wpa = WPA_VERSION_WPA2;
+
+ if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
+ os_free(sm->wpa_ie);
+ sm->wpa_ie = os_malloc(osen_ie_len);
+ if (sm->wpa_ie == NULL)
+ return -1;
+ }
+
+ os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
+ sm->wpa_ie_len = osen_ie_len;
+
+ return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
/**
* wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
* @pos: Pointer to the IE header
@@ -648,6 +749,12 @@
return 0;
}
+ if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) {
+ ie->osen = pos;
+ ie->osen_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) {
@@ -708,6 +815,25 @@
}
#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;
}
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 4999139..d2067ba 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -39,6 +39,13 @@
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 */
+
+ const u8 *osen;
+ size_t osen_len;
};
int wpa_parse_kde_ies(const u8 *buf, size_t len,
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index eb300f1..6f16f50 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -40,6 +40,7 @@
const u8 *ie, size_t ie_len,
int ssi_signal);
static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+static void hostapd_wps_nfc_clear(struct wps_context *wps);
struct wps_for_each_data {
@@ -287,6 +288,20 @@
}
+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)
{
@@ -363,40 +378,11 @@
}
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;
- }
+ /*
+ * WPS 2.0 does not allow WEP to be configured, so no need to
+ * process that option here either.
+ */
+ bss->auth_algs = 1;
}
/* Schedule configuration reload after short period of time to allow
@@ -457,6 +443,8 @@
hapd->wps->ssid_len = cred->ssid_len;
hapd->wps->encr_types = cred->encr_type;
hapd->wps->auth_types = cred->auth_type;
+ hapd->wps->ap_encr_type = cred->encr_type;
+ hapd->wps->ap_auth_type = cred->auth_type;
if (cred->key_len == 0) {
os_free(hapd->wps->network_key);
hapd->wps->network_key = NULL;
@@ -569,31 +557,11 @@
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");
- }
+ /*
+ * WPS 2.0 does not allow WEP to be configured, so no need to
+ * process that option here either.
+ */
+ fprintf(nconf, "auth_algs=1\n");
}
fprintf(nconf, "# WPS configuration - END\n");
@@ -881,7 +849,7 @@
}
-static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
{
wpabuf_free(hapd->wps_beacon_ie);
hapd->wps_beacon_ie = NULL;
@@ -889,6 +857,9 @@
wpabuf_free(hapd->wps_probe_resp_ie);
hapd->wps_probe_resp_ie = NULL;
+ if (deinit_only)
+ return;
+
hostapd_set_ap_wps_ie(hapd);
}
@@ -971,6 +942,21 @@
}
+static void hostapd_free_wps(struct wps_context *wps)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ wps_device_data_free(&wps->dev);
+ os_free(wps->network_key);
+ hostapd_wps_nfc_clear(wps);
+ wpabuf_free(wps->dh_pubkey);
+ wpabuf_free(wps->dh_privkey);
+ os_free(wps);
+}
+
+
int hostapd_init_wps(struct hostapd_data *hapd,
struct hostapd_bss_config *conf)
{
@@ -978,7 +964,7 @@
struct wps_registrar_config cfg;
if (conf->wps_state == 0) {
- hostapd_wps_clear_ies(hapd);
+ hostapd_wps_clear_ies(hapd, 0);
return 0;
}
@@ -1027,7 +1013,6 @@
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) {
@@ -1042,14 +1027,11 @@
"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;
- }
+ if (hostapd_wps_set_vendor_ext(hapd, wps) < 0)
+ goto fail;
wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
@@ -1088,18 +1070,6 @@
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) {
@@ -1109,19 +1079,15 @@
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;
- }
+ if (wps->network_key == NULL)
+ goto fail;
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;
- }
+ if (wps->network_key == NULL)
+ goto fail;
os_memcpy(wps->network_key, conf->ssid.wep.key[0],
conf->ssid.wep.len[0]);
wps->network_key_len = conf->ssid.wep.len[0];
@@ -1132,6 +1098,8 @@
wps->psk_set = 1;
}
+ wps->ap_auth_type = wps->auth_types;
+ wps->ap_encr_type = wps->encr_types;
if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
/* Override parameters to enable security by default */
wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
@@ -1165,9 +1133,7 @@
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;
+ goto fail;
}
#ifdef CONFIG_WPS_UPNP
@@ -1183,6 +1149,10 @@
hapd->wps = wps;
return 0;
+
+fail:
+ hostapd_free_wps(wps);
+ return -1;
}
@@ -1197,8 +1167,7 @@
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);
+ hostapd_free_wps(wps);
hapd->wps = NULL;
return -1;
}
@@ -1211,6 +1180,7 @@
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;
@@ -1226,21 +1196,19 @@
{
eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
- if (hapd->wps == NULL)
+ eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL);
+ if (hapd->wps == NULL) {
+ hostapd_wps_clear_ies(hapd, 1);
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);
+ hostapd_free_wps(hapd->wps);
hapd->wps = NULL;
- hostapd_wps_clear_ies(hapd);
+ hostapd_wps_clear_ies(hapd, 1);
}
@@ -1322,7 +1290,7 @@
{
const u8 *p2p_dev_addr = ctx;
if (hapd->wps == NULL)
- return 0;
+ return -1;
return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
}
@@ -1338,7 +1306,7 @@
static int wps_cancel(struct hostapd_data *hapd, void *ctx)
{
if (hapd->wps == NULL)
- return 0;
+ return -1;
wps_registrar_wps_cancel(hapd->wps->registrar);
ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
@@ -1459,6 +1427,16 @@
return 0;
}
+ if (!sta->eapol_sm) {
+ /*
+ * This can happen, e.g., if an ER sends an extra message after
+ * the station has disassociated (but not fully
+ * deauthenticated).
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized");
+ return 0;
+ }
+
p = os_zalloc(sizeof(*p));
if (p == NULL)
return -1;
@@ -1652,8 +1630,6 @@
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)
@@ -1768,7 +1744,8 @@
if (hapd->wps == NULL)
return NULL;
- ret = wps_get_oob_cred(hapd->wps);
+ 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);
@@ -1784,11 +1761,136 @@
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;
+
/*
- * Handover Select carrier record for WPS uses the same format as
- * configuration token.
+ * Enrollee/station is always initiator of the NFC connection handover,
+ * so use the request message here to find Enrollee public key hash.
*/
- return hostapd_wps_nfc_config_token(hapd, ndef);
+ 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;
}
@@ -1823,6 +1925,9 @@
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);
@@ -1849,6 +1954,8 @@
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);
}
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index a2c2cf0..204bd82 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -16,6 +16,7 @@
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,
@@ -36,6 +37,9 @@
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);
@@ -61,6 +65,10 @@
{
}
+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)
diff --git a/src/common/Makefile b/src/common/Makefile
index 9c41962..adfd3df 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
new file mode 100644
index 0000000..56b1122
--- /dev/null
+++ b/src/common/common_module_tests.c
@@ -0,0 +1,172 @@
+/*
+ * common module tests
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "ieee802_11_common.h"
+#include "wpa_common.h"
+
+
+struct ieee802_11_parse_test_data {
+ u8 *data;
+ size_t len;
+ ParseRes result;
+ int count;
+};
+
+static const struct ieee802_11_parse_test_data parse_tests[] = {
+ { (u8 *) "", 0, ParseOK, 0 },
+ { (u8 *) " ", 1, ParseFailed, 0 },
+ { (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
+ { (u8 *) "\xff\x01", 2, ParseFailed, 0 },
+ { (u8 *) "\xdd\x03\x01\x02\x03", 5, ParseUnknown, 1 },
+ { (u8 *) "\xdd\x04\x01\x02\x03\x04", 6, ParseUnknown, 1 },
+ { (u8 *) "\xdd\x04\x00\x50\xf2\x02", 6, ParseUnknown, 1 },
+ { (u8 *) "\xdd\x05\x00\x50\xf2\x02\x02", 7, ParseOK, 1 },
+ { (u8 *) "\xdd\x05\x00\x50\xf2\x02\xff", 7, ParseUnknown, 1 },
+ { (u8 *) "\xdd\x04\x00\x50\xf2\xff", 6, ParseUnknown, 1 },
+ { (u8 *) "\xdd\x04\x50\x6f\x9a\xff", 6, ParseUnknown, 1 },
+ { (u8 *) "\xdd\x04\x00\x90\x4c\x33", 6, ParseOK, 1 },
+ { (u8 *) "\xdd\x04\x00\x90\x4c\xff\xdd\x04\x00\x90\x4c\x33", 12,
+ ParseUnknown, 2 },
+ { (u8 *) "\x10\x01\x00\x21\x00", 5, ParseOK, 2 },
+ { (u8 *) "\x24\x00", 2, ParseOK, 1 },
+ { (u8 *) "\x38\x00", 2, ParseOK, 1 },
+ { (u8 *) "\x54\x00", 2, ParseOK, 1 },
+ { (u8 *) "\x5a\x00", 2, ParseOK, 1 },
+ { (u8 *) "\x65\x00", 2, ParseOK, 1 },
+ { (u8 *) "\x65\x12\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11",
+ 20, ParseOK, 1 },
+ { (u8 *) "\x6e\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xc7\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 },
+ { NULL, 0, ParseOK, 0 }
+};
+
+static int ieee802_11_parse_tests(void)
+{
+ int i, ret = 0;
+
+ wpa_printf(MSG_INFO, "ieee802_11_parse tests");
+
+ for (i = 0; parse_tests[i].data; i++) {
+ const struct ieee802_11_parse_test_data *test;
+ struct ieee802_11_elems elems;
+ ParseRes res;
+
+ test = &parse_tests[i];
+ res = ieee802_11_parse_elems(test->data, test->len, &elems, 1);
+ if (res != test->result ||
+ ieee802_11_ie_count(test->data, test->len) != test->count) {
+ wpa_printf(MSG_ERROR, "ieee802_11_parse test %d failed",
+ i);
+ ret = -1;
+ }
+ }
+
+ if (ieee802_11_vendor_ie_concat((const u8 *) "\x00\x01", 2, 0) != NULL)
+ {
+ wpa_printf(MSG_ERROR,
+ "ieee802_11_vendor_ie_concat test failed");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+struct rsn_ie_parse_test_data {
+ u8 *data;
+ size_t len;
+ int result;
+};
+
+static const struct rsn_ie_parse_test_data rsn_parse_tests[] = {
+ { (u8 *) "", 0, -1 },
+ { (u8 *) "\x30\x00", 2, -1 },
+ { (u8 *) "\x30\x02\x01\x00", 4, 0 },
+ { (u8 *) "\x30\x02\x00\x00", 4, -2 },
+ { (u8 *) "\x30\x02\x02\x00", 4, -2 },
+ { (u8 *) "\x30\x02\x00\x01", 4, -2 },
+ { (u8 *) "\x30\x02\x00\x00\x00", 5, -2 },
+ { (u8 *) "\x30\x03\x01\x00\x00", 5, -3 },
+ { (u8 *) "\x30\x06\x01\x00\x00\x00\x00\x00", 8, -1 },
+ { (u8 *) "\x30\x06\x01\x00\x00\x0f\xac\x04", 8, 0 },
+ { (u8 *) "\x30\x07\x01\x00\x00\x0f\xac\x04\x00", 9, -5 },
+ { (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x00", 10, -4 },
+ { (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x01", 10, -4 },
+ { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04",
+ 14, 0 },
+ { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x00\x01\x00\x0f\xac\x04",
+ 14, -4 },
+ { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x06",
+ 14, -1 },
+ { (u8 *) "\x30\x10\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x04\x00\x0f\xac\x08",
+ 18, 0 },
+ { (u8 *) "\x30\x0d\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00",
+ 15, -7 },
+ { (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x00",
+ 16, -6 },
+ { (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x01",
+ 16, -6 },
+ { (u8 *) "\x30\x12\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01",
+ 20, 0 },
+ { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x01\x00\x0f\xac\x02",
+ 24, 0 },
+ { (u8 *) "\x30\x13\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00",
+ 21, 0 },
+ { (u8 *) "\x30\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00",
+ 22, 0 },
+ { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00",
+ 24, 0 },
+ { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x01",
+ 24, -9 },
+ { (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x00\x00\x00",
+ 28, -10 },
+ { (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06",
+ 28, 0 },
+ { (u8 *) "\x30\x1c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06\x01\x02",
+ 30, 0 },
+ { NULL, 0, 0 }
+};
+
+static int rsn_ie_parse_tests(void)
+{
+ int i, ret = 0;
+
+ wpa_printf(MSG_INFO, "rsn_ie_parse tests");
+
+ for (i = 0; rsn_parse_tests[i].data; i++) {
+ const struct rsn_ie_parse_test_data *test;
+ struct wpa_ie_data data;
+
+ test = &rsn_parse_tests[i];
+ if (wpa_parse_wpa_ie_rsn(test->data, test->len, &data) !=
+ test->result) {
+ wpa_printf(MSG_ERROR, "rsn_ie_parse test %d failed", i);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+
+int common_module_tests(void)
+{
+ int ret = 0;
+
+ wpa_printf(MSG_INFO, "common module tests");
+
+ if (ieee802_11_parse_tests() < 0 ||
+ rsn_ie_parse_tests() < 0)
+ ret = -1;
+
+ return ret;
+}
diff --git a/src/common/defs.h b/src/common/defs.h
index 281dd8a..d4091e3 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -23,11 +23,15 @@
#define WPA_CIPHER_WEP104 BIT(2)
#define WPA_CIPHER_TKIP BIT(3)
#define WPA_CIPHER_CCMP BIT(4)
-#ifdef CONFIG_IEEE80211W
#define WPA_CIPHER_AES_128_CMAC BIT(5)
-#endif /* CONFIG_IEEE80211W */
#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)
@@ -44,12 +48,14 @@
#define WPA_KEY_MGMT_WAPI_PSK BIT(12)
#define WPA_KEY_MGMT_WAPI_CERT BIT(13)
#define WPA_KEY_MGMT_CCKM BIT(14)
+#define WPA_KEY_MGMT_OSEN BIT(15)
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_OSEN |
WPA_KEY_MGMT_IEEE8021X_SHA256));
}
@@ -58,7 +64,8 @@
return !!(akm & (WPA_KEY_MGMT_PSK |
WPA_KEY_MGMT_FT_PSK |
WPA_KEY_MGMT_PSK_SHA256 |
- WPA_KEY_MGMT_SAE));
+ WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_FT_SAE));
}
static inline int wpa_key_mgmt_ft(int akm)
@@ -77,13 +84,15 @@
static inline int wpa_key_mgmt_sha256(int akm)
{
return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
- WPA_KEY_MGMT_IEEE8021X_SHA256));
+ WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_OSEN));
}
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_wpa_psk(akm) ||
+ wpa_key_mgmt_sae(akm);
}
static inline int wpa_key_mgmt_wpa_any(int akm)
@@ -100,6 +109,7 @@
#define WPA_PROTO_WPA BIT(0)
#define WPA_PROTO_RSN BIT(1)
#define WPA_PROTO_WAPI BIT(2)
+#define WPA_PROTO_OSEN BIT(3)
#define WPA_AUTH_ALG_OPEN BIT(0)
#define WPA_AUTH_ALG_SHARED BIT(1)
@@ -117,41 +127,12 @@
WPA_ALG_PMK,
WPA_ALG_GCMP,
WPA_ALG_SMS4,
- WPA_ALG_KRK
-};
-
-/**
- * enum wpa_cipher - Cipher suites
- */
-enum wpa_cipher {
- CIPHER_NONE,
- CIPHER_WEP40,
- CIPHER_TKIP,
- CIPHER_CCMP,
- CIPHER_WEP104,
- CIPHER_GCMP,
- CIPHER_SMS4
-};
-
-/**
- * enum wpa_key_mgmt - Key management suites
- */
-enum wpa_key_mgmt {
- KEY_MGMT_802_1X,
- KEY_MGMT_PSK,
- KEY_MGMT_NONE,
- KEY_MGMT_802_1X_NO_WPA,
- KEY_MGMT_WPA_NONE,
- KEY_MGMT_FT_802_1X,
- KEY_MGMT_FT_PSK,
- KEY_MGMT_802_1X_SHA256,
- KEY_MGMT_PSK_SHA256,
- KEY_MGMT_WPS,
- KEY_MGMT_SAE,
- KEY_MGMT_FT_SAE,
- KEY_MGMT_WAPI_PSK,
- KEY_MGMT_WAPI_CERT,
- KEY_MGMT_CCKM
+ 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
};
/**
@@ -312,6 +293,7 @@
WPA_CTRL_REQ_EAP_PIN,
WPA_CTRL_REQ_EAP_OTP,
WPA_CTRL_REQ_EAP_PASSPHRASE,
+ WPA_CTRL_REQ_SIM,
NUM_WPA_CTRL_REQS
};
diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h
index 4811f38..6958661 100644
--- a/src/common/eapol_common.h
+++ b/src/common/eapol_common.h
@@ -22,17 +22,28 @@
/* followed by length octets of data */
} STRUCT_PACKED;
+struct ieee8023_hdr {
+ u8 dest[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u16 ethertype;
+} STRUCT_PACKED;
+
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
+#ifdef CONFIG_MACSEC
+#define EAPOL_VERSION 3
+#else /* CONFIG_MACSEC */
#define EAPOL_VERSION 2
+#endif /* CONFIG_MACSEC */
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
+ IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4,
+ IEEE802_1X_TYPE_EAPOL_MKA = 5,
};
enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index aab8ac6..173a400 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -108,10 +108,15 @@
elems->hs20 = pos;
elems->hs20_len = elen;
break;
+ case HS20_OSEN_OUI_TYPE:
+ /* Hotspot 2.0 OSEN */
+ elems->osen = pos;
+ elems->osen_len = elen;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
- "(type=%d len=%lu)\n",
+ "(type=%d len=%lu)",
pos[3], (unsigned long) elen);
return -1;
}
@@ -189,25 +194,12 @@
elems->supp_rates = pos;
elems->supp_rates_len = elen;
break;
- case WLAN_EID_FH_PARAMS:
- elems->fh_params = pos;
- elems->fh_params_len = elen;
- break;
case WLAN_EID_DS_PARAMS:
elems->ds_params = pos;
elems->ds_params_len = elen;
break;
case WLAN_EID_CF_PARAMS:
- elems->cf_params = pos;
- elems->cf_params_len = elen;
- break;
case WLAN_EID_TIM:
- elems->tim = pos;
- elems->tim_len = elen;
- break;
- case WLAN_EID_IBSS_PARAMS:
- elems->ibss_params = pos;
- elems->ibss_params_len = elen;
break;
case WLAN_EID_CHALLENGE:
elems->challenge = pos;
@@ -232,8 +224,6 @@
elems->rsn_ie_len = elen;
break;
case WLAN_EID_PWR_CAPABILITY:
- elems->power_cap = pos;
- elems->power_cap_len = elen;
break;
case WLAN_EID_SUPPORTED_CHANNELS:
elems->supp_channels = pos;
@@ -267,6 +257,11 @@
elems->vht_operation = pos;
elems->vht_operation_len = elen;
break;
+ case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
+ if (elen != 1)
+ break;
+ elems->vht_opmode_notif = pos;
+ break;
case WLAN_EID_LINK_ID:
if (elen < 18)
break;
@@ -276,6 +271,12 @@
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;
@@ -545,3 +546,60 @@
return num_11b > 0 && num_others == 0;
}
+
+
+const char * fc2str(u16 fc)
+{
+ u16 stype = WLAN_FC_GET_STYPE(fc);
+#define C2S(x) case x: return #x;
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case WLAN_FC_TYPE_MGMT:
+ switch (stype) {
+ C2S(WLAN_FC_STYPE_ASSOC_REQ)
+ C2S(WLAN_FC_STYPE_ASSOC_RESP)
+ C2S(WLAN_FC_STYPE_REASSOC_REQ)
+ C2S(WLAN_FC_STYPE_REASSOC_RESP)
+ C2S(WLAN_FC_STYPE_PROBE_REQ)
+ C2S(WLAN_FC_STYPE_PROBE_RESP)
+ C2S(WLAN_FC_STYPE_BEACON)
+ C2S(WLAN_FC_STYPE_ATIM)
+ C2S(WLAN_FC_STYPE_DISASSOC)
+ C2S(WLAN_FC_STYPE_AUTH)
+ C2S(WLAN_FC_STYPE_DEAUTH)
+ C2S(WLAN_FC_STYPE_ACTION)
+ }
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ switch (stype) {
+ C2S(WLAN_FC_STYPE_PSPOLL)
+ C2S(WLAN_FC_STYPE_RTS)
+ C2S(WLAN_FC_STYPE_CTS)
+ C2S(WLAN_FC_STYPE_ACK)
+ C2S(WLAN_FC_STYPE_CFEND)
+ C2S(WLAN_FC_STYPE_CFENDACK)
+ }
+ break;
+ case WLAN_FC_TYPE_DATA:
+ switch (stype) {
+ C2S(WLAN_FC_STYPE_DATA)
+ C2S(WLAN_FC_STYPE_DATA_CFACK)
+ C2S(WLAN_FC_STYPE_DATA_CFPOLL)
+ C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
+ C2S(WLAN_FC_STYPE_NULLFUNC)
+ C2S(WLAN_FC_STYPE_CFACK)
+ C2S(WLAN_FC_STYPE_CFPOLL)
+ C2S(WLAN_FC_STYPE_CFACKPOLL)
+ C2S(WLAN_FC_STYPE_QOS_DATA)
+ C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
+ C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
+ C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
+ C2S(WLAN_FC_STYPE_QOS_NULL)
+ C2S(WLAN_FC_STYPE_QOS_CFPOLL)
+ C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
+ }
+ break;
+ }
+ return "WLAN_FC_TYPE_UNKNOWN";
+#undef C2S
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 68c6b96..cf83057 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -13,11 +13,7 @@
struct ieee802_11_elems {
const u8 *ssid;
const u8 *supp_rates;
- const u8 *fh_params;
const u8 *ds_params;
- const u8 *cf_params;
- const u8 *tim;
- const u8 *ibss_params;
const u8 *challenge;
const u8 *erp_info;
const u8 *ext_supp_rates;
@@ -26,7 +22,6 @@
const u8 *wmm; /* WMM Information or Parameter Element */
const u8 *wmm_tspec;
const u8 *wps_ie;
- const u8 *power_cap;
const u8 *supp_channels;
const u8 *mdie;
const u8 *ftie;
@@ -35,23 +30,22 @@
const u8 *ht_operation;
const u8 *vht_capabilities;
const u8 *vht_operation;
+ const u8 *vht_opmode_notif;
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;
+ const u8 *osen;
u8 ssid_len;
u8 supp_rates_len;
- u8 fh_params_len;
u8 ds_params_len;
- u8 cf_params_len;
- u8 tim_len;
- u8 ibss_params_len;
u8 challenge_len;
u8 erp_info_len;
u8 ext_supp_rates_len;
@@ -60,7 +54,6 @@
u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
u8 wmm_tspec_len;
u8 wps_ie_len;
- u8 power_cap_len;
u8 supp_channels_len;
u8 mdie_len;
u8 ftie_len;
@@ -73,9 +66,11 @@
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;
+ u8 osen_len;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -103,4 +98,5 @@
int supp_rates_11b_only(struct ieee802_11_elems *elems);
+const char * fc2str(u16 fc);
#endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 137c309..6de71e9 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -161,6 +161,8 @@
#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_QUERY_RESP_OUTSTANDING 95
+#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
@@ -203,6 +205,7 @@
#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
@@ -221,10 +224,12 @@
#define WLAN_EID_QOS 46
#define WLAN_EID_RSN 48
#define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_NEIGHBOR_REPORT 52
#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
@@ -242,6 +247,7 @@
#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
@@ -267,6 +273,7 @@
#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
@@ -282,6 +289,19 @@
#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
@@ -566,9 +586,12 @@
/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
#define IEEE80211_HT_MCS_MASK_LEN 10
+/* HT Capabilities element */
struct ieee80211_ht_capabilities {
le16 ht_capabilities_info;
- u8 a_mpdu_params;
+ u8 a_mpdu_params; /* Maximum A-MPDU Length Exponent B0..B1
+ * Minimum MPDU Start Spacing B2..B4
+ * Reserved B5..B7 */
u8 supported_mcs_set[16];
le16 ht_extended_capabilities;
le32 tx_bf_capability_info;
@@ -576,12 +599,25 @@
} STRUCT_PACKED;
+/* HT Operation element */
struct ieee80211_ht_operation {
- u8 control_chan;
- u8 ht_param;
- le16 operation_mode;
- le16 stbc_param;
- u8 basic_set[16];
+ u8 primary_chan;
+ /* Five octets of HT Operation Information */
+ u8 ht_param; /* B0..B7 */
+ le16 operation_mode; /* B8..B23 */
+ le16 param; /* B24..B39 */
+ u8 basic_mcs_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;
@@ -610,7 +646,9 @@
#define ERP_INFO_USE_PROTECTION BIT(1)
#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
+#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5
+/* HT Capabilities Info field within HT Capabilities element */
#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)))
@@ -628,73 +666,86 @@
#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))
+/* B13 - Reserved (was PSMP support during P802.11n development) */
#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14))
#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15))
-
+/* HT Extended Capabilities field within HT Capabilities element */
#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0))
+#define EXT_HT_CAP_INFO_PCO_TRANS_TIME_MASK ((u16) (BIT(1) | BIT(2)))
#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1
+/* B3..B7 - Reserved */
+#define EXT_HT_CAP_INFO_MCS_FEEDBACK_MASK ((u16) (BIT(8) | BIT(9)))
#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8
-#define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10))
+#define EXT_HT_CAP_INFO_HTC_SUPPORT ((u16) BIT(10))
#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11))
+/* B12..B15 - Reserved */
+/* Transmit Beanforming Capabilities within HT Capabilities element */
+#define TX_BF_CAP_IMPLICIT_TXBF_RX_CAP ((u32) BIT(0))
+#define TX_BF_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
+#define TX_BF_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
+#define TX_BF_CAP_RX_NDP_CAP ((u32) BIT(3))
+#define TX_BF_CAP_TX_NDP_CAP ((u32) BIT(4))
+#define TX_BF_CAP_IMPLICIT_TX_BF_CAP ((u32) BIT(5))
+#define TX_BF_CAP_CALIBRATION_MASK ((u32) (BIT(6) | BIT(7))
+#define TX_BF_CAP_CALIB_OFFSET 6
+#define TX_BF_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
+#define TX_BF_CAP_EXPLICIT_NONCOMPR_STEERING_CAP ((u32) BIT(9))
+#define TX_BF_CAP_EXPLICIT_COMPR_STEERING_CAP ((u32) BIT(10))
+#define TX_BF_CAP_EXPLICIT_TX_BF_CSI_FEEDBACK_MASK ((u32) (BIT(10) | BIT(11)))
+#define TX_BF_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
+#define TX_BF_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
+#define TX_BF_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
+#define TX_BF_CAP_MINIMAL_GROUPING_OFFSET 17
+#define TX_BF_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
+#define TX_BF_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
+#define TX_BF_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
+#define TX_BF_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
+#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_MASK ((u32) (BIT(27) | BIT(28)))
+#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_OFFSET 27
+/* B29..B31 - Reserved */
-#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
+/* ASEL Capability field within HT Capabilities element */
+#define ASEL_CAP_ASEL_CAPABLE ((u8) BIT(0))
+#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
+#define ASEL_CAP_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
+#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
+#define ASEL_CAP_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
+#define ASEL_CAP_RX_AS_CAP ((u8) BIT(5))
+#define ASEL_CAP_TX_SOUNDING_PPDUS_CAP ((u8) BIT(6))
+/* B7 - Reserved */
-
-#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))
-
+/* First octet of HT Operation Information within HT Operation element */
#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_STA_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))
+/* B4..B7 - Reserved */
+/* HT Protection (B8..B9 of HT Operation Information) */
+#define HT_PROT_NO_PROTECTION 0
+#define HT_PROT_NONMEMBER_PROTECTION 1
+#define HT_PROT_20MHZ_PROTECTION 2
+#define HT_PROT_NON_HT_MIXED 3
+/* Bits within ieee80211_ht_operation::operation_mode (BIT(0) maps to B8 in
+ * HT Operation Information) */
+#define HT_OPER_OP_MODE_HT_PROT_MASK ((u16) (BIT(0) | BIT(1))) /* B8..B9 */
+#define HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT ((u16) BIT(2)) /* B10 */
+/* BIT(3), i.e., B11 in HT Operation Information field - Reserved */
+#define HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT ((u16) BIT(4)) /* B12 */
+/* BIT(5)..BIT(15), i.e., B13..B23 - Reserved */
-#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))
+/* Last two octets of HT Operation Information (BIT(0) = B24) */
+/* B24..B29 - Reserved */
+#define HT_OPER_PARAM_DUAL_BEACON ((u16) BIT(6))
+#define HT_OPER_PARAM_DUAL_CTS_PROTECTION ((u16) BIT(7))
+#define HT_OPER_PARAM_STBC_BEACON ((u16) BIT(8))
+#define HT_OPER_PARAM_LSIG_TXOP_PROT_FULL_SUPP ((u16) BIT(9))
+#define HT_OPER_PARAM_PCO_ACTIVE ((u16) BIT(10))
+#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11))
+/* B36..B39 - Reserved */
#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
@@ -702,8 +753,10 @@
/* 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))
@@ -712,20 +765,41 @@
#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_BEAMFORMER_ANTENNAS_MAX ((u32) BIT(13) | BIT(14))
-#define VHT_CAP_SOUNDING_DIMENTION_MAX ((u32) BIT(16) | BIT(17))
+#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))
+
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1 ((u32) BIT(23))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2 ((u32) BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3 ((u32) BIT(23) | BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4 ((u32) BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5 ((u32) BIT(23) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6 ((u32) BIT(24) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX ((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))
+#define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1))
+#define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \
+ BIT(6))
+#define VHT_OPMODE_NOTIF_RX_NSS_SHIFT 4
+
+#define VHT_RX_NSS_MAX_STREAMS 8
+
/* VHT channel widths */
#define VHT_CHANWIDTH_USE_HT 0
#define VHT_CHANWIDTH_80MHZ 1
@@ -735,12 +809,14 @@
#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
* 00:50:F2 */
#define WPA_IE_VENDOR_TYPE 0x0050f201
+#define WMM_IE_VENDOR_TYPE 0x0050f202
#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 OSEN_IE_VENDOR_TYPE 0x506f9a12
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -856,6 +932,7 @@
#define HS20_INDICATION_OUI_TYPE 16
#define HS20_ANQP_OUI_TYPE 17
+#define HS20_OSEN_OUI_TYPE 18
#define HS20_STYPE_QUERY_LIST 1
#define HS20_STYPE_CAPABILITY_LIST 2
#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
@@ -863,6 +940,21 @@
#define HS20_STYPE_CONNECTION_CAPABILITY 5
#define HS20_STYPE_NAI_HOME_REALM_QUERY 6
#define HS20_STYPE_OPERATING_CLASS 7
+#define HS20_STYPE_OSU_PROVIDERS_LIST 8
+#define HS20_STYPE_ICON_REQUEST 10
+#define HS20_STYPE_ICON_BINARY_FILE 11
+
+#define HS20_DGAF_DISABLED 0x01
+#define HS20_PPS_MO_ID_PRESENT 0x02
+#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
+#define HS20_VERSION 0x10 /* Release 2 */
+
+/* WNM-Notification WFA vendors specific subtypes */
+#define HS20_WNM_SUB_REM_NEEDED 0
+#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
+
+#define HS20_DEAUTH_REASON_CODE_BSS 0
+#define HS20_DEAUTH_REASON_CODE_ESS 1
/* Wi-Fi Direct (P2P) */
@@ -888,6 +980,7 @@
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
};
@@ -909,6 +1002,7 @@
#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)
@@ -933,6 +1027,12 @@
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
@@ -1002,6 +1102,11 @@
#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
@@ -1015,7 +1120,10 @@
#define WLAN_AKM_SUITE_PSK 0x000FAC02
#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03
#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04
+#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
+#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
#define WLAN_AKM_SUITE_CCKM 0x00409600
+#define WLAN_AKM_SUITE_OSEN 0x506f9a01
/* IEEE 802.11v - WNM Action field values */
@@ -1079,6 +1187,15 @@
#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)
@@ -1126,4 +1243,8 @@
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 */
diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h
new file mode 100644
index 0000000..cc88caa
--- /dev/null
+++ b/src/common/ieee802_1x_defs.h
@@ -0,0 +1,78 @@
+/*
+ * IEEE Std 802.1X-2010 definitions
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_DEFS_H
+#define IEEE802_1X_DEFS_H
+
+#define CS_ID_LEN 8
+#define CS_ID_GCM_AES_128 {0x00, 0x80, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01}
+#define CS_NAME_GCM_AES_128 "GCM-AES-128"
+
+enum macsec_policy {
+ /**
+ * Should secure sessions.
+ * This accepts key server's advice to determine whether to secure the
+ * session or not.
+ */
+ SHOULD_SECURE,
+
+ /**
+ * Disabled MACsec - do not secure sessions.
+ */
+ DO_NOT_SECURE,
+};
+
+
+/* IEEE Std 802.1X-2010 - Table 11-6 - MACsec Capability */
+enum macsec_cap {
+ /**
+ * MACsec is not implemented
+ */
+ MACSEC_CAP_NOT_IMPLEMENTED,
+
+ /**
+ * 'Integrity without confidentiality'
+ */
+ MACSEC_CAP_INTEGRITY,
+
+ /**
+ * 'Integrity without confidentiality' and
+ * 'Integrity and confidentiality' with a confidentiality offset of 0
+ */
+ MACSEC_CAP_INTEG_AND_CONF,
+
+ /**
+ * 'Integrity without confidentiality' and
+ * 'Integrity and confidentiality' with a confidentiality offset of 0,
+ * 30, 50
+ */
+ MACSEC_CAP_INTEG_AND_CONF_0_30_50,
+};
+
+enum validate_frames {
+ Disabled,
+ Checked,
+ Strict,
+};
+
+/* IEEE Std 802.1X-2010 - Table 11-6 - Confidentiality Offset */
+enum confidentiality_offset {
+ CONFIDENTIALITY_NONE = 0,
+ CONFIDENTIALITY_OFFSET_0 = 1,
+ CONFIDENTIALITY_OFFSET_30 = 2,
+ CONFIDENTIALITY_OFFSET_50 = 3,
+};
+
+/* IEEE Std 802.1X-2010 - Table 9-2 */
+#define DEFAULT_PRIO_INFRA_PORT 0x10
+#define DEFAULT_PRIO_PRIMRAY_AP 0x30
+#define DEFAULT_PRIO_SECONDARY_AP 0x50
+#define DEFAULT_PRIO_GROUP_CA_MEMBER 0x70
+#define DEFAULT_PRIO_NOT_KEY_SERVER 0xFF
+
+#endif /* IEEE802_1X_DEFS_H */
diff --git a/src/common/qca-vendor-attr.h b/src/common/qca-vendor-attr.h
new file mode 100644
index 0000000..6f51803
--- /dev/null
+++ b/src/common/qca-vendor-attr.h
@@ -0,0 +1,28 @@
+/*
+ * Qualcomm Atheros vendor specific attribute definitions
+ * 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_ATTR_H
+#define QCA_VENDOR_ATTR_H
+
+/*
+ * This file defines some of the attributes used with Qualcomm Atheros OUI
+ * 00:13:74 in a way that is not suitable for qca-vendor.h, e.g., due to
+ * compiler dependencies.
+ */
+
+struct qca_avoid_freq_range {
+ u32 start_freq;
+ u32 end_freq;
+} __attribute__ ((packed));
+
+struct qca_avoid_freq_list {
+ u32 count;
+ struct qca_avoid_freq_range range[0];
+} __attribute__ ((packed));
+
+#endif /* QCA_VENDOR_ATTR_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
new file mode 100644
index 0000000..4d9efd5
--- /dev/null
+++ b/src/common/qca-vendor.h
@@ -0,0 +1,74 @@
+/*
+ * 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_radiotap_vendor_ids - QCA radiotap vendor namespace IDs
+ */
+enum qca_radiotap_vendor_ids {
+ QCA_RADIOTAP_VID_WLANTEST = 0,
+};
+
+/**
+ * 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.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support
+ * for DFS offloading.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
+ * NAN Request/Response and NAN Indication messages. These messages are
+ * interpreted between the framework and the firmware component.
+ */
+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,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11,
+ QCA_NL80211_VENDOR_SUBCMD_NAN = 12,
+ QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
+ /* 14..33 - reserved for QCA */
+};
+
+
+enum qca_wlan_vendor_attr {
+ QCA_WLAN_VENDOR_ATTR_INVALID = 0,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
+ QCA_WLAN_VENDOR_ATTR_DFS = 1,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */
+ QCA_WLAN_VENDOR_ATTR_NAN = 2,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+ QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+ QCA_WLAN_VENDOR_ATTR_IFINDEX = 4,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
+};
+
+#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index bce60a3..b67623f 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -134,8 +134,10 @@
return NULL;
if (crypto_bignum_is_zero(bn) ||
crypto_bignum_is_one(bn) ||
- crypto_bignum_cmp(bn, sae->tmp->order) >= 0)
+ crypto_bignum_cmp(bn, sae->tmp->order) >= 0) {
+ crypto_bignum_deinit(bn, 0);
continue;
+ }
break;
}
@@ -503,6 +505,8 @@
const u8 *password, size_t password_len,
struct sae_data *sae)
{
+ if (sae->tmp == NULL)
+ return -1;
if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
password_len) < 0)
return -1;
@@ -634,7 +638,8 @@
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) ||
+ if (sae->tmp == NULL ||
+ (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;
@@ -646,6 +651,10 @@
const struct wpabuf *token)
{
u8 *pos;
+
+ if (sae->tmp == NULL)
+ return;
+
wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
if (token)
wpabuf_put_buf(buf, token);
@@ -678,7 +687,7 @@
{
if (allowed_groups) {
int i;
- for (i = 0; allowed_groups[i] >= 0; i++) {
+ for (i = 0; allowed_groups[i] > 0; i++) {
if (allowed_groups[i] == group)
break;
}
@@ -701,6 +710,11 @@
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);
@@ -797,7 +811,7 @@
/* element x and y coordinates < p */
if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
- os_memcmp(pos + sae->tmp->prime_len + sae->tmp->prime_len, prime,
+ os_memcmp(pos + sae->tmp->prime_len, prime,
sae->tmp->prime_len) >= 0) {
wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
"element");
@@ -985,6 +999,9 @@
{
const u8 *sc;
+ if (sae->tmp == NULL)
+ return;
+
/* Send-Confirm */
sc = wpabuf_put(buf, 0);
wpabuf_put_le16(buf, sae->send_confirm);
@@ -1016,6 +1033,11 @@
wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
+ if (sae->tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
+ return -1;
+ }
+
if (sae->tmp->ec)
sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ecc,
@@ -1029,7 +1051,7 @@
sae->tmp->own_commit_element_ffc,
verifier);
- if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+ if (os_memcmp_const(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);
diff --git a/src/common/tnc.h b/src/common/tnc.h
new file mode 100644
index 0000000..108acf9
--- /dev/null
+++ b/src/common/tnc.h
@@ -0,0 +1,121 @@
+/*
+ * TNC - Common defines
+ * Copyright (c) 2007-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TNC_H
+#define TNC_H
+
+typedef unsigned long TNC_UInt32;
+typedef unsigned char *TNC_BufferReference;
+
+typedef TNC_UInt32 TNC_IMVID;
+typedef TNC_UInt32 TNC_IMCID;
+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_MessageSubtype;
+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);
+typedef TNC_Result (*TNC_TNCS_ReportMessageTypesPointer)(
+ TNC_IMVID imvID,
+ TNC_MessageTypeList supportedTypes,
+ TNC_UInt32 typeCount);
+typedef TNC_Result (*TNC_TNCS_SendMessagePointer)(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_BufferReference message,
+ TNC_UInt32 messageLength,
+ TNC_MessageType messageType);
+typedef TNC_Result (*TNC_TNCS_RequestHandshakeRetryPointer)(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_RetryReason reason);
+typedef TNC_Result (*TNC_TNCS_ProvideRecommendationPointer)(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_IMV_Action_Recommendation recommendation,
+ TNC_IMV_Evaluation_Result evaluation);
+typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
+ TNC_IMCID imcID,
+ char *functionName,
+ void **pOutfunctionPointer);
+typedef TNC_Result (*TNC_TNCC_SendMessagePointer)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_BufferReference message,
+ TNC_UInt32 messageLength,
+ TNC_MessageType messageType);
+typedef TNC_Result (*TNC_TNCC_ReportMessageTypesPointer)(
+ TNC_IMCID imcID,
+ TNC_MessageTypeList supportedTypes,
+ TNC_UInt32 typeCount);
+typedef TNC_Result (*TNC_TNCC_RequestHandshakeRetryPointer)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_RetryReason reason);
+
+#define TNC_IFIMV_VERSION_1 1
+#define TNC_IFIMC_VERSION_1 1
+
+#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_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
+};
+
+#endif /* TNC_H */
diff --git a/src/common/version.h b/src/common/version.h
index 2faa8c7..1f25432 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,6 @@
#define VERSION_STR_POSTFIX ""
#endif /* VERSION_STR_POSTFIX */
-#define VERSION_STR "2.1-devel" VERSION_STR_POSTFIX
+#define VERSION_STR "2.3-devel" VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index c3afbfd..998a51a 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -14,6 +14,7 @@
#include "crypto/sha256.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
+#include "drivers/driver.h"
#include "ieee802_11_defs.h"
#include "defs.h"
#include "wpa_common.h"
@@ -56,6 +57,11 @@
case WPA_KEY_INFO_TYPE_AES_128_CMAC:
return omac1_aes_128(key, buf, len, mic);
#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_HS20
+ case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+ /* FIX: This should be based on negotiated AKM */
+ return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_HS20 */
default:
return -1;
}
@@ -353,6 +359,18 @@
#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;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
+ return WPA_CIPHER_GTK_NOT_USED;
return 0;
}
@@ -385,6 +403,26 @@
}
+static int wpa_cipher_valid_group(int cipher)
+{
+ return wpa_cipher_valid_pairwise(cipher) ||
+ cipher == WPA_CIPHER_WEP104 ||
+ cipher == WPA_CIPHER_WEP40 ||
+ cipher == WPA_CIPHER_GTK_NOT_USED;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+int wpa_cipher_valid_mgmt_group(int cipher)
+{
+ return cipher == WPA_CIPHER_AES_128_CMAC ||
+ cipher == WPA_CIPHER_BIP_GMAC_128 ||
+ cipher == WPA_CIPHER_BIP_GMAC_256 ||
+ cipher == WPA_CIPHER_BIP_CMAC_256;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
/**
* wpa_parse_wpa_ie_rsn - Parse RSN IE
* @rsn_ie: Buffer containing RSN IE
@@ -440,13 +478,11 @@
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__);
+ if (!wpa_cipher_valid_group(data->group_cipher)) {
+ wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x",
+ __func__, data->group_cipher);
return -1;
}
-#endif /* CONFIG_IEEE80211W */
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
} else if (left > 0) {
@@ -531,7 +567,7 @@
#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) {
+ if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
wpa_printf(MSG_DEBUG, "%s: Unsupported management "
"group cipher 0x%x", __func__,
data->mgmt_group_cipher);
@@ -543,8 +579,9 @@
#endif /* CONFIG_IEEE80211W */
if (left > 0) {
- wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
- __func__, left);
+ wpa_hexdump(MSG_DEBUG,
+ "wpa_parse_wpa_ie_rsn: ignore trailing bytes",
+ pos, left);
}
return 0;
@@ -681,8 +718,9 @@
}
if (left > 0) {
- wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
- __func__, left);
+ wpa_hexdump(MSG_DEBUG,
+ "wpa_parse_wpa_ie_wpa: ignore trailing bytes",
+ pos, left);
}
return 0;
@@ -912,6 +950,12 @@
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";
}
@@ -959,6 +1003,30 @@
}
+u32 wpa_akm_to_suite(int akm)
+{
+ if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
+ return WLAN_AKM_SUITE_FT_8021X;
+ if (akm & WPA_KEY_MGMT_FT_PSK)
+ return WLAN_AKM_SUITE_FT_PSK;
+ if (akm & WPA_KEY_MGMT_IEEE8021X)
+ return WLAN_AKM_SUITE_8021X;
+ if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ return WLAN_AKM_SUITE_8021X_SHA256;
+ if (akm & WPA_KEY_MGMT_IEEE8021X)
+ return WLAN_AKM_SUITE_8021X;
+ if (akm & WPA_KEY_MGMT_PSK_SHA256)
+ return WLAN_AKM_SUITE_PSK_SHA256;
+ if (akm & WPA_KEY_MGMT_PSK)
+ return WLAN_AKM_SUITE_PSK;
+ if (akm & WPA_KEY_MGMT_CCKM)
+ return WLAN_AKM_SUITE_CCKM;
+ if (akm & WPA_KEY_MGMT_OSEN)
+ return WLAN_AKM_SUITE_OSEN;
+ return 0;
+}
+
+
int wpa_compare_rsn_ie(int ft_initial_assoc,
const u8 *ie1, size_t ie1len,
const u8 *ie2, size_t ie2len)
@@ -1078,8 +1146,15 @@
int wpa_cipher_key_len(int cipher)
{
switch (cipher) {
+ case WPA_CIPHER_CCMP_256:
+ case WPA_CIPHER_GCMP_256:
+ case WPA_CIPHER_BIP_GMAC_256:
+ case WPA_CIPHER_BIP_CMAC_256:
+ return 32;
case WPA_CIPHER_CCMP:
case WPA_CIPHER_GCMP:
+ case WPA_CIPHER_AES_128_CMAC:
+ case WPA_CIPHER_BIP_GMAC_128:
return 16;
case WPA_CIPHER_TKIP:
return 32;
@@ -1096,6 +1171,8 @@
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:
@@ -1112,6 +1189,10 @@
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:
@@ -1121,34 +1202,24 @@
case WPA_CIPHER_WEP104:
case WPA_CIPHER_WEP40:
return WPA_ALG_WEP;
+ case WPA_CIPHER_AES_128_CMAC:
+ return WPA_ALG_IGTK;
+ case WPA_CIPHER_BIP_GMAC_128:
+ return WPA_ALG_BIP_GMAC_128;
+ case WPA_CIPHER_BIP_GMAC_256:
+ return WPA_ALG_BIP_GMAC_256;
+ case WPA_CIPHER_BIP_CMAC_256:
+ return WPA_ALG_BIP_CMAC_256;
}
return WPA_ALG_NONE;
}
-enum wpa_cipher wpa_cipher_to_suite_driver(int cipher)
-{
- switch (cipher) {
- case WPA_CIPHER_NONE:
- return CIPHER_NONE;
- case WPA_CIPHER_WEP40:
- return CIPHER_WEP40;
- case WPA_CIPHER_WEP104:
- return CIPHER_WEP104;
- case WPA_CIPHER_CCMP:
- return CIPHER_CCMP;
- case WPA_CIPHER_GCMP:
- return CIPHER_GCMP;
- case WPA_CIPHER_TKIP:
- default:
- return CIPHER_TKIP;
- }
-}
-
-
int wpa_cipher_valid_pairwise(int cipher)
{
- return cipher == WPA_CIPHER_CCMP ||
+ return cipher == WPA_CIPHER_CCMP_256 ||
+ cipher == WPA_CIPHER_GCMP_256 ||
+ cipher == WPA_CIPHER_CCMP ||
cipher == WPA_CIPHER_GCMP ||
cipher == WPA_CIPHER_TKIP;
}
@@ -1156,6 +1227,10 @@
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);
@@ -1173,65 +1248,80 @@
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;
+ if (cipher & WPA_CIPHER_AES_128_CMAC)
+ return RSN_CIPHER_SUITE_AES_128_CMAC;
+ if (cipher & WPA_CIPHER_BIP_GMAC_128)
+ return RSN_CIPHER_SUITE_BIP_GMAC_128;
+ if (cipher & WPA_CIPHER_BIP_GMAC_256)
+ return RSN_CIPHER_SUITE_BIP_GMAC_256;
+ if (cipher & WPA_CIPHER_BIP_CMAC_256)
+ return RSN_CIPHER_SUITE_BIP_CMAC_256;
return 0;
}
-int rsn_cipher_put_suites(u8 *pos, int ciphers)
+int rsn_cipher_put_suites(u8 *start, int ciphers)
{
- int num_suites = 0;
+ u8 *pos = start;
+ if (ciphers & WPA_CIPHER_CCMP_256) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256);
+ pos += RSN_SELECTOR_LEN;
+ }
+ if (ciphers & WPA_CIPHER_GCMP_256) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256);
+ pos += RSN_SELECTOR_LEN;
+ }
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;
+ return (pos - start) / RSN_SELECTOR_LEN;
}
-int wpa_cipher_put_suites(u8 *pos, int ciphers)
+int wpa_cipher_put_suites(u8 *start, int ciphers)
{
- int num_suites = 0;
+ u8 *pos = start;
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;
+ return (pos - start) / RSN_SELECTOR_LEN;
}
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)
@@ -1246,10 +1336,16 @@
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)
@@ -1280,7 +1376,11 @@
end++;
last = *end == '\0';
*end = '\0';
- if (os_strcmp(start, "CCMP") == 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;
@@ -1292,6 +1392,8 @@
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;
@@ -1312,6 +1414,20 @@
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);
@@ -1373,5 +1489,86 @@
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;
}
+
+
+static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
+ int capa_trigger, u8 *param_trigger)
+{
+ if (os_strcmp(start, trigger) != 0)
+ return 0;
+ if (!capa_trigger)
+ return 0;
+
+ *param_trigger = 1;
+ return 1;
+}
+
+
+struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
+ struct wpa_driver_capa *capa)
+{
+ struct wowlan_triggers *triggers;
+ char *start, *end, *buf;
+ int last;
+
+ if (!wowlan_triggers)
+ return NULL;
+
+ buf = os_strdup(wowlan_triggers);
+ if (buf == NULL)
+ return NULL;
+
+ triggers = os_zalloc(sizeof(*triggers));
+ if (triggers == NULL)
+ goto out;
+
+#define CHECK_TRIGGER(trigger) \
+ wpa_check_wowlan_trigger(start, #trigger, \
+ capa->wowlan_triggers.trigger, \
+ &triggers->trigger)
+
+ start = buf;
+ while (*start != '\0') {
+ while (isblank(*start))
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+ while (!isblank(*end) && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+
+ if (!CHECK_TRIGGER(any) &&
+ !CHECK_TRIGGER(disconnect) &&
+ !CHECK_TRIGGER(magic_pkt) &&
+ !CHECK_TRIGGER(gtk_rekey_failure) &&
+ !CHECK_TRIGGER(eap_identity_req) &&
+ !CHECK_TRIGGER(four_way_handshake) &&
+ !CHECK_TRIGGER(rfkill_release)) {
+ wpa_printf(MSG_DEBUG,
+ "Unknown/unsupported wowlan trigger '%s'",
+ start);
+ os_free(triggers);
+ triggers = NULL;
+ goto out;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+#undef CHECK_TRIGGER
+
+out:
+ os_free(buf);
+ return triggers;
+}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 2d63662..0ef5a9d 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -21,10 +21,12 @@
#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_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_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
+WPA_CIPHER_GTK_NOT_USED)
#define WPA_SELECTOR_LEN 4
#define WPA_VERSION 1
@@ -60,7 +62,12 @@
#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_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
@@ -70,11 +77,14 @@
#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.
@@ -98,6 +108,9 @@
#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))
@@ -115,6 +128,7 @@
#ifdef CONFIG_IEEE80211W
#define WPA_IGTK_LEN 16
+#define WPA_IGTK_MAX_LEN 32
#endif /* CONFIG_IEEE80211W */
@@ -143,6 +157,7 @@
/* 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_AKM_DEFINED 0
#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
@@ -269,10 +284,11 @@
} STRUCT_PACKED;
#ifdef CONFIG_IEEE80211W
+#define WPA_IGTK_KDE_PREFIX_LEN (2 + 6)
struct wpa_igtk_kde {
u8 keyid[2];
u8 pn[6];
- u8 igtk[WPA_IGTK_LEN];
+ u8 igtk[WPA_IGTK_MAX_LEN];
} STRUCT_PACKED;
#endif /* CONFIG_IEEE80211W */
@@ -361,6 +377,7 @@
const char * wpa_cipher_txt(int cipher);
const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
+u32 wpa_akm_to_suite(int akm);
int wpa_compare_rsn_ie(int ft_initial_assoc,
const u8 *ie1, size_t ie1len,
const u8 *ie2, size_t ie2len);
@@ -392,8 +409,8 @@
int wpa_cipher_key_len(int cipher);
int wpa_cipher_rsc_len(int cipher);
int wpa_cipher_to_alg(int cipher);
-enum wpa_cipher wpa_cipher_to_suite_driver(int cipher);
int wpa_cipher_valid_pairwise(int cipher);
+int wpa_cipher_valid_mgmt_group(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);
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index 83788d7..5820a13 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -25,6 +25,10 @@
#include "private/android_filesystem_config.h"
#endif /* ANDROID */
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+#include <net/if.h>
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+
#include "wpa_ctrl.h"
#include "common.h"
@@ -46,8 +50,13 @@
struct wpa_ctrl {
#ifdef CONFIG_CTRL_IFACE_UDP
int s;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 local;
+ struct sockaddr_in6 dest;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in local;
struct sockaddr_in dest;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
char *cookie;
char *remote_ifname;
char *remote_ip;
@@ -279,19 +288,33 @@
return NULL;
os_memset(ctrl, 0, sizeof(*ctrl));
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (ctrl->s < 0) {
perror("socket");
os_free(ctrl);
return NULL;
}
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ ctrl->local.sin6_family = AF_INET6;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ ctrl->local.sin6_addr = in6addr_any;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+ inet_pton(AF_INET6, "::1", &ctrl->local.sin6_addr);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
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 */
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+
if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
sizeof(ctrl->local)) < 0) {
close(ctrl->s);
@@ -299,14 +322,24 @@
return NULL;
}
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ ctrl->dest.sin6_family = AF_INET6;
+ inet_pton(AF_INET6, "::1", &ctrl->dest.sin6_addr);
+ ctrl->dest.sin6_port = htons(WPA_CTRL_IFACE_PORT);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
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);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
if (ctrl_path) {
char *port, *name;
int port_id;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char *scope;
+ int scope_id = 0;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
name = os_strdup(ctrl_path);
if (name == NULL) {
@@ -314,7 +347,11 @@
os_free(ctrl);
return NULL;
}
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ port = os_strchr(name, ',');
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
port = os_strchr(name, ':');
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (port) {
port_id = atoi(&port[1]);
@@ -322,7 +359,16 @@
} else
port_id = WPA_CTRL_IFACE_PORT;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ scope = os_strchr(name, '%');
+ if (scope) {
+ scope_id = if_nametoindex(&scope[1]);
+ scope[0] = '\0';
+ }
+ h = gethostbyname2(name, AF_INET6);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
h = gethostbyname(name);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
ctrl->remote_ip = os_strdup(name);
os_free(name);
if (h == NULL) {
@@ -332,16 +378,33 @@
os_free(ctrl);
return NULL;
}
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ ctrl->dest.sin6_scope_id = scope_id;
+ ctrl->dest.sin6_port = htons(port_id);
+ os_memcpy(&ctrl->dest.sin6_addr, h->h_addr, h->h_length);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
ctrl->dest.sin_port = htons(port_id);
- os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr,
- h->h_length);
+ os_memcpy(&ctrl->dest.sin_addr.s_addr, h->h_addr, h->h_length);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
} 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");
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+ wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s",
+ inet_ntop(AF_INET6, &ctrl->dest.sin6_addr, addr,
+ sizeof(ctrl->dest)),
+ ntohs(ctrl->dest.sin6_port),
+ strerror(errno));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+ wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s",
+ inet_ntoa(ctrl->dest.sin_addr),
+ ntohs(ctrl->dest.sin_port),
+ strerror(errno));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
close(ctrl->s);
os_free(ctrl->remote_ip);
os_free(ctrl);
@@ -391,7 +454,7 @@
void (*msg_cb)(char *msg, size_t len))
{
struct timeval tv;
- struct os_time started_at;
+ struct os_reltime started_at;
int res;
fd_set rfds;
const char *_cmd;
@@ -430,12 +493,12 @@
* longer before giving up.
*/
if (started_at.sec == 0)
- os_get_time(&started_at);
+ os_get_reltime(&started_at);
else {
- struct os_time n;
- os_get_time(&n);
+ struct os_reltime n;
+ os_get_reltime(&n);
/* Try for a few seconds. */
- if (n.sec > started_at.sec + 5)
+ if (os_reltime_expired(&n, &started_at, 5))
goto send_err;
}
os_sleep(1, 0);
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index fd7f686..d91594e 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -54,6 +54,8 @@
#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 */
@@ -62,14 +64,22 @@
#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 "
-#ifdef ANDROID_P2P
-/** Notify the Userspace about the freq conflict */
-#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
-#endif
+/** Change in the signal level was reported by the driver */
+#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
+/** Regulatory domain channel */
+#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
/** 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 */
@@ -143,14 +153,39 @@
#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: <PMF enabled> <timeout in ms> <Session Information URL> */
#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_BLACKLISTED "INTERWORKING-BLACKLISTED "
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
+#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED "
+#define INTERWORKING_SELECTED "INTERWORKING-SELECTED "
+
+/* Credential block added; parameters: <id> */
+#define CRED_ADDED "CRED-ADDED "
+/* Credential block modified; parameters: <id> <field> */
+#define CRED_MODIFIED "CRED-MODIFIED "
+/* Credential block removed; parameters: <id> */
+#define CRED_REMOVED "CRED-REMOVED "
#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
+/* parameters: <addr> <dialog_token> <freq> */
+#define GAS_QUERY_START "GAS-QUERY-START "
+/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
+#define GAS_QUERY_DONE "GAS-QUERY-DONE "
+
+#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
+#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
+
+#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 "
@@ -166,6 +201,21 @@
#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
@@ -189,6 +239,25 @@
#define WPA_BSS_MASK_DELIM BIT(17)
+/* VENDOR_ELEM_* frame id values */
+enum wpa_vendor_elem_frame {
+ VENDOR_ELEM_PROBE_REQ_P2P = 0,
+ VENDOR_ELEM_PROBE_RESP_P2P = 1,
+ VENDOR_ELEM_PROBE_RESP_P2P_GO = 2,
+ VENDOR_ELEM_BEACON_P2P_GO = 3,
+ VENDOR_ELEM_P2P_PD_REQ = 4,
+ VENDOR_ELEM_P2P_PD_RESP = 5,
+ VENDOR_ELEM_P2P_GO_NEG_REQ = 6,
+ VENDOR_ELEM_P2P_GO_NEG_RESP = 7,
+ VENDOR_ELEM_P2P_GO_NEG_CONF = 8,
+ VENDOR_ELEM_P2P_INV_REQ = 9,
+ VENDOR_ELEM_P2P_INV_RESP = 10,
+ VENDOR_ELEM_P2P_ASSOC_REQ = 11,
+ VENDOR_ELEM_P2P_ASSOC_RESP = 12,
+ NUM_VENDOR_ELEM_FRAMES
+};
+
+
/* wpa_supplicant/hostapd control interface access */
/**
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
new file mode 100644
index 0000000..28913b9
--- /dev/null
+++ b/src/common/wpa_helpers.c
@@ -0,0 +1,292 @@
+/*
+ * wpa_supplicant ctrl_iface helpers
+ * Copyright (c) 2010-2011, 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 <time.h>
+
+#include "common.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+
+
+char *wpas_ctrl_path = "/var/run/wpa_supplicant/";
+static int default_timeout = 60;
+
+
+static struct wpa_ctrl * wpa_open_ctrl(const char *ifname)
+{
+ char buf[128];
+ struct wpa_ctrl *ctrl;
+
+ os_snprintf(buf, sizeof(buf), "%s%s", wpas_ctrl_path, ifname);
+ ctrl = wpa_ctrl_open(buf);
+ if (ctrl == NULL)
+ printf("wpa_command: wpa_ctrl_open(%s) failed\n", buf);
+ return ctrl;
+}
+
+
+int wpa_command(const char *ifname, const char *cmd)
+{
+ struct wpa_ctrl *ctrl;
+ char buf[128];
+ size_t len;
+
+ printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd);
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return -1;
+ len = sizeof(buf);
+ if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL) < 0) {
+ printf("wpa_command: wpa_ctrl_request failed\n");
+ wpa_ctrl_close(ctrl);
+ return -1;
+ }
+ wpa_ctrl_close(ctrl);
+ buf[len] = '\0';
+ if (strncmp(buf, "FAIL", 4) == 0) {
+ printf("wpa_command: Command failed (FAIL received)\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+int wpa_command_resp(const char *ifname, const char *cmd,
+ char *resp, size_t resp_size)
+{
+ struct wpa_ctrl *ctrl;
+ size_t len;
+
+ printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd);
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return -1;
+ len = resp_size;
+ if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), resp, &len, NULL) < 0) {
+ printf("wpa_command: wpa_ctrl_request failed\n");
+ wpa_ctrl_close(ctrl);
+ return -1;
+ }
+ wpa_ctrl_close(ctrl);
+ resp[len] = '\0';
+ return 0;
+}
+
+
+struct wpa_ctrl * open_wpa_mon(const char *ifname)
+{
+ struct wpa_ctrl *ctrl;
+
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return NULL;
+ if (wpa_ctrl_attach(ctrl) < 0) {
+ wpa_ctrl_close(ctrl);
+ return NULL;
+ }
+
+ return ctrl;
+}
+
+
+int get_wpa_cli_event2(struct wpa_ctrl *mon,
+ const char *event, const char *event2,
+ char *buf, size_t buf_size)
+{
+ int fd, ret;
+ fd_set rfd;
+ char *pos;
+ struct timeval tv;
+ time_t start, now;
+
+ printf("Waiting for wpa_cli event %s\n", event);
+ fd = wpa_ctrl_get_fd(mon);
+ if (fd < 0)
+ return -1;
+
+ time(&start);
+ while (1) {
+ size_t len;
+
+ FD_ZERO(&rfd);
+ FD_SET(fd, &rfd);
+ tv.tv_sec = default_timeout;
+ tv.tv_usec = 0;
+ ret = select(fd + 1, &rfd, NULL, NULL, &tv);
+ if (ret == 0) {
+ printf("Timeout on waiting for event %s\n", event);
+ return -1;
+ }
+ if (ret < 0) {
+ printf("select: %s\n", strerror(errno));
+ return -1;
+ }
+ len = buf_size;
+ if (wpa_ctrl_recv(mon, buf, &len) < 0) {
+ printf("Failure while waiting for event %s\n", event);
+ return -1;
+ }
+ if (len == buf_size)
+ len--;
+ buf[len] = '\0';
+
+ pos = strchr(buf, '>');
+ if (pos &&
+ (strncmp(pos + 1, event, strlen(event)) == 0 ||
+ (event2 &&
+ strncmp(pos + 1, event2, strlen(event2)) == 0)))
+ return 0; /* Event found */
+
+ time(&now);
+ if ((int) (now - start) > default_timeout) {
+ printf("Timeout on waiting for event %s\n", event);
+ return -1;
+ }
+ }
+}
+
+
+int get_wpa_cli_event(struct wpa_ctrl *mon,
+ const char *event, char *buf, size_t buf_size)
+{
+ return get_wpa_cli_event2(mon, event, NULL, buf, buf_size);
+}
+
+
+int get_wpa_status(const char *ifname, const char *field, char *obuf,
+ size_t obuf_size)
+{
+ struct wpa_ctrl *ctrl;
+ char buf[4096];
+ char *pos, *end;
+ size_t len, flen;
+
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return -1;
+ len = sizeof(buf);
+ if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) {
+ wpa_ctrl_close(ctrl);
+ return -1;
+ }
+ wpa_ctrl_close(ctrl);
+ buf[len] = '\0';
+
+ flen = strlen(field);
+ pos = buf;
+ while (pos + flen < buf + len) {
+ if (pos > buf) {
+ if (*pos != '\n') {
+ pos++;
+ continue;
+ }
+ pos++;
+ }
+ if (strncmp(pos, field, flen) != 0 || pos[flen] != '=') {
+ pos++;
+ continue;
+ }
+ pos += flen + 1;
+ end = strchr(pos, '\n');
+ if (end == NULL)
+ return -1;
+ *end++ = '\0';
+ if (end - pos > (int) obuf_size)
+ return -1;
+ memcpy(obuf, pos, end - pos);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int wait_ip_addr(const char *ifname, int timeout)
+{
+ char ip[30];
+ int count = timeout;
+ struct wpa_ctrl *ctrl;
+
+ while (count > 0) {
+ printf("%s: ifname='%s' - %d seconds remaining\n",
+ __func__, ifname, count);
+ count--;
+ if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0
+ && strlen(ip) > 0) {
+ printf("IP address found: '%s'\n", ip);
+ return 0;
+ }
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+ return -1;
+ wpa_ctrl_close(ctrl);
+ sleep(1);
+ }
+ printf("%s: Could not get IP address for ifname='%s'", __func__,
+ ifname);
+ return -1;
+}
+
+
+int add_network(const char *ifname)
+{
+ char res[30];
+
+ if (wpa_command_resp(ifname, "ADD_NETWORK", res, sizeof(res)) < 0)
+ return -1;
+ return atoi(res);
+}
+
+
+int set_network(const char *ifname, int id, const char *field,
+ const char *value)
+{
+ char buf[200];
+ snprintf(buf, sizeof(buf), "SET_NETWORK %d %s %s", id, field, value);
+ return wpa_command(ifname, buf);
+}
+
+
+int set_network_quoted(const char *ifname, int id, const char *field,
+ const char *value)
+{
+ char buf[200];
+ snprintf(buf, sizeof(buf), "SET_NETWORK %d %s \"%s\"",
+ id, field, value);
+ return wpa_command(ifname, buf);
+}
+
+
+int add_cred(const char *ifname)
+{
+ char res[30];
+
+ if (wpa_command_resp(ifname, "ADD_CRED", res, sizeof(res)) < 0)
+ return -1;
+ return atoi(res);
+}
+
+
+int set_cred(const char *ifname, int id, const char *field, const char *value)
+{
+ char buf[200];
+ snprintf(buf, sizeof(buf), "SET_CRED %d %s %s", id, field, value);
+ return wpa_command(ifname, buf);
+}
+
+
+int set_cred_quoted(const char *ifname, int id, const char *field,
+ const char *value)
+{
+ char buf[200];
+ snprintf(buf, sizeof(buf), "SET_CRED %d %s \"%s\"",
+ id, field, value);
+ return wpa_command(ifname, buf);
+}
diff --git a/src/common/wpa_helpers.h b/src/common/wpa_helpers.h
new file mode 100644
index 0000000..54c2872
--- /dev/null
+++ b/src/common/wpa_helpers.h
@@ -0,0 +1,37 @@
+/*
+ * wpa_supplicant ctrl_iface helpers
+ * Copyright (c) 2010-2011, 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.
+ */
+
+#ifndef WPA_HELPERS_H
+#define WPA_HELPERS_H
+
+int wpa_command(const char *ifname, const char *cmd);
+int wpa_command_resp(const char *ifname, const char *cmd,
+ char *resp, size_t resp_size);
+int get_wpa_status(const char *ifname, const char *field, char *obuf,
+ size_t obuf_size);
+
+struct wpa_ctrl * open_wpa_mon(const char *ifname);
+int wait_ip_addr(const char *ifname, int timeout);
+int get_wpa_cli_event(struct wpa_ctrl *mon,
+ const char *event, char *buf, size_t buf_size);
+int get_wpa_cli_event2(struct wpa_ctrl *mon,
+ const char *event, const char *event2,
+ char *buf, size_t buf_size);
+
+int add_network(const char *ifname);
+int set_network(const char *ifname, int id, const char *field,
+ const char *value);
+int set_network_quoted(const char *ifname, int id, const char *field,
+ const char *value);
+int add_cred(const char *ifname);
+int set_cred(const char *ifname, int id, const char *field, const char *value);
+int set_cred_quoted(const char *ifname, int id, const char *field,
+ const char *value);
+
+#endif /* WPA_HELPERS_H */
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index a605a65..2a92109 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -1,7 +1,7 @@
all: libcrypto.a
clean:
- rm -f *~ *.o *.d libcrypto.a
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a
install:
@echo Nothing to be made.
@@ -9,6 +9,7 @@
include ../lib.rules
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
#CFLAGS += -DALL_DH_GROUPS
diff --git a/src/crypto/aes-ccm.c b/src/crypto/aes-ccm.c
index d14670d..cf22778 100644
--- a/src/crypto/aes-ccm.c
+++ b/src/crypto/aes-ccm.c
@@ -203,7 +203,7 @@
aes_encrypt_deinit(aes);
- if (os_memcmp(x, t, M) != 0) {
+ if (os_memcmp_const(x, t, M) != 0) {
wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch");
return -1;
}
diff --git a/src/crypto/aes-gcm.c b/src/crypto/aes-gcm.c
index 3d91c71..84294d2 100644
--- a/src/crypto/aes-gcm.c
+++ b/src/crypto/aes-gcm.c
@@ -310,7 +310,7 @@
aes_encrypt_deinit(aes);
- if (os_memcmp(tag, T, 16) != 0) {
+ if (os_memcmp_const(tag, T, 16) != 0) {
wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch");
return -1;
}
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 9bccaaa..f2d5662 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -271,6 +271,10 @@
*/
struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+ const u8 *e, size_t e_len);
+
/**
* crypto_private_key_import - Import an RSA private key
* @key: Key buffer (DER encoded RSA private key)
@@ -534,16 +538,6 @@
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
diff --git a/src/crypto/crypto_internal-rsa.c b/src/crypto/crypto_internal-rsa.c
index 54209fa..dc7f350 100644
--- a/src/crypto/crypto_internal-rsa.c
+++ b/src/crypto/crypto_internal-rsa.c
@@ -26,6 +26,15 @@
}
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+ const u8 *e, size_t e_len)
+{
+ return (struct crypto_public_key *)
+ crypto_rsa_import_public_key_parts(n, n_len, e, e_len);
+}
+
+
struct crypto_private_key * crypto_private_key_import(const u8 *key,
size_t len,
const char *passwd)
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 5215c00..f02aaac 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -26,6 +26,8 @@
#include "common.h"
#include "wpabuf.h"
#include "dh_group5.h"
+#include "sha1.h"
+#include "sha256.h"
#include "crypto.h"
#if OPENSSL_VERSION_NUMBER < 0x00907000
@@ -338,10 +340,10 @@
ret = 0;
error:
- BN_free(bn_base);
- BN_free(bn_exp);
- BN_free(bn_modulus);
- BN_free(bn_result);
+ BN_clear_free(bn_base);
+ BN_clear_free(bn_exp);
+ BN_clear_free(bn_modulus);
+ BN_clear_free(bn_result);
BN_CTX_free(ctx);
return ret;
}
@@ -569,12 +571,12 @@
if (keylen < 0)
goto err;
wpabuf_put(res, keylen);
- BN_free(pub_key);
+ BN_clear_free(pub_key);
return res;
err:
- BN_free(pub_key);
+ BN_clear_free(pub_key);
wpabuf_free(res);
return NULL;
}
@@ -914,13 +916,6 @@
}
-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)
@@ -1071,7 +1066,7 @@
{
if (e == NULL)
return;
- BN_free(e->order);
+ BN_clear_free(e->order);
EC_GROUP_free(e->group);
BN_CTX_free(e->bnctx);
os_free(e);
@@ -1143,8 +1138,8 @@
ret = 0;
}
- BN_free(x_bn);
- BN_free(y_bn);
+ BN_clear_free(x_bn);
+ BN_clear_free(y_bn);
return ret;
}
@@ -1160,20 +1155,20 @@
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);
+ BN_clear_free(x);
+ BN_clear_free(y);
+ EC_POINT_clear_free(elem);
return NULL;
}
if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y,
e->bnctx)) {
- EC_POINT_free(elem);
+ EC_POINT_clear_free(elem);
elem = NULL;
}
- BN_free(x);
- BN_free(y);
+ BN_clear_free(x);
+ BN_clear_free(y);
return (struct crypto_ec_point *) elem;
}
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 3a675df..58e94c3 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1169,7 +1169,7 @@
#endif /* ALL_DH_GROUPS */
};
-#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0]))
+#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups)
const struct dh_group * dh_groups_get(int id)
diff --git a/src/crypto/fips_prf_cryptoapi.c b/src/crypto/fips_prf_cryptoapi.c
deleted file mode 100644
index dca93a3..0000000
--- a/src/crypto/fips_prf_cryptoapi.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * FIPS 186-2 PRF for Microsoft CryptoAPI
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "crypto.h"
-
-
-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;
-}
diff --git a/src/crypto/fips_prf_gnutls.c b/src/crypto/fips_prf_gnutls.c
deleted file mode 100644
index 947e6f6..0000000
--- a/src/crypto/fips_prf_gnutls.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * FIPS 186-2 PRF for libgcrypt
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <gcrypt.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 libgcrypt? */
- return -1;
-}
diff --git a/src/crypto/fips_prf_nss.c b/src/crypto/fips_prf_nss.c
deleted file mode 100644
index 2c962f4..0000000
--- a/src/crypto/fips_prf_nss.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * FIPS 186-2 PRF for NSS
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <openssl/sha.h>
-
-#include "common.h"
-#include "crypto.h"
-
-
-int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
-{
- return -1;
-}
diff --git a/src/crypto/milenage.c b/src/crypto/milenage.c
index a7f9c6a..6edea57 100644
--- a/src/crypto/milenage.c
+++ b/src/crypto/milenage.c
@@ -217,7 +217,7 @@
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)
+ os_memcmp_const(mac_s, auts + 6, 8) != 0)
return -1;
return 0;
}
@@ -312,7 +312,7 @@
wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8);
- if (os_memcmp(mac_a, autn + 8, 8) != 0) {
+ if (os_memcmp_const(mac_a, autn + 8, 8) != 0) {
wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch");
wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A",
autn + 8, 8);
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index b2bbab2..49a5c1c 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -58,6 +58,7 @@
WPA_PUT_LE16(ucs2_buffer + j,
((c & 0xF) << 12) |
((c2 & 0x3F) << 6) | (c3 & 0x3F));
+ j += 2;
}
}
}
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index 10bf153..24bc3ff 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -19,6 +19,7 @@
void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+#ifdef CONFIG_CRYPTO_INTERNAL
/**
* sha1_vector - SHA-1 hash for data vector
* @num_elem: Number of elements in the data vector
@@ -38,6 +39,7 @@
SHA1Final(mac, &ctx);
return 0;
}
+#endif /* CONFIG_CRYPTO_INTERNAL */
/* ===== start - public domain SHA1 implementation ===== */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 2fdaa02..65e0f79 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -40,7 +40,8 @@
TLS_FAIL_SUBJECT_MISMATCH = 5,
TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
TLS_FAIL_BAD_CERTIFICATE = 7,
- TLS_FAIL_SERVER_CHAIN_PROBE = 8
+ TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9
};
union tls_event_data {
@@ -84,6 +85,8 @@
#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
#define TLS_CONN_REQUEST_OCSP BIT(3)
#define TLS_CONN_REQUIRE_OCSP BIT(4)
+#define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
+#define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
/**
* struct tls_connection_params - Parameters for TLS connection
@@ -96,6 +99,8 @@
* %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
@@ -137,6 +142,7 @@
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;
@@ -531,4 +537,19 @@
void *tls_ctx, struct tls_connection *conn,
tls_session_ticket_cb cb, void *ctx);
+void tls_connection_set_log_cb(struct tls_connection *conn,
+ void (*log_cb)(void *ctx, const char *msg),
+ void *ctx);
+
+#define TLS_BREAK_VERIFY_DATA BIT(0)
+#define TLS_BREAK_SRV_KEY_X_HASH BIT(1)
+#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2)
+#define TLS_DHE_PRIME_511B BIT(3)
+#define TLS_DHE_PRIME_767B BIT(4)
+#define TLS_DHE_PRIME_15 BIT(5)
+#define TLS_DHE_PRIME_58B BIT(6)
+#define TLS_DHE_NON_PRIME BIT(7)
+
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags);
+
#endif /* TLS_H */
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index a5d72f4..cb23eb9 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -125,8 +125,6 @@
}
-extern int wpa_debug_show_keys;
-
void * tls_init(const struct tls_config *conf)
{
struct tls_global *global;
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 91f0690..6563ed2 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -28,6 +28,7 @@
struct tls_connection {
struct tlsv1_client *client;
struct tlsv1_server *server;
+ struct tls_global *global;
};
@@ -85,6 +86,7 @@
conn = os_zalloc(sizeof(*conn));
if (conn == NULL)
return NULL;
+ conn->global = global;
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (!global->server) {
@@ -109,6 +111,28 @@
}
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags)
+{
+ if (conn->server)
+ tlsv1_server_set_test_flags(conn->server, flags);
+}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+ void (*log_cb)(void *ctx, const char *msg),
+ void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ tlsv1_server_set_log_cb(conn->server, log_cb, ctx);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
{
if (conn == NULL)
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index d27af0a..d2d6600 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -48,23 +48,6 @@
#endif
#endif
-#ifdef ANDROID
-#include <openssl/pem.h>
-#include <keystore/keystore_get.h>
-
-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 */
-
#ifdef SSL_set_tlsext_status_type
#ifndef OPENSSL_NO_TLSEXT
#define HAVE_OCSP
@@ -72,6 +55,22 @@
#endif /* OPENSSL_NO_TLSEXT */
#endif /* SSL_set_tlsext_status_type */
+#ifdef ANDROID
+#include <openssl/pem.h>
+#include <keystore/keystore_get.h>
+
+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 {
@@ -93,7 +92,7 @@
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;
+ char *subject_match, *altsubject_match, *suffix_match;
int read_alerts, write_alerts, failed;
tls_session_ticket_cb session_ticket_cb;
@@ -106,6 +105,7 @@
unsigned int ca_cert_verify:1;
unsigned int cert_probe:1;
unsigned int server_cert_only:1;
+ unsigned int invalid_hb_used:1;
u8 srv_cert_hash[32];
@@ -113,6 +113,7 @@
X509 *peer_cert;
X509 *peer_issuer;
+ X509 *peer_issuer_issuer;
};
@@ -785,12 +786,13 @@
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;
+#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
+ context = tls_global;
#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
}
tls_openssl_ref_count++;
@@ -798,11 +800,13 @@
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;
- } else if (context != tls_global) {
- os_free(context);
}
return NULL;
}
@@ -981,14 +985,35 @@
return count;
}
+
+static void tls_msg_cb(int write_p, int version, int content_type,
+ const void *buf, size_t len, SSL *ssl, void *arg)
+{
+ struct tls_connection *conn = arg;
+ const u8 *pos = buf;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d",
+ write_p ? "TX" : "RX", version, content_type);
+ wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
+ if (content_type == 24 && len >= 3 && pos[0] == 1) {
+ size_t payload_len = WPA_GET_BE16(pos + 1);
+ if (payload_len + 3 > len) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Heartbeat attack detected");
+ conn->invalid_hb_used = 1;
+ }
+ }
+}
+
+
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);
+ struct tls_context *context = SSL_CTX_get_app_data(ssl);
+#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
+ struct tls_context *context = tls_global;
#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
conn = os_zalloc(sizeof(*conn));
@@ -1004,16 +1029,13 @@
conn->context = context;
SSL_set_app_data(conn->ssl, conn);
+ SSL_set_msg_callback(conn->ssl, tls_msg_cb);
+ SSL_set_msg_callback_arg(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 */
-#ifdef ANDROID
- options |= SSL_OP_NO_TLSv1_1;
- options |= SSL_OP_NO_TLSv1_2;
- options |= SSL_OP_NO_TICKET;
-#endif /* ANDROID */
SSL_set_options(conn->ssl, options);
conn->ssl_in = BIO_new(BIO_s_mem());
@@ -1049,6 +1071,7 @@
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);
}
@@ -1139,6 +1162,104 @@
}
+#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) {
@@ -1267,10 +1388,13 @@
SSL *ssl;
struct tls_connection *conn;
struct tls_context *context;
- char *match, *altmatch;
+ char *match, *altmatch, *suffix_match;
const char *err_str;
err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+ if (!err_cert)
+ return 0;
+
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,
@@ -1285,10 +1409,13 @@
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;
@@ -1357,6 +1484,14 @@
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);
@@ -1619,7 +1754,8 @@
static int tls_connection_set_subject_match(struct tls_connection *conn,
const char *subject_match,
- const char *altsubject_match)
+ const char *altsubject_match,
+ const char *suffix_match)
{
os_free(conn->subject_match);
conn->subject_match = NULL;
@@ -1637,6 +1773,14 @@
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;
}
@@ -2521,10 +2665,25 @@
out_data = openssl_handshake(conn, in_data, server);
if (out_data == NULL)
return NULL;
+ if (conn->invalid_hb_used) {
+ wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+ wpabuf_free(out_data);
+ return NULL;
+ }
if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
*appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
+ if (conn->invalid_hb_used) {
+ wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+ if (appl_data) {
+ wpabuf_free(*appl_data);
+ *appl_data = NULL;
+ }
+ wpabuf_free(out_data);
+ return NULL;
+ }
+
return out_data;
}
@@ -2626,6 +2785,12 @@
}
wpabuf_put(buf, res);
+ if (conn->invalid_hb_used) {
+ wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+ wpabuf_free(buf);
+ return NULL;
+ }
+
return buf;
}
@@ -2772,7 +2937,6 @@
static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
- extern int wpa_debug_level;
BIO *out;
size_t rlen;
char *txt;
@@ -2804,6 +2968,41 @@
}
+static void debug_print_cert(X509 *cert, const char *title)
+{
+#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;
+
+ X509_print(out, cert);
+ 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: %s\n%s", title, 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;
@@ -2813,6 +3012,8 @@
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) {
@@ -2843,8 +3044,40 @@
return 0;
}
- status = OCSP_basic_verify(basic, NULL, SSL_CTX_get_cert_store(s->ctx),
- 0);
+ store = SSL_CTX_get_cert_store(s->ctx);
+ if (conn->peer_issuer) {
+ debug_print_cert(conn->peer_issuer, "Add OCSP 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");
@@ -2855,8 +3088,15 @@
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
- if (!conn->peer_cert || !conn->peer_issuer) {
- wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate or issue certificate not available for OCSP status check");
+ 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;
@@ -2902,7 +3142,7 @@
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");
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
return 1;
}
@@ -2947,7 +3187,6 @@
{
int ret;
unsigned long err;
- SSL_CTX *ssl_ctx = tls_ctx;
if (conn == NULL)
return -1;
@@ -2967,7 +3206,8 @@
}
if (tls_connection_set_subject_match(conn,
params->subject_match,
- params->altsubject_match))
+ params->altsubject_match,
+ params->suffix_match))
return -1;
if (params->engine && params->ca_cert_id) {
@@ -3017,8 +3257,22 @@
#endif /* SSL_clear_options */
#endif /* SSL_OP_NO_TICKET */
+#ifdef SSL_OP_NO_TLSv1_1
+ if (params->flags & TLS_CONN_DISABLE_TLSv1_1)
+ SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1);
+ else
+ SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1);
+#endif /* SSL_OP_NO_TLSv1_1 */
+#ifdef SSL_OP_NO_TLSv1_2
+ if (params->flags & TLS_CONN_DISABLE_TLSv1_2)
+ SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2);
+ else
+ SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2);
+#endif /* SSL_OP_NO_TLSv1_2 */
+
#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);
diff --git a/src/drivers/Makefile b/src/drivers/Makefile
index 07600e5..5721154 100644
--- a/src/drivers/Makefile
+++ b/src/drivers/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
rm -f build.wpa_supplicant build.hostapd
install:
diff --git a/src/drivers/android_drv.h b/src/drivers/android_drv.h
index 5906527..31d9440 100644
--- a/src/drivers/android_drv.h
+++ b/src/drivers/android_drv.h
@@ -1,12 +1,8 @@
/*
* Android driver interface
*
- * 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.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef ANDROID_DRV_H
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index b8c9829..352c163 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1,6 +1,6 @@
/*
* Driver interface definition
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -37,6 +37,27 @@
#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,
+};
+
+enum reg_type {
+ REGDOM_TYPE_UNKNOWN,
+ REGDOM_TYPE_COUNTRY,
+ REGDOM_TYPE_WORLD,
+ REGDOM_TYPE_CUSTOM_WORLD,
+ REGDOM_TYPE_INTERSECTION,
+};
+
/**
* struct hostapd_channel_data - Channel information
*/
@@ -57,7 +78,7 @@
int flag;
/**
- * max_tx_power - Maximum transmit power in dBm
+ * max_tx_power - Regulatory transmit power limit in dBm
*/
u8 max_tx_power;
@@ -80,6 +101,9 @@
*/
long double interference_factor;
#endif /* CONFIG_ACS */
+
+ /* DFS CAC time in milliseconds */
+ unsigned int dfs_cac_ms;
};
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
@@ -221,7 +245,7 @@
struct wpa_scan_results {
struct wpa_scan_res **res;
size_t num;
- struct os_time fetch_time;
+ struct os_reltime fetch_time;
};
/**
@@ -327,7 +351,30 @@
* 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;
+ unsigned int p2p_probe:1;
+
+ /**
+ * 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.
+ */
+ unsigned int only_new_results:1;
+
+ /**
+ * low_priority - Requests driver to use a lower scan priority
+ *
+ * This is used to request the driver to use a lower scan priority
+ * if it supports such a thing.
+ */
+ unsigned int low_priority:1;
+
+ /*
+ * NOTE: Whenever adding new parameters here, please make sure
+ * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
+ * matching changes.
+ */
};
/**
@@ -376,6 +423,16 @@
const u8 *bssid;
/**
+ * bssid_hint - BSSID of a proposed AP
+ *
+ * This indicates which BSS has been found a suitable candidate for
+ * initial association for drivers that use driver/firmwate-based BSS
+ * selection. Unlike the @bssid parameter, @bssid_hint does not limit
+ * the driver from selecting other BSSes in the ESS.
+ */
+ const u8 *bssid_hint;
+
+ /**
* ssid - The selected SSID
*/
const u8 *ssid;
@@ -393,6 +450,16 @@
int freq;
/**
+ * freq_hint - Frequency of the channel the proposed AP is using
+ *
+ * This provides a channel on which a suitable BSS has been found as a
+ * hint for the driver. Unlike the @freq parameter, @freq_hint does not
+ * limit the driver from selecting other channels for
+ * driver/firmware-based BSS selection.
+ */
+ int freq_hint;
+
+ /**
* bg_scan_period - Background scan period in seconds, 0 to disable
* background scan, or -1 to indicate no change to default driver
* configuration
@@ -400,6 +467,11 @@
int bg_scan_period;
/**
+ * beacon_int - Beacon interval for IBSS or 0 to use driver default
+ */
+ int beacon_int;
+
+ /**
* 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
@@ -430,25 +502,25 @@
unsigned int wpa_proto;
/**
- * pairwise_suite - Selected pairwise cipher suite
+ * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
*
* This is usually ignored if @wpa_ie is used.
*/
- enum wpa_cipher pairwise_suite;
+ unsigned int pairwise_suite;
/**
- * group_suite - Selected group cipher suite
+ * group_suite - Selected group cipher suite (WPA_CIPHER_*)
*
* This is usually ignored if @wpa_ie is used.
*/
- enum wpa_cipher group_suite;
+ unsigned int group_suite;
/**
- * key_mgmt_suite - Selected key management suite
+ * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
*
* This is usually ignored if @wpa_ie is used.
*/
- enum wpa_key_mgmt key_mgmt_suite;
+ unsigned int key_mgmt_suite;
/**
* auth_alg - Allowed authentication algorithms
@@ -618,11 +690,21 @@
HIDDEN_SSID_ZERO_CONTENTS
};
+struct wowlan_triggers {
+ u8 any;
+ u8 disconnect;
+ u8 magic_pkt;
+ u8 gtk_rekey_failure;
+ u8 eap_identity_req;
+ u8 four_way_handshake;
+ u8 rfkill_release;
+};
+
struct wpa_driver_ap_params {
/**
* head - Beacon head from IEEE 802.11 header to IEs before TIM IE
*/
- const u8 *head;
+ u8 *head;
/**
* head_len - Length of the head buffer in octets
@@ -632,7 +714,7 @@
/**
* tail - Beacon tail following TIM IE
*/
- const u8 *tail;
+ u8 *tail;
/**
* tail_len - Length of the tail buffer in octets
@@ -663,7 +745,7 @@
* This is used by drivers that reply to Probe Requests internally in
* AP mode and require the full Probe Response template.
*/
- const u8 *proberesp;
+ u8 *proberesp;
/**
* proberesp_len - Length of the proberesp buffer in octets
@@ -799,6 +881,16 @@
* disable_dgaf - Whether group-addressed frames are disabled
*/
int disable_dgaf;
+
+ /**
+ * osen - Whether OSEN security is enabled
+ */
+ int osen;
+
+ /**
+ * freq - Channel parameters for dynamic bandwidth changes
+ */
+ struct hostapd_freq_params *freq;
};
/**
@@ -821,6 +913,13 @@
#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
+#define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED 0x00001000
unsigned int enc;
#define WPA_DRIVER_AUTH_OPEN 0x00000001
@@ -832,7 +931,8 @@
#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 all DFS operations */
+#define WPA_DRIVER_FLAGS_DFS_OFFLOAD 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
@@ -844,8 +944,8 @@
#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
-/* Driver takes care of P2P management operations */
-#define WPA_DRIVER_FLAGS_P2P_MGMT 0x00000100
+/* Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
+#define WPA_DRIVER_FLAGS_HT_2040_COEX 0x00000100
/* Driver supports concurrent P2P operations */
#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200
/*
@@ -855,7 +955,8 @@
#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 supports station and key removal when stopping an AP */
+#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT 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
@@ -898,6 +999,10 @@
#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;
@@ -945,6 +1050,8 @@
*/
const u8 *extended_capa, *extended_capa_mask;
unsigned int extended_capa_len;
+
+ struct wowlan_triggers wowlan_triggers;
};
@@ -971,11 +1078,17 @@
u16 listen_interval;
const struct ieee80211_ht_capabilities *ht_capabilities;
const struct ieee80211_vht_capabilities *vht_capabilities;
+ int vht_opmode_enabled;
+ u8 vht_opmode;
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 {
@@ -1087,17 +1200,6 @@
#define WPA_STA_MFP BIT(3)
#define WPA_STA_TDLS_PEER BIT(4)
-/**
- * struct p2p_params - P2P parameters for driver-based P2P management
- */
-struct p2p_params {
- const char *dev_name;
- u8 pri_dev_type[8];
-#define DRV_MAX_SEC_DEV_TYPES 5
- u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8];
- size_t num_sec_dev_types;
-};
-
enum tdls_oper {
TDLS_DISCOVERY_REQ,
TDLS_SETUP,
@@ -1152,6 +1254,75 @@
};
/**
+ * 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;
+};
+
+/* TDLS peer capabilities for send_tdls_mgmt() */
+enum tdls_peer_capability {
+ TDLS_PEER_HT = BIT(0),
+ TDLS_PEER_VHT = BIT(1),
+ TDLS_PEER_WMM = BIT(2),
+};
+
+#ifdef CONFIG_MACSEC
+struct macsec_init_params {
+ Boolean always_include_sci;
+ Boolean use_es;
+ Boolean use_scb;
+};
+#endif /* CONFIG_MACSEC */
+
+
+/**
* struct wpa_driver_ops - Driver interface API definition
*
* This structure defines the API that each driver interface needs to implement
@@ -1200,7 +1371,9 @@
* @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, %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
@@ -1565,6 +1738,14 @@
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
*
@@ -1950,12 +2131,13 @@
* (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);
+ const char *bridge, int use_existing);
/**
* if_remove - Remove a virtual interface
@@ -2017,7 +2199,7 @@
* @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,
+ int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted,
u32 session_timeout);
/**
@@ -2081,7 +2263,7 @@
* 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);
+ const char *bridge_ifname, char *ifname_wds);
/**
* send_action - Transmit an Action frame
@@ -2132,7 +2314,7 @@
*
* 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_ACTION events. Optionally, 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.
*
@@ -2314,228 +2496,13 @@
const char * (*get_radio_name)(void *priv);
/**
- * p2p_find - Start P2P Device Discovery
- * @priv: Private driver interface data
- * @timeout: Timeout for find operation in seconds or 0 for no timeout
- * @type: Device Discovery type (enum p2p_discovery_type)
- * Returns: 0 on success, -1 on failure
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_find)(void *priv, unsigned int timeout, int type);
-
- /**
- * p2p_stop_find - Stop P2P Device Discovery
- * @priv: Private driver interface data
- * Returns: 0 on success, -1 on failure
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_stop_find)(void *priv);
-
- /**
- * p2p_listen - Start P2P Listen state for specified duration
- * @priv: Private driver interface data
- * @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.
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_listen)(void *priv, unsigned int timeout);
-
- /**
- * p2p_connect - Start P2P group formation (GO negotiation)
- * @priv: Private driver interface data
- * @peer_addr: MAC address of the peer P2P client
- * @wps_method: enum p2p_wps_method value indicating config method
- * @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 persistent group
- * Returns: 0 on success, -1 on failure
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method,
- int go_intent, const u8 *own_interface_addr,
- unsigned int force_freq, int persistent_group);
-
- /**
- * wps_success_cb - Report successfully completed WPS provisioning
- * @priv: Private driver interface data
- * @peer_addr: Peer address
- * Returns: 0 on success, -1 on failure
- *
- * This function is used to report successfully completed WPS
- * provisioning during group formation in both GO/Registrar and
- * client/Enrollee roles.
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*wps_success_cb)(void *priv, const u8 *peer_addr);
-
- /**
- * p2p_group_formation_failed - Report failed WPS provisioning
- * @priv: Private driver interface data
- * Returns: 0 on success, -1 on failure
- *
- * 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.
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_group_formation_failed)(void *priv);
-
- /**
- * p2p_set_params - Set P2P parameters
- * @priv: Private driver interface data
- * @params: P2P parameters
- * Returns: 0 on success, -1 on failure
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_set_params)(void *priv, const struct p2p_params *params);
-
- /**
- * p2p_prov_disc_req - Send Provision Discovery Request
- * @priv: Private driver interface data
- * @peer_addr: MAC address of the peer P2P client
- * @config_methods: WPS Config Methods value (only one bit set)
- * 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 EVENT_P2P_PROV_DISC_RESPONSE event.
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr,
- u16 config_methods, int join);
-
- /**
- * p2p_sd_request - Schedule a service discovery query
- * @priv: Private driver interface data
- * @dst: Destination peer or %NULL to apply for all peers
- * @tlvs: P2P Service Query TLV(s)
- * Returns: Reference to the query or 0 on failure
- *
- * Response to the query is indicated with the
- * EVENT_P2P_SD_RESPONSE driver event.
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- u64 (*p2p_sd_request)(void *priv, const u8 *dst,
- const struct wpabuf *tlvs);
-
- /**
- * p2p_sd_cancel_request - Cancel a pending service discovery query
- * @priv: Private driver interface data
- * @req: Query reference from p2p_sd_request()
- * Returns: 0 on success, -1 on failure
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_sd_cancel_request)(void *priv, u64 req);
-
- /**
- * p2p_sd_response - Send response to a service discovery query
- * @priv: Private driver interface data
- * @freq: Frequency from EVENT_P2P_SD_REQUEST event
- * @dst: Destination address from EVENT_P2P_SD_REQUEST event
- * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event
- * @resp_tlvs: P2P Service Response TLV(s)
- * Returns: 0 on success, -1 on failure
- *
- * This function is called as a response to the request indicated with
- * the EVENT_P2P_SD_REQUEST driver event.
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_sd_response)(void *priv, int freq, const u8 *dst,
- u8 dialog_token,
- const struct wpabuf *resp_tlvs);
-
- /**
- * p2p_service_update - Indicate a change in local services
- * @priv: Private driver interface data
- * Returns: 0 on success, -1 on failure
- *
- * 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.
- *
- * This function is only used if the driver implements P2P management,
- * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
- * struct wpa_driver_capa.
- */
- int (*p2p_service_update)(void *priv);
-
- /**
- * p2p_reject - Reject peer device (explicitly block connections)
- * @priv: Private driver interface data
- * @addr: MAC address of the peer
- * Returns: 0 on success, -1 on failure
- */
- int (*p2p_reject)(void *priv, const u8 *addr);
-
- /**
- * p2p_invite - Invite a P2P Device into a group
- * @priv: Private driver interface data
- * @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
- * @go_dev_addr: Forced GO Device Address or %NULL if none
- * @persistent_group: Whether this is to reinvoke a persistent group
- * Returns: 0 on success, -1 on failure
- */
- int (*p2p_invite)(void *priv, const u8 *peer, int role,
- const u8 *bssid, const u8 *ssid, size_t ssid_len,
- const u8 *go_dev_addr, int persistent_group);
-
- /**
* 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)
+ * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield)
* @buf: TDLS IEs to add to the message
* @len: Length of buf in octets
* Returns: 0 on success, negative (<0) on failure
@@ -2544,7 +2511,7 @@
* 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,
+ u8 dialog_token, u16 status_code, u32 peer_capab,
const u8 *buf, size_t len);
/**
@@ -2572,10 +2539,26 @@
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);
+
+ /**
+ * set_wowlan - Set wake-on-wireless triggers
+ * @priv: Private driver interface data
+ * @triggers: wowlan triggers
+ */
+ int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers);
+
+ /**
* 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);
/**
@@ -2591,18 +2574,43 @@
* 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
- *
+ * 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
+ int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
+#endif /* ANDROID */
+
+ /**
+ * vendor_cmd - Execute vendor specific command
+ * @priv: Private driver interface data
+ * @vendor_id: Vendor id
+ * @subcmd: Vendor command id
+ * @data: Vendor command parameters (%NULL if no parameters)
+ * @data_len: Data length
+ * @buf: Return buffer (%NULL to ignore reply)
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This function handles vendor specific commands that are passed to
+ * the driver/device. The command is identified by vendor id and
+ * command id. Parameters can be passed as argument to the command
+ * in the data buffer. Reply (if any) will be filled in the supplied
+ * return buffer.
+ *
+ * The exact driver behavior is driver interface and vendor specific. As
+ * an example, this will be converted to a vendor specific cfg80211
+ * command in case of the nl80211 driver interface.
+ */
+ int (*vendor_cmd)(void *priv, unsigned int vendor_id,
+ unsigned int subcmd, const u8 *data, size_t data_len,
+ struct wpabuf *buf);
+
/**
* set_rekey_info - Set rekey information
* @priv: Private driver interface data
@@ -2735,21 +2743,21 @@
* switch_channel - Announce channel switch and migrate the GO to the
* given frequency
* @priv: Private driver interface data
- * @freq: Frequency in MHz
+ * @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, unsigned int freq);
+ 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: Frequency (in MHz) of the channel
+ * @freq: Channel parameters
* Returns: 0 on success, -1 on failure
*/
- int (*start_dfs_cac)(void *priv, int freq);
+ int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
/**
* stop_ap - Removes beacon from AP
@@ -2786,6 +2794,212 @@
* 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);
+
+#ifdef CONFIG_MACSEC
+ int (*macsec_init)(void *priv, struct macsec_init_params *params);
+
+ int (*macsec_deinit)(void *priv);
+
+ /**
+ * enable_protect_frames - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = protect frames enabled
+ * FALSE = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*enable_protect_frames)(void *priv, Boolean enabled);
+
+ /**
+ * set_replay_protect - Set replay protect status and window size
+ * @priv: Private driver interface data
+ * @enabled: TRUE = replay protect enabled
+ * FALSE = replay protect disabled
+ * @window: replay window size, valid only when replay protect enabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_replay_protect)(void *priv, Boolean enabled, u32 window);
+
+ /**
+ * set_current_cipher_suite - Set current cipher suite
+ * @priv: Private driver interface data
+ * @cs: EUI64 identifier
+ * @cs_len: Length of the cs buffer in octets
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_current_cipher_suite)(void *priv, const u8 *cs,
+ size_t cs_len);
+
+ /**
+ * enable_controlled_port - Set controlled port status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = controlled port enabled
+ * FALSE = controlled port disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*enable_controlled_port)(void *priv, Boolean enabled);
+
+ /**
+ * get_receive_lowest_pn - Get receive lowest pn
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * @an: association number
+ * @lowest_pn: lowest accept pn
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an,
+ u32 *lowest_pn);
+
+ /**
+ * get_transmit_next_pn - Get transmit next pn
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * @an: association number
+ * @next_pn: next pn
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an,
+ u32 *next_pn);
+
+ /**
+ * set_transmit_next_pn - Set transmit next pn
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * @an: association number
+ * @next_pn: next pn
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an,
+ u32 next_pn);
+
+ /**
+ * get_available_receive_sc - get available receive channel
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_available_receive_sc)(void *priv, u32 *channel);
+
+ /**
+ * create_receive_sc - create secure channel for receiving
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * @sci_addr: secure channel identifier - address
+ * @sci_port: secure channel identifier - port
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+ * 2 = Strict)
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr,
+ u16 sci_port, unsigned int conf_offset,
+ int validation);
+
+ /**
+ * delete_receive_sc - delete secure connection for receiving
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*delete_receive_sc)(void *priv, u32 channel);
+
+ /**
+ * create_receive_sa - create secure association for receive
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @an: association number
+ * @lowest_pn: the lowest packet number can be received
+ * @sak: the secure association key
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_receive_sa)(void *priv, u32 channel, u8 an,
+ u32 lowest_pn, const u8 *sak);
+
+ /**
+ * enable_receive_sa - enable the SA for receive
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @an: association number
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*enable_receive_sa)(void *priv, u32 channel, u8 an);
+
+ /**
+ * disable_receive_sa - disable SA for receive
+ * @priv: private driver interface data from init()
+ * @channel: secure channel index
+ * @an: association number
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*disable_receive_sa)(void *priv, u32 channel, u8 an);
+
+ /**
+ * get_available_transmit_sc - get available transmit channel
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_available_transmit_sc)(void *priv, u32 *channel);
+
+ /**
+ * create_transmit_sc - create secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @sci_addr: secure channel identifier - address
+ * @sci_port: secure channel identifier - port
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr,
+ u16 sci_port, unsigned int conf_offset);
+
+ /**
+ * delete_transmit_sc - delete secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*delete_transmit_sc)(void *priv, u32 channel);
+
+ /**
+ * create_transmit_sa - create secure association for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel index
+ * @an: association number
+ * @next_pn: the packet number used as next transmit packet
+ * @confidentiality: True if the SA is to provide confidentiality
+ * as well as integrity
+ * @sak: the secure association key
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn,
+ Boolean confidentiality, const u8 *sak);
+
+ /**
+ * enable_transmit_sa - enable SA for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @an: association number
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*enable_transmit_sa)(void *priv, u32 channel, u8 an);
+
+ /**
+ * disable_transmit_sa - disable SA for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @an: association number
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
+#endif /* CONFIG_MACSEC */
};
@@ -2999,15 +3213,6 @@
EVENT_RX_MGMT,
/**
- * EVENT_RX_ACTION - Action frame received
- *
- * This event is used to indicate when an Action frame has been
- * received. Information about the received frame is included in
- * union wpa_event_data::rx_action.
- */
- EVENT_RX_ACTION,
-
- /**
* EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
*
* This event is used to indicate when the driver has started the
@@ -3111,7 +3316,8 @@
* 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.
+ * interfaces. This event can be propagated when channel switching
+ * fails.
*/
EVENT_INTERFACE_UNAVAILABLE,
@@ -3154,38 +3360,6 @@
EVENT_STATION_LOW_ACK,
/**
- * EVENT_P2P_DEV_FOUND - Report a discovered P2P device
- *
- * This event is used only if the driver implements P2P management
- * internally. Event data is stored in
- * union wpa_event_data::p2p_dev_found.
- */
- EVENT_P2P_DEV_FOUND,
-
- /**
- * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request
- *
- * This event is used only if the driver implements P2P management
- * internally. Event data is stored in
- * union wpa_event_data::p2p_go_neg_req_rx.
- */
- EVENT_P2P_GO_NEG_REQ_RX,
-
- /**
- * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation
- *
- * This event is used only if the driver implements P2P management
- * internally. Event data is stored in
- * union wpa_event_data::p2p_go_neg_completed.
- */
- EVENT_P2P_GO_NEG_COMPLETED,
-
- EVENT_P2P_PROV_DISC_REQUEST,
- EVENT_P2P_PROV_DISC_RESPONSE,
- EVENT_P2P_SD_REQUEST,
- EVENT_P2P_SD_RESPONSE,
-
- /**
* EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
*/
EVENT_IBSS_PEER_LOST,
@@ -3272,17 +3446,36 @@
*/
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_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
};
@@ -3650,48 +3843,26 @@
const u8 *frame;
size_t frame_len;
u32 datarate;
- int ssi_signal; /* dBm */
- } rx_mgmt;
-
- /**
- * struct rx_action - Data for EVENT_RX_ACTION events
- */
- struct rx_action {
- /**
- * da - Destination address of the received Action frame
- */
- const u8 *da;
/**
- * sa - Source address of the received Action frame
+ * drv_priv - Pointer to store driver private BSS information
+ *
+ * If not set to NULL, this is used for comparison with
+ * hostapd_data->drv_priv to determine which BSS should process
+ * the frame.
*/
- const u8 *sa;
-
- /**
- * bssid - Address 3 of the received Action frame
- */
- const u8 *bssid;
-
- /**
- * category - Action frame category
- */
- u8 category;
-
- /**
- * data - Action frame body after category field
- */
- const u8 *data;
-
- /**
- * len - Length of data in octets
- */
- size_t len;
+ void *drv_priv;
/**
* freq - Frequency (in MHz) on which the frame was received
*/
int freq;
- } rx_action;
+
+ /**
+ * 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
@@ -3831,66 +4002,6 @@
} low_ack;
/**
- * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND
- */
- struct p2p_dev_found {
- const u8 *addr;
- const u8 *dev_addr;
- const u8 *pri_dev_type;
- const char *dev_name;
- u16 config_methods;
- u8 dev_capab;
- u8 group_capab;
- } p2p_dev_found;
-
- /**
- * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX
- */
- struct p2p_go_neg_req_rx {
- const u8 *src;
- u16 dev_passwd_id;
- } p2p_go_neg_req_rx;
-
- /**
- * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED
- */
- struct p2p_go_neg_completed {
- struct p2p_go_neg_results *res;
- } p2p_go_neg_completed;
-
- struct p2p_prov_disc_req {
- 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;
- } p2p_prov_disc_req;
-
- struct p2p_prov_disc_resp {
- const u8 *peer;
- u16 config_methods;
- } p2p_prov_disc_resp;
-
- struct p2p_sd_req {
- int freq;
- const u8 *sa;
- u8 dialog_token;
- u16 update_indic;
- const u8 *tlvs;
- size_t tlvs_len;
- } p2p_sd_req;
-
- struct p2p_sd_resp {
- const u8 *sa;
- u16 update_indic;
- const u8 *tlvs;
- size_t tlvs_len;
- } p2p_sd_resp;
-
- /**
* struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
*/
struct ibss_peer_lost {
@@ -3935,11 +4046,17 @@
* @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;
/**
@@ -3961,6 +4078,11 @@
*/
struct dfs_event {
int freq;
+ int ht_enabled;
+ int chan_offset;
+ enum chan_width chan_width;
+ int cf1;
+ int cf2;
} dfs_event;
/**
@@ -3973,6 +4095,25 @@
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
+ * @type: Regulatory change type
+ * @alpha2: Country code (or "" if not available)
+ */
+ struct channel_list_changed {
+ enum reg_change_initiator initiator;
+ enum reg_type type;
+ char alpha2[3];
+ } 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;
};
/**
@@ -4031,4 +4172,7 @@
/* 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 */
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index c2f5934..b569a0a 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -260,6 +260,17 @@
case WPA_CIPHER_CCMP:
v = IEEE80211_CIPHER_AES_CCM;
break;
+#ifdef ATH_GCM_SUPPORT
+ case WPA_CIPHER_CCMP_256:
+ v = IEEE80211_CIPHER_AES_CCM_256;
+ break;
+ case WPA_CIPHER_GCMP:
+ v = IEEE80211_CIPHER_AES_GCM;
+ break;
+ case WPA_CIPHER_GCMP_256:
+ v = IEEE80211_CIPHER_AES_GCM_256;
+ break;
+#endif /* ATH_GCM_SUPPORT */
case WPA_CIPHER_TKIP:
v = IEEE80211_CIPHER_TKIP;
break;
@@ -294,6 +305,14 @@
v = 0;
if (params->wpa_pairwise & WPA_CIPHER_CCMP)
v |= 1<<IEEE80211_CIPHER_AES_CCM;
+#ifdef ATH_GCM_SUPPORT
+ if (params->wpa_pairwise & WPA_CIPHER_CCMP_256)
+ v |= 1<<IEEE80211_CIPHER_AES_CCM_256;
+ if (params->wpa_pairwise & WPA_CIPHER_GCMP)
+ v |= 1<<IEEE80211_CIPHER_AES_GCM;
+ if (params->wpa_pairwise & WPA_CIPHER_GCMP_256)
+ v |= 1<<IEEE80211_CIPHER_AES_GCM_256;
+#endif /* ATH_GCM_SUPPORT */
if (params->wpa_pairwise & WPA_CIPHER_TKIP)
v |= 1<<IEEE80211_CIPHER_TKIP;
if (params->wpa_pairwise & WPA_CIPHER_NONE)
@@ -354,19 +373,16 @@
return atheros_set_privacy(drv, 0);
}
if (!params->wpa && !params->ieee802_1x) {
- hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
- HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+ wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
return -1;
}
if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
- hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
- HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+ wpa_printf(MSG_WARNING, "Error configuring WPA state!");
return -1;
}
if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
- hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
- HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+ wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
return -1;
}
@@ -474,10 +490,32 @@
case WPA_ALG_CCMP:
cipher = IEEE80211_CIPHER_AES_CCM;
break;
+#ifdef ATH_GCM_SUPPORT
+ case WPA_ALG_CCMP_256:
+ cipher = IEEE80211_CIPHER_AES_CCM_256;
+ break;
+ case WPA_ALG_GCMP:
+ cipher = IEEE80211_CIPHER_AES_GCM;
+ break;
+ case WPA_ALG_GCMP_256:
+ cipher = IEEE80211_CIPHER_AES_GCM_256;
+ break;
+#endif /* ATH_GCM_SUPPORT */
#ifdef CONFIG_IEEE80211W
case WPA_ALG_IGTK:
cipher = IEEE80211_CIPHER_AES_CMAC;
break;
+#ifdef ATH_GCM_SUPPORT
+ case WPA_ALG_BIP_CMAC_256:
+ cipher = IEEE80211_CIPHER_AES_CMAC_256;
+ break;
+ case WPA_ALG_BIP_GMAC_128:
+ cipher = IEEE80211_CIPHER_AES_GMAC;
+ break;
+ case WPA_ALG_BIP_GMAC_256:
+ cipher = IEEE80211_CIPHER_AES_GMAC_256;
+ break;
+#endif /* ATH_GCM_SUPPORT */
#endif /* CONFIG_IEEE80211W */
default:
printf("%s: unknown/unsupported algorithm %d\n",
@@ -809,16 +847,10 @@
drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
break;
case WLAN_FC_STYPE_ACTION:
- if (&mgmt->u.action.category > buf + len)
- break;
os_memset(&event, 0, sizeof(event));
- event.rx_action.da = mgmt->da;
- event.rx_action.sa = mgmt->sa;
- event.rx_action.bssid = mgmt->bssid;
- event.rx_action.category = mgmt->u.action.category;
- event.rx_action.data = &mgmt->u.action.category;
- event.rx_action.len = buf + len - event.rx_action.data;
- wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &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))
@@ -872,8 +904,59 @@
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)
@@ -906,16 +989,10 @@
switch (stype) {
case WLAN_FC_STYPE_ACTION:
- if (&mgmt->u.action.category > buf + len)
- break;
os_memset(&event, 0, sizeof(event));
- event.rx_action.da = mgmt->da;
- event.rx_action.sa = mgmt->sa;
- event.rx_action.bssid = mgmt->bssid;
- event.rx_action.category = mgmt->u.action.category;
- event.rx_action.data = &mgmt->u.action.category;
- event.rx_action.len = buf + len - event.rx_action.data;
- wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
break;
default:
break;
@@ -1334,7 +1411,7 @@
while (1) {
os_memset(&iwr, 0, sizeof(iwr));
- os_strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
iwr.u.data.pointer = (void *) tbuf;
iwr.u.data.length = sizeof(tbuf);
@@ -1832,6 +1909,25 @@
wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
params->assocresp_ies);
+#if defined(CONFIG_HS20) && defined(IEEE80211_PARAM_OSEN)
+ if (params->osen) {
+ struct wpa_bss_params bss_params;
+
+ os_memset(&bss_params, 0, sizeof(struct wpa_bss_params));
+ bss_params.enabled = 1;
+ bss_params.wpa = 2;
+ bss_params.wpa_pairwise = WPA_CIPHER_CCMP;
+ bss_params.wpa_group = WPA_CIPHER_CCMP;
+ bss_params.ieee802_1x = 1;
+
+ if (atheros_set_privacy(priv, 1) ||
+ set80211param(priv, IEEE80211_PARAM_OSEN, 1))
+ return -1;
+
+ return atheros_set_ieee8021x(priv, &bss_params);
+ }
+#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */
+
return 0;
}
@@ -1963,7 +2059,7 @@
}
-#ifdef CONFIG_WNM
+#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)
{
@@ -2116,7 +2212,7 @@
return -1;
}
}
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
const struct wpa_driver_ops wpa_driver_atheros_ops = {
@@ -2150,7 +2246,8 @@
.add_sta_node = atheros_add_sta_node,
#endif /* CONFIG_IEEE80211R */
.send_action = atheros_send_action,
-#ifdef CONFIG_WNM
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
.wnm_oper = atheros_wnm_oper,
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+ .set_qos_map = atheros_set_qos_map,
};
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 4acb12b..ca64d5c 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -62,36 +62,12 @@
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 enum ieee80211_opmode
-get80211opmode(struct bsd_driver_data *drv)
-{
- struct ifmediareq ifmr;
-
- (void) memset(&ifmr, 0, sizeof(ifmr));
- (void) strncpy(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;
- if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
- return IEEE80211_M_MBSS;
- }
- return IEEE80211_M_STA;
-}
-
-
static int
bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
{
@@ -310,21 +286,15 @@
return 0;
}
-#ifdef HOSTAPD
-static int
-bsd_commit(void *priv)
-{
- return bsd_ctrl_iface(priv, 1);
-}
-#endif /* HOSTAPD */
-
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,
@@ -380,12 +350,14 @@
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) {
@@ -522,28 +494,6 @@
return bsd_ctrl_iface(priv, 1);
}
-#ifdef HOSTAPD
-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);
-}
-#endif /* HOSTAPD */
-
static void
bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
{
@@ -636,7 +586,7 @@
return 0;
}
-static int
+static size_t
rtbuf_len(void)
{
size_t len;
@@ -773,37 +723,26 @@
bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
{
struct bsd_driver_data *drv = ctx;
- char *buf;
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, len;
+ int n;
union wpa_event_data data;
- len = rtbuf_len();
-
- buf = os_malloc(len);
- if (buf == NULL) {
- wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
- return;
- }
-
- n = read(sock, buf, len);
+ 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));
- os_free(buf);
return;
}
- rtm = (struct rt_msghdr *) buf;
+ 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);
- os_free(buf);
return;
}
ifan = (struct if_announcemsghdr *) rtm;
@@ -844,7 +783,6 @@
}
break;
}
- os_free(buf);
}
static void
@@ -861,7 +799,15 @@
drv = os_zalloc(sizeof(struct bsd_driver_data));
if (drv == NULL) {
- printf("Could not allocate memory for bsd driver data\n");
+ 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;
}
@@ -904,6 +850,7 @@
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;
@@ -924,9 +871,37 @@
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
@@ -1104,9 +1079,9 @@
if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
return -1;
- privacy = !(params->pairwise_suite == CIPHER_NONE &&
- params->group_suite == CIPHER_NONE &&
- params->key_mgmt_suite == KEY_MGMT_NONE &&
+ 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);
@@ -1202,7 +1177,6 @@
wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
{
struct bsd_driver_data *drv = sock_ctx;
- char *buf;
struct if_announcemsghdr *ifan;
struct if_msghdr *ifm;
struct rt_msghdr *rtm;
@@ -1210,30 +1184,20 @@
struct ieee80211_michael_event *mic;
struct ieee80211_leave_event *leave;
struct ieee80211_join_event *join;
- int n, len;
+ int n;
- len = rtbuf_len();
-
- buf = os_malloc(len);
- if (buf == NULL) {
- wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
- return;
- }
-
- n = read(sock, buf, len);
+ 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));
- os_free(buf);
return;
}
- rtm = (struct rt_msghdr *) buf;
+ 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);
- os_free(buf);
return;
}
os_memset(&event, 0, sizeof(event));
@@ -1248,7 +1212,6 @@
case IFAN_DEPARTURE:
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
default:
- os_free(buf);
return;
}
wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
@@ -1321,7 +1284,6 @@
}
break;
}
- os_free(buf);
}
static void
@@ -1492,6 +1454,33 @@
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)
{
@@ -1502,6 +1491,15 @@
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
@@ -1556,6 +1554,7 @@
fail:
close(drv->sock);
fail1:
+ os_free(drv->event_buf);
os_free(drv);
return NULL;
#undef GETPARAM
@@ -1581,6 +1580,7 @@
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);
}
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 8d1d22e..3058cd5 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -49,7 +49,6 @@
E2S(TX_STATUS);
E2S(RX_FROM_UNKNOWN);
E2S(RX_MGMT);
- E2S(RX_ACTION);
E2S(REMAIN_ON_CHANNEL);
E2S(CANCEL_REMAIN_ON_CHANNEL);
E2S(MLME_RX);
@@ -65,13 +64,6 @@
E2S(UNPROT_DEAUTH);
E2S(UNPROT_DISASSOC);
E2S(STATION_LOW_ACK);
- E2S(P2P_DEV_FOUND);
- E2S(P2P_GO_NEG_REQ_RX);
- E2S(P2P_GO_NEG_COMPLETED);
- E2S(P2P_PROV_DISC_REQUEST);
- E2S(P2P_PROV_DISC_RESPONSE);
- E2S(P2P_SD_REQUEST);
- E2S(P2P_SD_RESPONSE);
E2S(IBSS_PEER_LOST);
E2S(DRIVER_GTK_REKEY);
E2S(SCHED_SCAN_STOPPED);
@@ -85,6 +77,8 @@
E2S(DFS_CAC_ABORTED);
E2S(DFS_NOP_FINISHED);
E2S(SURVEY);
+ E2S(SCAN_STARTED);
+ E2S(AVOID_FREQUENCIES);
}
return "UNKNOWN";
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
new file mode 100644
index 0000000..cf24799
--- /dev/null
+++ b/src/drivers/driver_macsec_qca.c
@@ -0,0 +1,887 @@
+/*
+ * Wired Ethernet driver interface for QCA MACsec driver
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ * Copyright (c) 2013-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 <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "driver.h"
+
+#include "nss_macsec_secy.h"
+#include "nss_macsec_secy_rx.h"
+#include "nss_macsec_secy_tx.h"
+
+#define MAXSC 16
+
+/* TCI field definition */
+#define TCI_ES 0x40
+#define TCI_SC 0x20
+#define TCI_SCB 0x10
+#define TCI_E 0x08
+#define TCI_C 0x04
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct macsec_qca_data {
+ char ifname[IFNAMSIZ + 1];
+ u32 secy_id;
+ void *ctx;
+
+ int sock; /* raw packet socket for driver access */
+ int pf_sock;
+ int membership, multi, iff_allmulti, iff_up;
+
+ /* shadow */
+ Boolean always_include_sci;
+ Boolean use_es;
+ Boolean use_scb;
+ Boolean protect_frames;
+ Boolean replay_protect;
+ u32 replay_window;
+};
+
+
+static int macsec_qca_multicast_membership(int sock, int ifindex,
+ const u8 *addr, int add)
+{
+#ifdef __linux__
+ struct packet_mreq mreq;
+
+ if (sock < 0)
+ return -1;
+
+ os_memset(&mreq, 0, sizeof(mreq));
+ mreq.mr_ifindex = ifindex;
+ mreq.mr_type = PACKET_MR_MULTICAST;
+ mreq.mr_alen = ETH_ALEN;
+ os_memcpy(mreq.mr_address, addr, ETH_ALEN);
+
+ if (setsockopt(sock, SOL_PACKET,
+ add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ perror("setsockopt");
+ return -1;
+ }
+ return 0;
+#else /* __linux__ */
+ return -1;
+#endif /* __linux__ */
+}
+
+
+static int macsec_qca_get_ssid(void *priv, u8 *ssid)
+{
+ ssid[0] = 0;
+ return 0;
+}
+
+
+static int macsec_qca_get_bssid(void *priv, u8 *bssid)
+{
+ /* Report PAE group address as the "BSSID" for macsec connection. */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static int macsec_qca_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+ capa->flags = WPA_DRIVER_FLAGS_WIRED;
+ return 0;
+}
+
+
+static int macsec_qca_get_ifflags(const char *ifname, int *flags)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ 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 macsec_qca_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 macsec_qca_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 macsec_qca_multi(const char *ifname, const u8 *addr, int add)
+{
+ struct ifreq ifr;
+ int s;
+
+#ifdef __sun__
+ return -1;
+#endif /* __sun__ */
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ 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 __macsec_drv_init(struct macsec_qca_data *drv)
+{
+ int ret = 0;
+ fal_rx_ctl_filt_t rx_ctl_filt;
+ fal_tx_ctl_filt_t tx_ctl_filt;
+
+ wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id);
+
+ /* Enable Secy and Let EAPoL bypass */
+ ret = nss_macsec_secy_en_set(drv->secy_id, TRUE);
+ if (ret)
+ wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL");
+
+ ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id,
+ FAL_SC_SA_MAP_1_4);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nss_macsec_secy_sc_sa_mapping_mode_set: FAIL");
+
+ os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt));
+ rx_ctl_filt.bypass = 1;
+ rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE;
+ rx_ctl_filt.match_mask = 0xffff;
+ rx_ctl_filt.ether_type_da_range = 0x888e;
+ ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt);
+ if (ret)
+ wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL");
+
+ os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt));
+ tx_ctl_filt.bypass = 1;
+ tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE;
+ tx_ctl_filt.match_mask = 0xffff;
+ tx_ctl_filt.ether_type_da_range = 0x888e;
+ ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt);
+ if (ret)
+ wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL");
+}
+
+
+static void __macsec_drv_deinit(struct macsec_qca_data *drv)
+{
+ nss_macsec_secy_en_set(drv->secy_id, FALSE);
+ nss_macsec_secy_rx_sc_del_all(drv->secy_id);
+ nss_macsec_secy_tx_sc_del_all(drv->secy_id);
+}
+
+
+static void * macsec_qca_init(void *ctx, const char *ifname)
+{
+ struct macsec_qca_data *drv;
+ int flags;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->ctx = ctx;
+
+ /* Board specific settings */
+ if (os_memcmp("eth2", drv->ifname, 4) == 0)
+ drv->secy_id = 1;
+ else if (os_memcmp("eth3", drv->ifname, 4) == 0)
+ drv->secy_id = 2;
+ else
+ drv->secy_id = -1;
+
+#ifdef __linux__
+ drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (drv->pf_sock < 0)
+ perror("socket(PF_PACKET)");
+#else /* __linux__ */
+ drv->pf_sock = -1;
+#endif /* __linux__ */
+
+ if (macsec_qca_get_ifflags(ifname, &flags) == 0 &&
+ !(flags & IFF_UP) &&
+ macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) {
+ drv->iff_up = 1;
+ }
+
+ if (macsec_qca_multicast_membership(drv->pf_sock,
+ if_nametoindex(drv->ifname),
+ pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Added multicast membership with packet socket",
+ __func__);
+ drv->membership = 1;
+ } else if (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Added multicast membership with SIOCADDMULTI",
+ __func__);
+ drv->multi = 1;
+ } else if (macsec_qca_get_ifflags(ifname, &flags) < 0) {
+ wpa_printf(MSG_INFO, "%s: Could not get interface flags",
+ __func__);
+ os_free(drv);
+ return NULL;
+ } else if (flags & IFF_ALLMULTI) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Interface is already configured for multicast",
+ __func__);
+ } else if (macsec_qca_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) {
+ wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
+ __func__);
+ os_free(drv);
+ return NULL;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
+ drv->iff_allmulti = 1;
+ }
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ {
+ int status;
+ wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+ __func__);
+ while (macsec_qca_get_ifstatus(ifname, &status) == 0 &&
+ status == 0)
+ sleep(1);
+ }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+ return drv;
+}
+
+
+static void macsec_qca_deinit(void *priv)
+{
+ struct macsec_qca_data *drv = priv;
+ int flags;
+
+ if (drv->membership &&
+ macsec_qca_multicast_membership(drv->pf_sock,
+ if_nametoindex(drv->ifname),
+ pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to remove PAE multicast group (PACKET)",
+ __func__);
+ }
+
+ if (drv->multi &&
+ macsec_qca_multi(drv->ifname, pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
+ __func__);
+ }
+
+ if (drv->iff_allmulti &&
+ (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 ||
+ macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+ __func__);
+ }
+
+ if (drv->iff_up &&
+ macsec_qca_get_ifflags(drv->ifname, &flags) == 0 &&
+ (flags & IFF_UP) &&
+ macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+ __func__);
+ }
+
+ if (drv->pf_sock != -1)
+ close(drv->pf_sock);
+
+ os_free(drv);
+}
+
+
+static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params)
+{
+ struct macsec_qca_data *drv = priv;
+
+ drv->always_include_sci = params->always_include_sci;
+ drv->use_es = params->use_es;
+ drv->use_scb = params->use_scb;
+
+ wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d",
+ __func__, drv->use_es, drv->use_scb,
+ drv->always_include_sci);
+
+ __macsec_drv_init(drv);
+
+ return 0;
+}
+
+
+static int macsec_qca_macsec_deinit(void *priv)
+{
+ struct macsec_qca_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ __macsec_drv_deinit(drv);
+
+ return 0;
+}
+
+
+static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+ drv->protect_frames = enabled;
+
+ return ret;
+}
+
+
+static int macsec_qca_set_replay_protect(void *priv, Boolean enabled,
+ unsigned int window)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u",
+ __func__, enabled, window);
+
+ drv->replay_protect = enabled;
+ drv->replay_window = window;
+
+ return ret;
+}
+
+
+static int macsec_qca_set_current_cipher_suite(void *priv, const u8 *cs,
+ size_t cs_len)
+{
+ u8 default_cs_id[] = CS_ID_GCM_AES_128;
+
+ if (cs_len != CS_ID_LEN ||
+ os_memcmp(cs, default_cs_id, cs_len) != 0) {
+ wpa_hexdump(MSG_ERROR, "macsec: NOT supported CipherSuite",
+ cs, cs_len);
+ return -1;
+ }
+
+ /* Support default Cipher Suite 0080020001000001 (GCM-AES-128) */
+ wpa_printf(MSG_DEBUG, "%s: default support aes-gcm-128", __func__);
+
+ return 0;
+}
+
+
+static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled);
+
+ ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled);
+
+ return ret;
+}
+
+
+static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an,
+ u32 *lowest_pn)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ u32 next_pn = 0;
+ bool enabled = FALSE;
+ u32 win;
+
+ ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an,
+ &next_pn);
+ ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
+ &enabled);
+ ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id,
+ channel, &win);
+
+ if (enabled)
+ *lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
+ else
+ *lowest_pn = next_pn;
+
+ wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn);
+
+ return ret;
+}
+
+
+static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an,
+ u32 *next_pn)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an,
+ next_pn);
+
+ wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn);
+
+ return ret;
+}
+
+
+int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
+ next_pn);
+
+ wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn);
+
+ return ret;
+}
+
+
+static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ u32 sc_ch = 0;
+ bool in_use = FALSE;
+
+ for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+ ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch,
+ &in_use);
+ if (ret)
+ continue;
+
+ if (!in_use) {
+ *channel = sc_ch;
+ wpa_printf(MSG_DEBUG, "%s: channel=%d",
+ __func__, *channel);
+ return 0;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
+
+ return -1;
+}
+
+
+static int macsec_qca_create_receive_sc(void *priv, u32 channel,
+ const u8 *sci_addr, u16 sci_port,
+ unsigned int conf_offset,
+ int validation)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ fal_rx_prc_lut_t entry;
+ fal_rx_sc_validate_frame_e vf;
+ enum validate_frames validate_frames = validation;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+ /* rx prc lut */
+ os_memset(&entry, 0, sizeof(entry));
+
+ os_memcpy(entry.sci, sci_addr, ETH_ALEN);
+ entry.sci[6] = (sci_port >> 8) & 0xf;
+ entry.sci[7] = sci_port & 0xf;
+ entry.sci_mask = 0xf;
+
+ entry.valid = 1;
+ entry.channel = channel;
+ entry.action = FAL_RX_PRC_ACTION_PROCESS;
+ entry.offset = conf_offset;
+
+ /* rx validate frame */
+ if (validate_frames == Strict)
+ vf = FAL_RX_SC_VALIDATE_FRAME_STRICT;
+ else if (validate_frames == Checked)
+ vf = FAL_RX_SC_VALIDATE_FRAME_CHECK;
+ else
+ vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED;
+
+ ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+ ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel);
+ ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel,
+ vf);
+ ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel,
+ drv->replay_protect);
+ ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id,
+ channel,
+ drv->replay_window);
+
+ return ret;
+}
+
+
+static int macsec_qca_delete_receive_sc(void *priv, u32 channel)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ fal_rx_prc_lut_t entry;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+ /* rx prc lut */
+ os_memset(&entry, 0, sizeof(entry));
+
+ ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel);
+ ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+
+ return ret;
+}
+
+
+static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an,
+ u32 lowest_pn, const u8 *sak)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ fal_rx_sak_t rx_sak;
+ int i = 0;
+
+ wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
+ __func__, channel, an, lowest_pn);
+
+ os_memset(&rx_sak, 0, sizeof(rx_sak));
+ for (i = 0; i < 16; i++)
+ rx_sak.sak[i] = sak[15 - i];
+
+ ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an);
+ ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak);
+
+ return ret;
+}
+
+
+static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+ ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE);
+
+ return ret;
+}
+
+
+static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+ ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE);
+
+ return ret;
+}
+
+
+static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ u32 sc_ch = 0;
+ bool in_use = FALSE;
+
+ for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+ ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
+ &in_use);
+ if (ret)
+ continue;
+
+ if (!in_use) {
+ *channel = sc_ch;
+ wpa_printf(MSG_DEBUG, "%s: channel=%d",
+ __func__, *channel);
+ return 0;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: no avaiable channel", __func__);
+
+ return -1;
+}
+
+
+static int macsec_qca_create_transmit_sc(void *priv, u32 channel,
+ const u8 *sci_addr, u16 sci_port,
+ unsigned int conf_offset)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ fal_tx_class_lut_t entry;
+ u8 psci[ETH_ALEN + 2];
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+ /* class lut */
+ os_memset(&entry, 0, sizeof(entry));
+
+ entry.valid = 1;
+ entry.action = FAL_TX_CLASS_ACTION_FORWARD;
+ entry.channel = channel;
+
+ os_memcpy(psci, sci_addr, ETH_ALEN);
+ psci[6] = (sci_port >> 8) & 0xf;
+ psci[7] = sci_port & 0xf;
+
+ ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+ ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
+ ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel,
+ drv->protect_frames);
+ ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
+ channel,
+ conf_offset);
+
+ return ret;
+}
+
+
+static int macsec_qca_delete_transmit_sc(void *priv, u32 channel)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ fal_tx_class_lut_t entry;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+ /* class lut */
+ os_memset(&entry, 0, sizeof(entry));
+
+ ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+ ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel);
+
+ return ret;
+}
+
+
+static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an,
+ u32 next_pn, Boolean confidentiality,
+ const u8 *sak)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ u8 tci = 0;
+ fal_tx_sak_t tx_sak;
+ int i;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
+ __func__, channel, an, next_pn, confidentiality);
+
+ if (drv->always_include_sci)
+ tci |= TCI_SC;
+ else if (drv->use_es)
+ tci |= TCI_ES;
+ else if (drv->use_scb)
+ tci |= TCI_SCB;
+
+ if (confidentiality)
+ tci |= TCI_E | TCI_C;
+
+ os_memset(&tx_sak, 0, sizeof(tx_sak));
+ for (i = 0; i < 16; i++)
+ tx_sak.sak[i] = sak[15 - i];
+
+ ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
+ next_pn);
+ ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak);
+ ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
+ (tci >> 2));
+ ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an);
+
+ return ret;
+}
+
+
+static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+ ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE);
+
+ return ret;
+}
+
+
+static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+ ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE);
+
+ return ret;
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
+ .name = "macsec_qca",
+ .desc = "QCA MACsec Ethernet driver",
+ .get_ssid = macsec_qca_get_ssid,
+ .get_bssid = macsec_qca_get_bssid,
+ .get_capa = macsec_qca_get_capa,
+ .init = macsec_qca_init,
+ .deinit = macsec_qca_deinit,
+
+ .macsec_init = macsec_qca_macsec_init,
+ .macsec_deinit = macsec_qca_macsec_deinit,
+ .enable_protect_frames = macsec_qca_enable_protect_frames,
+ .set_replay_protect = macsec_qca_set_replay_protect,
+ .set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
+ .enable_controlled_port = macsec_qca_enable_controlled_port,
+ .get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
+ .get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
+ .set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
+ .get_available_receive_sc = macsec_qca_get_available_receive_sc,
+ .create_receive_sc = macsec_qca_create_receive_sc,
+ .delete_receive_sc = macsec_qca_delete_receive_sc,
+ .create_receive_sa = macsec_qca_create_receive_sa,
+ .enable_receive_sa = macsec_qca_enable_receive_sa,
+ .disable_receive_sa = macsec_qca_disable_receive_sa,
+ .get_available_transmit_sc = macsec_qca_get_available_transmit_sc,
+ .create_transmit_sc = macsec_qca_create_transmit_sc,
+ .delete_transmit_sc = macsec_qca_delete_transmit_sc,
+ .create_transmit_sa = macsec_qca_create_transmit_sa,
+ .enable_transmit_sa = macsec_qca_enable_transmit_sa,
+ .disable_transmit_sa = macsec_qca_disable_transmit_sa,
+};
diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c
index bb48011..1635c1f 100644
--- a/src/drivers/driver_madwifi.c
+++ b/src/drivers/driver_madwifi.c
@@ -1,5 +1,5 @@
/*
- * WPA Supplicant - driver interaction with MADWIFI 802.11 driver
+ * hostapd - driver interaction with MADWIFI 802.11 driver
* Copyright (c) 2004, Sam Leffler <sam@errno.com>
* Copyright (c) 2004, Video54 Technologies
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
@@ -7,10 +7,9 @@
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
- * While this driver wrapper supports both AP (hostapd) and station
- * (wpa_supplicant) operations, the station side is deprecated and
- * driver_wext.c should be used instead. This driver wrapper should only be
- * used with hostapd for AP mode functionality.
+ * 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"
@@ -182,7 +181,7 @@
#endif /* MADWIFI_NG */
int idx = op - first;
if (first <= op &&
- idx < (int) (sizeof(opnames) / sizeof(opnames[0])) &&
+ idx < (int) ARRAY_SIZE(opnames) &&
opnames[idx])
perror(opnames[idx]);
else
@@ -322,19 +321,16 @@
IEEE80211_AUTH_AUTO);
}
if (!params->wpa && !params->ieee802_1x) {
- hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
- HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+ wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
return -1;
}
if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
- hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
- HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+ wpa_printf(MSG_WARNING, "Error configuring WPA state!");
return -1;
}
if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
- hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
- HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+ wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
return -1;
}
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 4656c1b..4953af6 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -1074,8 +1074,8 @@
/* Try to continue anyway */
}
- if (params->key_mgmt_suite == KEY_MGMT_NONE ||
- params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
+ 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++) {
@@ -1102,12 +1102,12 @@
priv_mode = Ndis802_11PrivFilterAcceptAll;
} else if (params->wpa_ie[0] == WLAN_EID_RSN) {
priv_mode = Ndis802_11PrivFilter8021xWEP;
- if (params->key_mgmt_suite == KEY_MGMT_PSK)
+ 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 == KEY_MGMT_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) {
@@ -1129,35 +1129,35 @@
#endif /* CONFIG_WPS */
} else {
priv_mode = Ndis802_11PrivFilter8021xWEP;
- if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE)
+ if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
auth_mode = Ndis802_11AuthModeWPANone;
- else if (params->key_mgmt_suite == KEY_MGMT_PSK)
+ else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
auth_mode = Ndis802_11AuthModeWPAPSK;
else
auth_mode = Ndis802_11AuthModeWPA;
}
switch (params->pairwise_suite) {
- case CIPHER_CCMP:
+ case WPA_CIPHER_CCMP:
encr = Ndis802_11Encryption3Enabled;
break;
- case CIPHER_TKIP:
+ case WPA_CIPHER_TKIP:
encr = Ndis802_11Encryption2Enabled;
break;
- case CIPHER_WEP40:
- case CIPHER_WEP104:
+ case WPA_CIPHER_WEP40:
+ case WPA_CIPHER_WEP104:
encr = Ndis802_11Encryption1Enabled;
break;
- case CIPHER_NONE:
+ case WPA_CIPHER_NONE:
#ifdef CONFIG_WPS
if (params->wps == WPS_MODE_PRIVACY) {
encr = Ndis802_11Encryption1Enabled;
break;
}
#endif /* CONFIG_WPS */
- if (params->group_suite == CIPHER_CCMP)
+ if (params->group_suite == WPA_CIPHER_CCMP)
encr = Ndis802_11Encryption3Enabled;
- else if (params->group_suite == CIPHER_TKIP)
+ else if (params->group_suite == WPA_CIPHER_TKIP)
encr = Ndis802_11Encryption2Enabled;
else
encr = Ndis802_11EncryptionDisabled;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index a3ff189..8ebf7d9 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1,6 +1,6 @@
/*
* Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
@@ -28,6 +28,8 @@
#include "common.h"
#include "eloop.h"
#include "utils/list.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "l2_packet/l2_packet.h"
@@ -109,6 +111,17 @@
#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;
@@ -140,6 +153,31 @@
}
+#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
@@ -193,10 +231,13 @@
unsigned int added_bridge:1;
unsigned int in_deinit:1;
unsigned int wdev_id_set:1;
+ unsigned int added_if:1;
+ unsigned int static_ap:1;
u8 addr[ETH_ALEN];
int freq;
+ int bandwidth;
int if_dynamic;
void *ctx;
@@ -226,6 +267,11 @@
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;
@@ -254,7 +300,15 @@
unsigned int retry_auth:1;
unsigned int use_monitor:1;
unsigned int ignore_next_local_disconnect:1;
+ unsigned int ignore_next_local_deauth:1;
unsigned int allow_p2p_device:1;
+ unsigned int hostapd:1;
+ unsigned int start_mode_ap:1;
+ unsigned int start_iface_up:1;
+ unsigned int test_use_roc_tx:1;
+ unsigned int ignore_deauth_event:1;
+ unsigned int dfs_vendor_cmd_avail:1;
+ unsigned int have_low_prio_scan:1;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
@@ -264,21 +318,16 @@
struct wpa_driver_scan_filter *filter_ssids;
size_t num_filter_ssids;
- struct i802_bss first_bss;
+ struct i802_bss *first_bss;
int eapol_tx_sock;
-#ifdef HOSTAPD
int eapol_sock; /* socket for EAPOL frames */
int default_if_indices[16];
int *if_indices;
int num_if_indices;
- int last_freq;
- int last_freq_ht;
-#endif /* HOSTAPD */
-
/* From failed authentication command */
int auth_freq;
u8 auth_bssid_[ETH_ALEN];
@@ -300,8 +349,11 @@
void *timeout_ctx);
static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
enum nl80211_iftype nlmode);
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, int freq);
+
static int
-wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
+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);
@@ -320,44 +372,44 @@
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
+#ifdef ANDROID_P2P_STUB
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) {
+ return 0;
+}
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len) {
+ return 0;
+}
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) {
+ return -1;
+}
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp) {
+ return 0;
+}
+#else /* ANDROID_P2P_STUB */
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);
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp);
+#endif /* ANDROID_P2P_STUB */
+#endif /* ANDROID_P2P */
-#endif
-#ifdef HOSTAPD
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);
-#else /* HOSTAPD */
-static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-}
-static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-}
-
-static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
- return 0;
-}
-#endif /* HOSTAPD */
-#ifdef ANDROID
-extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
- size_t buf_len);
-#endif
-
-static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
- struct hostapd_freq_params *freq);
+static int nl80211_set_channel(struct i802_bss *bss,
+ struct hostapd_freq_params *freq, int set_chan);
static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
int ifindex, int disabled);
@@ -365,6 +417,7 @@
static int wpa_driver_nl80211_authenticate_retry(
struct wpa_driver_nl80211_data *drv);
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
static int i802_set_iface_flags(struct i802_bss *bss, int up);
@@ -472,6 +525,11 @@
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";
}
@@ -479,24 +537,45 @@
}
+/* 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);
+ 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);
+ 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);
+ return nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+ nlmode == NL80211_IFTYPE_P2P_GO;
}
@@ -513,6 +592,7 @@
struct wpa_driver_nl80211_data *drv;
struct wpa_scan_results *res;
unsigned int assoc_freq;
+ unsigned int ibss_freq;
u8 assoc_bssid[ETH_ALEN];
};
@@ -575,8 +655,14 @@
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
valid_handler, valid_data);
- while (err > 0)
- nl_recvmsgs(nl_handle, cb);
+ while (err > 0) {
+ int res = nl_recvmsgs(nl_handle, cb);
+ if (res < 0) {
+ wpa_printf(MSG_INFO,
+ "nl80211: %s->nl_recvmsgs failed: %d",
+ __func__, res);
+ }
+ }
out:
nl_cb_put(cb);
nlmsg_free(msg);
@@ -594,10 +680,7 @@
}
-#ifndef ANDROID
-static
-#endif
-int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+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)
@@ -772,7 +855,6 @@
}
-#ifndef HOSTAPD
static int nl80211_get_macaddr(struct i802_bss *bss)
{
struct nl_msg *msg;
@@ -794,7 +876,6 @@
nlmsg_free(msg);
return NL80211_IFTYPE_UNSPECIFIED;
}
-#endif /* HOSTAPD */
static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
@@ -829,10 +910,15 @@
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");
- nl_recvmsgs(handle, w->nl_cb);
+ res = nl_recvmsgs(handle, w->nl_cb);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+ __func__, res);
+ }
}
@@ -916,8 +1002,7 @@
return NULL;
}
- eloop_register_read_sock(nl_socket_get_fd(w->nl_beacons),
- nl80211_recv_beacons, w, w->nl_beacons);
+ nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w);
dl_list_add(&nl80211_wiphys, &w->list);
@@ -964,10 +1049,9 @@
if (!dl_list_empty(&w->bsss))
return;
- eloop_unregister_read_sock(nl_socket_get_fd(w->nl_beacons));
+ nl80211_destroy_eloop_handle(&w->nl_beacons);
nl_cb_put(w->nl_cb);
- nl_destroy_handles(&w->nl_beacons);
dl_list_del(&w->list);
os_free(w);
}
@@ -995,48 +1079,55 @@
}
-static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
- char *buf, size_t len, int del)
+static void wpa_driver_nl80211_event_newlink(
+ struct wpa_driver_nl80211_data *drv, char *ifname)
{
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->first_bss.ifname, event.interface_status.ifname) == 0) {
- if (del) {
- if (drv->if_removed) {
- wpa_printf(MSG_DEBUG, "nl80211: if_removed "
- "already set - ignore event");
- return;
- }
- drv->if_removed = 1;
- } else {
- 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) {
- wpa_printf(MSG_DEBUG, "nl80211: if_removed "
- "already cleared - ignore event");
- return;
- }
- drv->if_removed = 0;
+ 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);
}
@@ -1053,8 +1144,8 @@
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)
+ if (os_strcmp(((char *) attr) + rta_len,
+ drv->first_bss->ifname) == 0)
return 1;
else
break;
@@ -1075,7 +1166,7 @@
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);
+ wpa_driver_nl80211_finish_drv_init(drv, NULL, 0);
return 1;
}
@@ -1103,21 +1194,57 @@
{
struct nl80211_global *global = ctx;
struct wpa_driver_nl80211_data *drv;
- int attrlen, rta_len;
+ 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 event for foreign "
- "ifindex %d", ifi->ifi_index);
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK 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,
+ 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]" : "",
@@ -1126,7 +1253,7 @@
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) {
+ drv->first_bss->ifname) > 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event since interface %s is up", namebuf);
return;
@@ -1140,24 +1267,34 @@
drv->if_disabled = 1;
wpa_supplicant_event(drv->ctx,
EVENT_INTERFACE_DISABLED, NULL);
+
+ /*
+ * Try to get drv again, since it may be removed as
+ * part of the EVENT_INTERFACE_DISABLED handling for
+ * dynamic interfaces
+ */
+ drv = nl80211_find_drv(global, ifi->ifi_index,
+ buf, len);
+ if (!drv)
+ return;
}
}
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) {
+ 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) {
+ } 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);
+ 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);
+ "removed", drv->first_bss->ifname);
} else {
wpa_printf(MSG_DEBUG, "nl80211: Interface up");
drv->if_disabled = 0;
@@ -1174,24 +1311,15 @@
*/
if (drv->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
- !(ifi->ifi_flags & IFF_RUNNING))
+ !(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);
-
- 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_nl80211_event_link(
- drv,
- ((char *) attr) + rta_len,
- attr->rta_len - rta_len, 0);
- } else if (attr->rta_type == IFLA_MASTER)
- brid = nla_get_u32((struct nlattr *) attr);
- attr = RTA_NEXT(attr, attrlen);
}
+ 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);
@@ -1208,32 +1336,40 @@
{
struct nl80211_global *global = ctx;
struct wpa_driver_nl80211_data *drv;
- int attrlen, rta_len;
+ 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 dellink event for "
- "foreign ifindex %d", ifi->ifi_index);
+ 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;
-
- rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
- if (attr->rta_type == IFLA_IFNAME) {
- wpa_driver_nl80211_event_link(
- drv,
- ((char *) attr) + rta_len,
- attr->rta_len - rta_len, 1);
- } else if (attr->rta_type == IFLA_MASTER)
+ 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];
@@ -1294,11 +1430,12 @@
ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
msg = NULL;
if (ret == 0) {
+ unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
+ arg.ibss_freq : arg.assoc_freq;
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;
+ "associated BSS from scan results: %u MHz", freq);
+ if (freq)
+ drv->assoc_freq = freq;
return drv->assoc_freq;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
@@ -1453,37 +1590,98 @@
}
-static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
- struct nlattr *freq, struct nlattr *type)
+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 || !type)
+ if (!freq)
return;
- 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;
+ 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);
- wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data);
+ bss->freq = data.ch_switch.freq;
+
+ wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
}
@@ -1512,19 +1710,21 @@
}
-static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
+static void mlme_event_mgmt(struct i802_bss *bss,
struct nlattr *freq, struct nlattr *sig,
const u8 *frame, size_t len)
{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
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 action frame");
+ wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
return;
}
@@ -1536,23 +1736,19 @@
os_memset(&event, 0, sizeof(event));
if (freq) {
- event.rx_action.freq = nla_get_u32(freq);
- drv->last_mgmt_freq = event.rx_action.freq;
+ event.rx_mgmt.freq = nla_get_u32(freq);
+ rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
}
- if (stype == WLAN_FC_STYPE_ACTION) {
- event.rx_action.da = mgmt->da;
- event.rx_action.sa = mgmt->sa;
- event.rx_action.bssid = mgmt->bssid;
- event.rx_action.category = mgmt->u.action.category;
- event.rx_action.data = &mgmt->u.action.category + 1;
- event.rx_action.len = frame + len - event.rx_action.data;
- wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
- } else {
- 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);
- }
+ wpa_printf(MSG_DEBUG,
+ "nl80211: RX frame sa=" MACSTR
+ " freq=%d ssi_signal=%d stype=%u (%s) len=%u",
+ MAC2STR(mgmt->sa), rx_freq, ssi_signal, stype, fc2str(fc),
+ (unsigned int) len);
+ event.rx_mgmt.frame = frame;
+ event.rx_mgmt.frame_len = len;
+ event.rx_mgmt.ssi_signal = ssi_signal;
+ event.rx_mgmt.drv_priv = bss;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
}
@@ -1653,7 +1849,7 @@
if (type == EVENT_DISASSOC) {
event.disassoc_info.locally_generated =
- !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
+ !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) {
@@ -1662,8 +1858,21 @@
mgmt->u.disassoc.variable;
}
} else {
+ if (drv->ignore_deauth_event) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
+ drv->ignore_deauth_event = 0;
+ return;
+ }
event.deauth_info.locally_generated =
- !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
+ !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+ if (drv->ignore_next_local_deauth) {
+ drv->ignore_next_local_deauth = 0;
+ if (event.deauth_info.locally_generated) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
+ return;
+ }
+ wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
+ }
event.deauth_info.addr = bssid;
event.deauth_info.reason_code = reason_code;
if (frame + len > mgmt->u.deauth.variable) {
@@ -1776,7 +1985,7 @@
nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_FRAME:
- mlme_event_mgmt(drv, freq, sig, nla_data(frame),
+ mlme_event_mgmt(bss, freq, sig, nla_data(frame),
nla_len(frame));
break;
case NL80211_CMD_FRAME_TX_STATUS:
@@ -1836,7 +2045,7 @@
static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
struct nlattr *tb[])
{
- u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
+ unsigned int freq;
if (tb[NL80211_ATTR_MAC] == NULL) {
wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
@@ -1845,14 +2054,17 @@
}
os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
- /* register for any AUTH message */
- nl80211_register_frame(&drv->first_bss, drv->first_bss.nl_mgmt,
- type, NULL, 0);
-
drv->associated = 1;
wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
MAC2STR(drv->bssid));
+ freq = nl80211_get_assoc_freq(drv);
+ if (freq) {
+ wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz",
+ freq);
+ drv->first_bss->freq = freq;
+ }
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
}
@@ -1962,21 +2174,36 @@
&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);
}
@@ -2514,11 +2741,43 @@
return;
os_memset(&data, 0, sizeof(data));
- data.dfs_event.freq = nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]);
- event_type = nla_get_u8(tb[NL80211_ATTR_RADAR_EVENT]);
+ data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
- wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
- data.dfs_event.freq);
+ /* 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:
@@ -2559,10 +2818,182 @@
}
+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 nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+ enum nl80211_reg_initiator init;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+
+ if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]);
+ wpa_printf(MSG_DEBUG, " * initiator=%d", init);
+ switch (init) {
+ 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;
+ }
+
+ if (tb[NL80211_ATTR_REG_TYPE]) {
+ enum nl80211_reg_type type;
+ type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
+ wpa_printf(MSG_DEBUG, " * type=%d", type);
+ switch (type) {
+ case NL80211_REGDOM_TYPE_COUNTRY:
+ data.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
+ break;
+ case NL80211_REGDOM_TYPE_WORLD:
+ data.channel_list_changed.type = REGDOM_TYPE_WORLD;
+ break;
+ case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
+ data.channel_list_changed.type =
+ REGDOM_TYPE_CUSTOM_WORLD;
+ break;
+ case NL80211_REGDOM_TYPE_INTERSECTION:
+ data.channel_list_changed.type =
+ REGDOM_TYPE_INTERSECTION;
+ break;
+ }
+ }
+
+ if (tb[NL80211_ATTR_REG_ALPHA2]) {
+ os_strlcpy(data.channel_list_changed.alpha2,
+ nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
+ sizeof(data.channel_list_changed.alpha2));
+ wpa_printf(MSG_DEBUG, " * alpha2=%s",
+ data.channel_list_changed.alpha2);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
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);
@@ -2570,7 +3001,7 @@
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,
+ wpa_driver_nl80211_set_mode(drv->first_bss,
drv->ap_scan_as_station);
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
}
@@ -2578,17 +3009,32 @@
switch (cmd) {
case NL80211_CMD_TRIGGER_SCAN:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
+ drv->scan_state = SCAN_STARTED;
+ if (drv->scan_for_auth) {
+ /*
+ * Cannot indicate EVENT_SCAN_STARTED here since we skip
+ * EVENT_SCAN_RESULTS in scan_for_auth case and the
+ * upper layer implementation could get confused about
+ * scanning state.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
+ break;
+ }
+ 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);
@@ -2597,10 +3043,12 @@
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.
@@ -2631,8 +3079,13 @@
tb[NL80211_ATTR_RESP_IE]);
break;
case NL80211_CMD_CH_SWITCH_NOTIFY:
- mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ],
- tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ 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],
@@ -2655,14 +3108,14 @@
nl80211_cqm_event(drv, tb);
break;
case NL80211_CMD_REG_CHANGE:
- wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
- wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
- NULL);
+ nl80211_reg_change_event(drv, tb);
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,
- NULL);
+ &data);
break;
case NL80211_CMD_NEW_STATION:
nl80211_new_station_event(drv, tb);
@@ -2694,6 +3147,9 @@
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);
@@ -2716,7 +3172,7 @@
if (tb[NL80211_ATTR_IFINDEX]) {
ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
- for (bss = &drv->first_bss; bss; bss = bss->next)
+ 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;
@@ -2727,7 +3183,7 @@
} 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) {
+ 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;
@@ -2765,7 +3221,7 @@
dl_list_for_each_safe(drv, tmp, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
- for (bss = &drv->first_bss; bss; bss = bss->next) {
+ 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 &&
@@ -2822,10 +3278,15 @@
void *handle)
{
struct nl_cb *cb = eloop_ctx;
+ int res;
wpa_printf(MSG_MSGDUMP, "nl80211: Event message available");
- nl_recvmsgs(handle, cb);
+ res = nl_recvmsgs(handle, cb);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+ __func__, res);
+ }
}
@@ -2865,6 +3326,44 @@
}
+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;
@@ -2917,6 +3416,9 @@
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;
+ unsigned int have_low_prio_scan:1;
};
@@ -3025,10 +3527,12 @@
}
if (combination_has_p2p && combination_has_mgd) {
- info->p2p_concurrent = 1;
- info->num_multichan_concurrent =
+ unsigned int num_channels =
nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
- return 1;
+
+ info->p2p_concurrent = 1;
+ if (info->num_multichan_concurrent < num_channels)
+ info->num_multichan_concurrent = num_channels;
}
return 0;
@@ -3074,6 +3578,71 @@
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;
+ case WLAN_CIPHER_SUITE_NO_GROUP_ADDR:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
+ break;
}
}
}
@@ -3125,6 +3694,12 @@
if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
+
+ if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
+ capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
+
+ if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
+ info->have_low_prio_scan = 1;
}
@@ -3144,6 +3719,35 @@
}
+static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
+ struct nlattr *tb)
+{
+ struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
+
+ if (tb == NULL)
+ return;
+
+ if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
+ tb, NULL))
+ return;
+
+ if (triggers[NL80211_WOWLAN_TRIG_ANY])
+ capa->wowlan_triggers.any = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
+ capa->wowlan_triggers.disconnect = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+ capa->wowlan_triggers.magic_pkt = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
+ capa->wowlan_triggers.gtk_rekey_failure = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
+ capa->wowlan_triggers.eap_identity_req = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
+ capa->wowlan_triggers.four_way_handshake = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
+ capa->wowlan_triggers.rfkill_release = 1;
+}
+
+
static int wiphy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -3156,7 +3760,7 @@
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_WIPHY_NAME])
- os_strncpy(drv->phyname,
+ os_strlcpy(drv->phyname,
nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
sizeof(drv->phyname));
if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
@@ -3178,6 +3782,7 @@
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 "
@@ -3230,6 +3835,49 @@
}
}
+ 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);
+ if (vinfo->subcmd ==
+ QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)
+ drv->dfs_vendor_cmd_avail = 1;
+
+ 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);
+ }
+ }
+
+ wiphy_info_wowlan_triggers(capa,
+ tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
+
+ if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
+ capa->max_stations =
+ nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
+
return NL_SKIP;
}
@@ -3255,7 +3903,7 @@
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)
+ 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))
@@ -3288,6 +3936,9 @@
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);
@@ -3305,15 +3956,10 @@
return -1;
drv->has_capability = 1;
- /* 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.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
@@ -3322,6 +3968,15 @@
drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ /*
+ * As all cfg80211 drivers must support cases where the AP interface is
+ * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
+ * case that the user space daemon has crashed, they must be able to
+ * cleanup all stations and key entries in the AP tear down flow. Thus,
+ * this flag can/should always be set for cfg80211 drivers.
+ */
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
+
if (!info.device_ap_sme) {
drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
@@ -3335,21 +3990,15 @@
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;
+ drv->have_low_prio_scan = info.have_low_prio_scan;
-#ifdef ANDROID_P2P
- if(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
- /* Driver is new enough to support monitorless mode*/
- wpa_printf(MSG_DEBUG, "nl80211: Driver is new "
- "enough to support monitor-less mode");
- drv->use_monitor = 0;
- }
-#else
/*
* 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;
-#endif
if (drv->device_ap_sme && drv->use_monitor) {
/*
@@ -3469,14 +4118,24 @@
/* 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);
- eloop_register_read_sock(nl_socket_get_fd(global->nl_event),
- wpa_driver_nl80211_event_receive,
- global->nl_cb, global->nl_event);
+ nl80211_register_eloop_read(&global->nl_event,
+ wpa_driver_nl80211_event_receive,
+ global->nl_cb);
return 0;
@@ -3520,7 +4179,7 @@
{
struct wpa_driver_nl80211_data *drv = ctx;
wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
- if (i802_set_iface_flags(&drv->first_bss, 1)) {
+ if (i802_set_iface_flags(drv->first_bss, 1)) {
wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
"after rfkill unblock");
return;
@@ -3610,16 +4269,9 @@
}
-/**
- * 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)
+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;
@@ -3632,7 +4284,17 @@
return NULL;
drv->global = global_priv;
drv->ctx = ctx;
- bss = &drv->first_bss;
+ 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;
@@ -3663,7 +4325,10 @@
os_free(rcfg);
}
- if (wpa_driver_nl80211_finish_drv_init(drv))
+ 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);
@@ -3701,6 +4366,21 @@
}
+/**
+ * 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)
@@ -3708,15 +4388,16 @@
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;
- wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p",
- type, nl_handle);
- wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
- match, match_len);
+ buf[0] = '\0';
+ wpa_snprintf_hex(buf, sizeof(buf), match, match_len);
+ wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s",
+ type, fc2str(type), nl_handle, buf);
nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
@@ -3757,14 +4438,18 @@
if (bss->nl_mgmt == NULL)
return -1;
- eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt),
- wpa_driver_nl80211_event_receive, bss->nl_cb,
- bss->nl_mgmt);
-
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)
{
@@ -3777,67 +4462,100 @@
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)
- return -1;
+ ret = -1;
/* GAS Initial Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
- return -1;
+ ret = -1;
/* GAS Comeback Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
- return -1;
+ ret = -1;
/* GAS Comeback Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
- return -1;
+ 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)
- return -1;
+ ret = -1;
/* P2P Action */
if (nl80211_register_action_frame(bss,
(u8 *) "\x7f\x50\x6f\x9a\x09",
5) < 0)
- return -1;
+ ret = -1;
#endif /* CONFIG_P2P */
#ifdef CONFIG_IEEE80211W
/* SA Query Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
- return -1;
+ 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)
- return -1;
+ ret = -1;
}
#endif /* CONFIG_TDLS */
/* FT Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
- return -1;
+ 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)
- return -1;
+ ret = -1;
/* WNM-Sleep Mode Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
- return -1;
+ ret = -1;
- return 0;
+#ifdef CONFIG_HS20
+ /* WNM-Notification */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0)
+ return -1;
+#endif /* CONFIG_HS20 */
+
+ nl80211_mgmt_handle_register_eloop(bss);
+
+ return ret;
}
@@ -3885,7 +4603,7 @@
* it isn't per interface ... maybe just dump the scan
* results periodically for OLBC?
*/
-// WLAN_FC_STYPE_BEACON,
+ /* WLAN_FC_STYPE_BEACON, */
};
unsigned int i;
@@ -3894,7 +4612,7 @@
wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
"handle %p", bss->nl_mgmt);
- for (i = 0; i < sizeof(stypes) / sizeof(stypes[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(stypes); i++) {
if (nl80211_register_frame(bss, bss->nl_mgmt,
(WLAN_FC_TYPE_MGMT << 2) |
(stypes[i] << 4),
@@ -3909,10 +4627,10 @@
if (nl80211_get_wiphy_data_ap(bss) == NULL)
goto out_err;
+ nl80211_mgmt_handle_register_eloop(bss);
return 0;
out_err:
- eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
nl_destroy_handles(&bss->nl_mgmt);
return -1;
}
@@ -3931,10 +4649,10 @@
NULL, 0) < 0)
goto out_err;
+ nl80211_mgmt_handle_register_eloop(bss);
return 0;
out_err:
- eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
nl_destroy_handles(&bss->nl_mgmt);
return -1;
}
@@ -3946,8 +4664,7 @@
return;
wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
"(%s)", bss->nl_mgmt, reason);
- eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
- nl_destroy_handles(&bss->nl_mgmt);
+ nl80211_destroy_eloop_handle(&bss->nl_mgmt);
nl80211_put_wiphy_data_ap(bss);
}
@@ -3977,7 +4694,7 @@
wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
bss->ifname, (long long unsigned int) bss->wdev_id,
- strerror(ret));
+ strerror(-ret));
nla_put_failure:
nlmsg_free(msg);
@@ -4007,7 +4724,7 @@
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));
+ strerror(-ret));
nla_put_failure:
nlmsg_free(msg);
@@ -4031,13 +4748,12 @@
static int
-wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+ const u8 *set_addr, int first)
{
-#ifndef HOSTAPD
- enum nl80211_iftype nlmode = NL80211_IFTYPE_STATION;
-#endif /* HOSTAPD */
- struct i802_bss *bss = &drv->first_bss;
+ 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;
@@ -4048,52 +4764,60 @@
bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set;
drv->global->if_add_wdevid_set = 0;
+ if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
+ bss->static_ap = 1;
+
if (wpa_driver_nl80211_capa(drv))
return -1;
wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
bss->ifname, drv->phyname);
-#ifndef HOSTAPD
- if (bss->if_dynamic)
- nlmode = nl80211_get_ifmode(bss);
+ 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;
- /*
- * Make sure the interface starts up in station mode unless this is a
- * dynamically added interface (e.g., P2P) that was already configured
- * with proper iftype.
- */
+ if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
+ drv->start_mode_ap = 1;
+
+ if (drv->hostapd || bss->static_ap)
+ 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 to use managed mode");
+ wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode");
return -1;
}
- drv->nlmode = nlmode;
- 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");
+ if (nlmode == NL80211_IFTYPE_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 {
+ if (!rfkill_is_blocked(drv->rfkill)) {
+ int ret = i802_set_iface_flags(bss, 1);
+ if (ret) {
wpa_printf(MSG_ERROR, "nl80211: Could not set "
"interface '%s' UP", bss->ifname);
- return -1;
+ return ret;
}
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ return ret;
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+ "interface '%s' due to rfkill", bss->ifname);
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ return 0;
+ drv->if_disabled = 1;
+ send_rfkill_event = 1;
}
- netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
- 1, IF_OPER_DORMANT);
-#endif /* HOSTAPD */
+ 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))
@@ -4116,6 +4840,8 @@
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);
@@ -4164,15 +4890,6 @@
if (is_ap_interface(drv->nlmode))
wpa_driver_nl80211_del_beacon(drv);
-#ifdef HOSTAPD
- if (drv->last_freq_ht) {
- /* Clear HT flags from the driver */
- struct hostapd_freq_params freq;
- os_memset(&freq, 0, sizeof(freq));
- freq.freq = drv->last_freq;
- wpa_driver_nl80211_set_freq(bss, &freq);
- }
-
if (drv->eapol_sock >= 0) {
eloop_unregister_read_sock(drv->eapol_sock);
close(drv->eapol_sock);
@@ -4180,20 +4897,23 @@
if (drv->if_indices != drv->default_if_indices)
os_free(drv->if_indices);
-#endif /* HOSTAPD */
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);
+ eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx);
rfkill_deinit(drv->rfkill);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
- (void) i802_set_iface_flags(bss, 0);
+ if (!drv->start_iface_up)
+ (void) i802_set_iface_flags(bss, 0);
if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
- wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
+ 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");
@@ -4201,7 +4921,7 @@
}
nl_cb_put(drv->nl_cb);
- nl80211_destroy_bss(&drv->first_bss);
+ nl80211_destroy_bss(drv->first_bss);
os_free(drv->filter_ssids);
@@ -4212,6 +4932,7 @@
os_free(drv->extended_capa);
os_free(drv->extended_capa_mask);
+ os_free(drv->first_bss);
os_free(drv);
}
@@ -4228,7 +4949,7 @@
{
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,
+ wpa_driver_nl80211_set_mode(drv->first_bss,
drv->ap_scan_as_station);
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
}
@@ -4243,6 +4964,7 @@
{
struct nl_msg *msg;
size_t i;
+ u32 scan_flags = 0;
msg = nlmsg_alloc();
if (!msg)
@@ -4299,6 +5021,20 @@
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");
+ scan_flags |= NL80211_SCAN_FLAG_FLUSH;
+ }
+
+ if (params->low_priority && drv->have_low_prio_scan) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+ scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+ }
+
+ if (scan_flags)
+ NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags);
+
return msg;
fail:
@@ -4356,8 +5092,9 @@
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
"(%s)", ret, strerror(-ret));
-#ifdef HOSTAPD
- if (is_ap_interface(drv->nlmode)) {
+ if (drv->hostapd && is_ap_interface(drv->nlmode)) {
+ enum nl80211_iftype old_mode = drv->nlmode;
+
/*
* mac80211 does not allow scan requests in AP mode, so
* try to do this in station mode.
@@ -4372,15 +5109,13 @@
}
/* Restore AP mode when processing scan results */
- drv->ap_scan_as_station = drv->nlmode;
+ drv->ap_scan_as_station = old_mode;
ret = 0;
} else
goto nla_put_failure;
-#else /* HOSTAPD */
- goto nla_put_failure;
-#endif /* HOSTAPD */
}
+ drv->scan_state = SCAN_REQUESTED;
/* Not all drivers generate "scan completed" wireless event, so try to
* read results after a timeout. */
timeout = 10;
@@ -4456,10 +5191,20 @@
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);
@@ -4627,6 +5372,13 @@
wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
_arg->assoc_freq);
}
+ if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+ bss[NL80211_BSS_FREQUENCY]) {
+ _arg->ibss_freq =
+ nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+ _arg->ibss_freq);
+ }
if (status == NL80211_BSS_STATUS_ASSOCIATED &&
bss[NL80211_BSS_BSSID]) {
os_memcpy(_arg->assoc_bssid,
@@ -4712,7 +5464,8 @@
* 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.
+ * 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;
@@ -4730,8 +5483,9 @@
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)) {
+ 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
@@ -4832,7 +5586,7 @@
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)
+ if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
goto nla_put_failure;
arg.drv = drv;
@@ -4897,6 +5651,97 @@
}
+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;
+ case WPA_CIPHER_GTK_NOT_USED:
+ return WLAN_CIPHER_SUITE_NO_GROUP_ADDR;
+ }
+
+ 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,
@@ -4934,49 +5779,15 @@
} else {
nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY);
NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
- switch (alg) {
- case WPA_ALG_WEP:
- if (key_len == 5)
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- WLAN_CIPHER_SUITE_WEP40);
- else
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- WLAN_CIPHER_SUITE_WEP104);
- break;
- case WPA_ALG_TKIP:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- WLAN_CIPHER_SUITE_TKIP);
- break;
- case WPA_ALG_CCMP:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- WLAN_CIPHER_SUITE_CCMP);
- break;
- case WPA_ALG_GCMP:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- WLAN_CIPHER_SUITE_GCMP);
- break;
- case WPA_ALG_IGTK:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- WLAN_CIPHER_SUITE_AES_CMAC);
- break;
- case WPA_ALG_SMS4:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- WLAN_CIPHER_SUITE_SMS4);
- break;
- case WPA_ALG_KRK:
- NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
- WLAN_CIPHER_SUITE_KRK);
- break;
- default:
- wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
- "algorithm %d", __func__, alg);
- nlmsg_free(msg);
- return -1;
- }
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ wpa_alg_to_cipher_suite(alg, key_len));
}
- if (seq && seq_len)
+ if (seq && seq_len) {
NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
+ wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len);
+ }
if (addr && !is_broadcast_ether_addr(addr)) {
wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr));
@@ -5077,33 +5888,8 @@
NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx);
- switch (alg) {
- case WPA_ALG_WEP:
- if (key_len == 5)
- NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
- WLAN_CIPHER_SUITE_WEP40);
- else
- NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
- WLAN_CIPHER_SUITE_WEP104);
- break;
- case WPA_ALG_TKIP:
- NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP);
- break;
- case WPA_ALG_CCMP:
- NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP);
- break;
- case WPA_ALG_GCMP:
- NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP);
- break;
- case WPA_ALG_IGTK:
- NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
- WLAN_CIPHER_SUITE_AES_CMAC);
- break;
- default:
- wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
- "algorithm %d", __func__, alg);
- return -1;
- }
+ 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);
@@ -5238,15 +6024,25 @@
const u8 *addr, int reason_code)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret;
+
+ if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+ nl80211_mark_disconnected(drv);
+ return nl80211_leave_ibss(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);
+ ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
+ reason_code, 0);
+ /*
+ * For locally generated deauthenticate, supplicant already generates a
+ * DEAUTH event, so ignore the event from NL80211.
+ */
+ drv->ignore_next_local_deauth = ret == 0;
+ return ret;
}
@@ -5309,6 +6105,7 @@
is_retry = drv->retry_auth;
drv->retry_auth = 0;
+ drv->ignore_deauth_event = 0;
nl80211_mark_disconnected(drv);
os_memset(drv->auth_bssid, 0, ETH_ALEN);
@@ -5410,6 +6207,7 @@
*/
wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
"after forced deauthentication");
+ drv->ignore_deauth_event = 1;
wpa_driver_nl80211_deauthenticate(
bss, params->bssid,
WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -5475,7 +6273,7 @@
struct wpa_driver_nl80211_data *drv)
{
struct wpa_driver_auth_params params;
- struct i802_bss *bss = &drv->first_bss;
+ struct i802_bss *bss = drv->first_bss;
int i;
wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
@@ -5560,22 +6358,17 @@
u8 channel;
chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
chan->flag = 0;
+ chan->dfs_cac_ms = 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_PASSIVE_SCAN])
- chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN;
- if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
- chan->flag |= HOSTAPD_CHAN_NO_IBSS;
+ 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_MAX_TX_POWER] &&
- !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
- chan->max_tx_power = nla_get_u32(
- tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100;
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
enum nl80211_dfs_state state =
nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
@@ -5592,6 +6385,11 @@
break;
}
}
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
+ chan->dfs_cac_ms = nla_get_u32(
+ tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
+ }
}
@@ -5601,8 +6399,7 @@
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_PASSIVE_SCAN] = { .type = NLA_FLAG },
- [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .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 },
@@ -5898,25 +6695,29 @@
}
-static void nl80211_reg_rule_ht40(struct nlattr *tb[],
- struct phy_info_arg *results)
+static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
+ 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;
+ for (m = 0; m < *results->num_modes; m++) {
+ int c;
+ struct hostapd_hw_modes *mode = &results->modes[m];
- 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;
+ 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;
+ }
+ }
+}
- wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz",
- start, end, max_bw);
- if (max_bw < 40)
- return;
+
+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 &
@@ -5954,6 +6755,76 @@
}
+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;
@@ -5980,14 +6851,50 @@
return NL_SKIP;
}
- wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
- (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+ 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, flags = 0;
nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
nla_data(nl_rule), nla_len(nl_rule), reg_policy);
- nl80211_reg_rule_ht40(tb_rule, results);
+ 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;
+ if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
+ flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
+ start, end, max_bw, max_eirp,
+ flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
+ flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
+ flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
+ flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
+ "",
+ flags & NL80211_RRF_DFS ? " (DFS)" : "",
+ flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
+ flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
+ flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
+ 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)
@@ -5997,12 +6904,19 @@
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_ht40_flags(struct wpa_driver_nl80211_data *drv,
- struct phy_info_arg *results)
+static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
+ struct phy_info_arg *results)
{
struct nl_msg *msg;
@@ -6042,10 +6956,11 @@
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 (nl80211_set_iface_id(msg, bss) < 0)
+ goto nla_put_failure;
if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
- nl80211_set_ht40_flags(drv, &result);
+ nl80211_set_regulatory_flags(drv, &result);
return wpa_driver_nl80211_postprocess_modes(result.modes,
num_modes);
}
@@ -6121,16 +7036,48 @@
{
struct wpa_driver_nl80211_data *drv = bss->drv;
u64 cookie;
+ int res;
- if (freq == 0)
+ if (freq == 0 && drv->nlmode == NL80211_IFTYPE_ADHOC) {
+ freq = nl80211_get_assoc_freq(drv);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: send_frame - Use assoc_freq=%u for IBSS",
+ freq);
+ }
+ if (freq == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u",
+ bss->freq);
freq = bss->freq;
+ }
- if (drv->use_monitor)
+ 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);
+ }
- return nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
- &cookie, no_cck, noack, offchanok);
+ 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;
}
@@ -6147,6 +7094,10 @@
mgmt = (struct ieee80211_mgmt *) data;
fc = le_to_host16(mgmt->frame_control);
+ wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da= " MACSTR
+ " noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u fc=0x%x (%s) nlmode=%d",
+ MAC2STR(mgmt->da), noack, freq, no_cck, offchanok, wait_time,
+ fc, fc2str(fc), drv->nlmode);
if ((is_sta_interface(drv->nlmode) ||
drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
@@ -6157,16 +7108,22 @@
* but it works due to the single-threaded nature
* of wpa_supplicant.
*/
- if (freq == 0)
+ 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)
+ 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,
@@ -6189,6 +7146,7 @@
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);
@@ -6305,7 +7263,7 @@
int beacon_set;
int ifindex = if_nametoindex(bss->ifname);
int num_suites;
- u32 suites[10];
+ u32 suites[10], suite;
u32 ver;
beacon_set = bss->beacon_set;
@@ -6400,17 +7358,8 @@
wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
params->pairwise_ciphers);
- num_suites = 0;
- if (params->pairwise_ciphers & WPA_CIPHER_CCMP)
- suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
- if (params->pairwise_ciphers & WPA_CIPHER_GCMP)
- suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
- if (params->pairwise_ciphers & WPA_CIPHER_TKIP)
- suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
- if (params->pairwise_ciphers & WPA_CIPHER_WEP104)
- suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
- if (params->pairwise_ciphers & WPA_CIPHER_WEP40)
- suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
+ 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);
@@ -6418,28 +7367,9 @@
wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
params->group_cipher);
- switch (params->group_cipher) {
- case WPA_CIPHER_CCMP:
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
- WLAN_CIPHER_SUITE_CCMP);
- break;
- case WPA_CIPHER_GCMP:
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
- WLAN_CIPHER_SUITE_GCMP);
- break;
- case WPA_CIPHER_TKIP:
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
- WLAN_CIPHER_SUITE_TKIP);
- break;
- case WPA_CIPHER_WEP104:
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
- WLAN_CIPHER_SUITE_WEP104);
- break;
- case WPA_CIPHER_WEP40:
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
- WLAN_CIPHER_SUITE_WEP40);
- break;
- }
+ 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",
@@ -6478,6 +7408,30 @@
nl80211_set_bss(bss, params->cts_protect, params->preamble,
params->short_slot_time, params->ht_opmode,
params->isolate, params->basic_rates);
+ if (beacon_set && params->freq &&
+ params->freq->bandwidth != bss->bandwidth) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Update BSS %s bandwidth: %d -> %d",
+ bss->ifname, bss->bandwidth,
+ params->freq->bandwidth);
+ ret = nl80211_set_channel(bss, params->freq, 1);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Frequency set failed: %d (%s)",
+ ret, strerror(-ret));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Frequency set succeeded for ht2040 coex");
+ bss->bandwidth = params->freq->bandwidth;
+ }
+ } else if (!beacon_set) {
+ /*
+ * cfg80211 updates the driver on frequence change in AP
+ * mode only at the point when beaconing is started, so
+ * set the initial value here.
+ */
+ bss->bandwidth = params->freq->bandwidth;
+ }
}
return ret;
nla_put_failure:
@@ -6486,24 +7440,9 @@
}
-static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
- struct hostapd_freq_params *freq)
+static int nl80211_put_freq_params(struct nl_msg *msg,
+ 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);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
if (freq->vht_enabled) {
switch (freq->bandwidth) {
@@ -6528,7 +7467,7 @@
NL80211_CHAN_WIDTH_160);
break;
default:
- return -1;
+ return -EINVAL;
}
NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1);
if (freq->center_freq2)
@@ -6550,6 +7489,34 @@
break;
}
}
+ return 0;
+
+nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int nl80211_set_channel(struct i802_bss *bss,
+ struct hostapd_freq_params *freq, int set_chan)
+{
+ 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, set_chan ? NL80211_CMD_SET_CHANNEL :
+ 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;
@@ -6651,6 +7618,12 @@
params->vht_capabilities);
}
+ if (params->vht_opmode_enabled) {
+ wpa_printf(MSG_DEBUG, " * opmode=%u", params->vht_opmode);
+ NLA_PUT_U8(msg, NL80211_ATTR_OPMODE_NOTIF,
+ params->vht_opmode);
+ }
+
wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability);
NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability);
@@ -6661,6 +7634,22 @@
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;
@@ -6730,11 +7719,14 @@
int ifidx)
{
struct nl_msg *msg;
+ struct wpa_driver_nl80211_data *drv2;
wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
/* stop listening for EAPOL on this interface */
- del_ifidx(drv, ifidx);
+ dl_list_for_each(drv2, &drv->global->interfaces,
+ struct wpa_driver_nl80211_data, list)
+ del_ifidx(drv2, ifidx);
msg = nlmsg_alloc();
if (!msg)
@@ -6800,7 +7792,7 @@
return -1;
nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE);
- if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
+ 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);
@@ -6819,6 +7811,12 @@
NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
}
+ /*
+ * Tell cfg80211 that the interface belongs to the socket that created
+ * it, and the interface should be deleted when the socket is closed.
+ */
+ NLA_PUT_FLAG(msg, NL80211_ATTR_IFACE_SOCKET_OWNER);
+
ret = send_and_recv_msgs(drv, msg, handler, arg);
msg = NULL;
if (ret) {
@@ -6839,8 +7837,17 @@
if (ifidx <= 0)
return -1;
- /* start listening for EAPOL on this interface */
- add_ifidx(drv, ifidx);
+ /*
+ * Some virtual interfaces need to process EAPOL packets and events on
+ * the parent interface. This is used mainly with hostapd.
+ */
+ if (drv->hostapd ||
+ iftype == NL80211_IFTYPE_AP_VLAN ||
+ iftype == NL80211_IFTYPE_WDS ||
+ iftype == NL80211_IFTYPE_MONITOR) {
+ /* 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)) {
@@ -6856,7 +7863,7 @@
const char *ifname, enum nl80211_iftype iftype,
const u8 *addr, int wds,
int (*handler)(struct nl_msg *, void *),
- void *arg)
+ void *arg, int use_existing)
{
int ret;
@@ -6865,6 +7872,21 @@
/* 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);
+ if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+ addr) < 0 &&
+ (linux_set_iface_flags(drv->global->ioctl_sock,
+ ifname, 0) < 0 ||
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+ addr) < 0 ||
+ linux_set_iface_flags(drv->global->ioctl_sock,
+ ifname, 1) < 0))
+ return -1;
+ return -ENFILE;
+ }
wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
/* Try to remove the interface that was already there. */
@@ -6966,12 +7988,13 @@
len = recv(sock, buf, sizeof(buf), 0);
if (len < 0) {
- perror("recv");
+ wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
+ strerror(errno));
return;
}
- if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
- printf("received invalid radiotap frame\n");
+ if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
+ wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
return;
}
@@ -6980,7 +8003,8 @@
if (ret == -ENOENT)
break;
if (ret) {
- printf("received invalid radiotap frame (%d)\n", ret);
+ wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
+ ret);
return;
}
switch (iter.this_arg_index) {
@@ -7014,11 +8038,11 @@
return;
if (!injected)
- handle_frame(drv, buf + iter.max_length,
- len - iter.max_length, datarate, ssi_signal);
+ 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);
+ handle_tx_callback(drv->ctx, buf + iter._max_length,
+ len - iter._max_length, !failed);
}
@@ -7130,7 +8154,7 @@
};
static struct sock_fprog msock_filter = {
- .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]),
+ .len = ARRAY_SIZE(msock_filter_insns),
.filter = msock_filter_insns,
};
@@ -7165,7 +8189,8 @@
if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
&msock_filter, sizeof(msock_filter))) {
- perror("SO_ATTACH_FILTER");
+ wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
+ strerror(errno));
return -1;
}
@@ -7176,7 +8201,10 @@
static void nl80211_remove_monitor_interface(
struct wpa_driver_nl80211_data *drv)
{
- drv->monitor_refcount--;
+ 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;
@@ -7202,27 +8230,29 @@
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) {
+ 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);
+ 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);
+ 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, NULL, NULL, 0);
if (drv->monitor_ifidx == -EOPNOTSUPP) {
/*
@@ -7247,7 +8277,8 @@
ll.sll_ifindex = drv->monitor_ifidx;
drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (drv->monitor_sock < 0) {
- perror("socket[PF_PACKET,SOCK_RAW]");
+ wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
+ strerror(errno));
goto error;
}
@@ -7258,7 +8289,8 @@
}
if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
- perror("monitor socket bind");
+ wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
+ strerror(errno));
goto error;
}
@@ -7266,16 +8298,18 @@
optval = 20;
if (setsockopt
(drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
- perror("Failed to set socket priority");
+ 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)) {
- printf("Could not register monitor read socket\n");
+ wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
goto error;
}
+ drv->monitor_refcount++;
return 0;
error:
nl80211_remove_monitor_interface(drv);
@@ -7287,8 +8321,8 @@
{
struct wpa_driver_nl80211_data *drv = bss->drv;
- wpa_printf(MSG_DEBUG, "nl80211: Setup AP - device_ap_sme=%d "
- "use_monitor=%d", drv->device_ap_sme, drv->use_monitor);
+ 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
@@ -7311,16 +8345,6 @@
!drv->device_ap_sme)
return -1;
-#ifdef ANDROID_P2P
- if (drv->device_ap_sme && drv->use_monitor)
- if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
- return -1;
-
- if (drv->use_monitor &&
- nl80211_create_monitor_interface(drv))
- return -1;
-#endif
-
if (drv->device_ap_sme &&
wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
@@ -7336,6 +8360,8 @@
{
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)
@@ -7390,19 +8416,16 @@
u8 *pos;
int res;
int qos = flags & WPA_STA_WMM;
-#ifndef ANDROID_P2P
+
if (drv->device_ap_sme || !drv->use_monitor)
-#else
- if (drv->device_ap_sme && !drv->use_monitor)
-#endif
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) {
- printf("malloc() failed for i802_send_data(len=%lu)\n",
- (unsigned long) len);
+ wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
+ (unsigned long) len);
return -1;
}
@@ -7519,14 +8542,14 @@
nlmode = NL80211_IFTYPE_AP;
old_mode = drv->nlmode;
- if (wpa_driver_nl80211_set_mode(&drv->first_bss, 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 (nl80211_set_channel(drv->first_bss, &freq, 0)) {
if (old_mode != nlmode)
- wpa_driver_nl80211_set_mode(&drv->first_bss, old_mode);
+ wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
nl80211_remove_monitor_interface(drv);
return -1;
}
@@ -7558,6 +8581,12 @@
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;
}
@@ -7572,8 +8601,7 @@
wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
- if (wpa_driver_nl80211_set_mode(&drv->first_bss,
- NL80211_IFTYPE_ADHOC)) {
+ if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, params->freq)) {
wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
"IBSS mode");
return -1;
@@ -7600,6 +8628,12 @@
wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+ if (params->beacon_int > 0) {
+ wpa_printf(MSG_DEBUG, " * beacon_int=%d", params->beacon_int);
+ NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL,
+ params->beacon_int);
+ }
+
ret = nl80211_set_conn_keys(params, msg);
if (ret)
goto nla_put_failure;
@@ -7610,10 +8644,10 @@
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
}
- if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
- params->key_mgmt_suite == KEY_MGMT_PSK ||
- params->key_mgmt_suite == KEY_MGMT_802_1X_SHA256 ||
- params->key_mgmt_suite == KEY_MGMT_PSK_SHA256) {
+ 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);
}
@@ -7651,40 +8685,45 @@
}
-static int wpa_driver_nl80211_try_connect(
- struct wpa_driver_nl80211_data *drv,
- struct wpa_driver_associate_params *params)
+static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params,
+ struct nl_msg *msg)
{
- struct nl_msg *msg;
- enum nl80211_auth_type type;
- int ret = 0;
- 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);
-
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->bssid_hint) {
+ wpa_printf(MSG_DEBUG, " * bssid_hint=" MACSTR,
+ MAC2STR(params->bssid_hint));
+ NLA_PUT(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
+ params->bssid_hint);
+ }
+
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->freq_hint) {
+ wpa_printf(MSG_DEBUG, " * freq_hint=%d", params->freq_hint);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
+ params->freq_hint);
+ }
+
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);
@@ -7695,11 +8734,148 @@
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_GTK_NOT_USED &&
+ !(drv->capa.enc & WPA_DRIVER_CAPA_ENC_GTK_NOT_USED)) {
+ /*
+ * This is likely to work even though many drivers do not
+ * advertise support for operations without GTK.
+ */
+ wpa_printf(MSG_DEBUG, " * skip group cipher configuration for GTK_NOT_USED due to missing driver support advertisement");
+ } else 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 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
+ 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_IEEE8021X_SHA256:
+ mgmt = WLAN_AKM_SUITE_8021X_SHA256;
+ break;
+ case WPA_KEY_MGMT_PSK_SHA256:
+ mgmt = WLAN_AKM_SUITE_PSK_SHA256;
+ break;
+ case WPA_KEY_MGMT_OSEN:
+ mgmt = WLAN_AKM_SUITE_OSEN;
+ break;
+ case WPA_KEY_MGMT_PSK:
+ default:
+ mgmt = WLAN_AKM_SUITE_PSK;
+ break;
+ }
+ wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt);
+ 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);
+ wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz);
+ NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
+ wpa_hexdump(MSG_DEBUG, " * htcaps_mask",
+ params->htcaps_mask, sz);
+ 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);
+ wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz);
+ NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
+ wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask",
+ params->vhtcaps_mask, sz);
+ 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++;
@@ -7728,129 +8904,6 @@
NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
skip_auth_type:
- 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 != CIPHER_NONE) {
- int cipher;
-
- switch (params->pairwise_suite) {
- case CIPHER_SMS4:
- cipher = WLAN_CIPHER_SUITE_SMS4;
- break;
- case CIPHER_WEP40:
- cipher = WLAN_CIPHER_SUITE_WEP40;
- break;
- case CIPHER_WEP104:
- cipher = WLAN_CIPHER_SUITE_WEP104;
- break;
- case CIPHER_CCMP:
- cipher = WLAN_CIPHER_SUITE_CCMP;
- break;
- case CIPHER_GCMP:
- cipher = WLAN_CIPHER_SUITE_GCMP;
- break;
- case CIPHER_TKIP:
- default:
- cipher = WLAN_CIPHER_SUITE_TKIP;
- break;
- }
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
- }
-
- if (params->group_suite != CIPHER_NONE) {
- int cipher;
-
- switch (params->group_suite) {
- case CIPHER_SMS4:
- cipher = WLAN_CIPHER_SUITE_SMS4;
- break;
- case CIPHER_WEP40:
- cipher = WLAN_CIPHER_SUITE_WEP40;
- break;
- case CIPHER_WEP104:
- cipher = WLAN_CIPHER_SUITE_WEP104;
- break;
- case CIPHER_CCMP:
- cipher = WLAN_CIPHER_SUITE_CCMP;
- break;
- case CIPHER_GCMP:
- cipher = WLAN_CIPHER_SUITE_GCMP;
- break;
- case CIPHER_TKIP:
- default:
- cipher = WLAN_CIPHER_SUITE_TKIP;
- break;
- }
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
- }
-
- if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
- params->key_mgmt_suite == KEY_MGMT_PSK ||
- params->key_mgmt_suite == KEY_MGMT_FT_802_1X ||
- params->key_mgmt_suite == KEY_MGMT_FT_PSK ||
- params->key_mgmt_suite == KEY_MGMT_CCKM) {
- int mgmt = WLAN_AKM_SUITE_PSK;
-
- switch (params->key_mgmt_suite) {
- case KEY_MGMT_CCKM:
- mgmt = WLAN_AKM_SUITE_CCKM;
- break;
- case KEY_MGMT_802_1X:
- mgmt = WLAN_AKM_SUITE_8021X;
- break;
- case KEY_MGMT_FT_802_1X:
- mgmt = WLAN_AKM_SUITE_FT_8021X;
- break;
- case KEY_MGMT_FT_PSK:
- mgmt = WLAN_AKM_SUITE_FT_PSK;
- break;
- case KEY_MGMT_PSK:
- default:
- mgmt = WLAN_AKM_SUITE_PSK;
- break;
- }
- NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
- }
-
-#ifdef CONFIG_IEEE80211W
- if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
- NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
-#endif /* CONFIG_IEEE80211W */
-
- 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 */
-
ret = nl80211_set_conn_keys(params, msg);
if (ret)
goto nla_put_failure;
@@ -7900,7 +8953,7 @@
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- int ret = -1;
+ int ret;
struct nl_msg *msg;
if (params->mode == IEEE80211_MODE_AP)
@@ -7928,95 +8981,9 @@
drv->ifindex);
nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE);
- 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->pairwise_suite != CIPHER_NONE) {
- int cipher;
-
- switch (params->pairwise_suite) {
- case CIPHER_WEP40:
- cipher = WLAN_CIPHER_SUITE_WEP40;
- break;
- case CIPHER_WEP104:
- cipher = WLAN_CIPHER_SUITE_WEP104;
- break;
- case CIPHER_CCMP:
- cipher = WLAN_CIPHER_SUITE_CCMP;
- break;
- case CIPHER_GCMP:
- cipher = WLAN_CIPHER_SUITE_GCMP;
- break;
- case CIPHER_TKIP:
- default:
- cipher = WLAN_CIPHER_SUITE_TKIP;
- break;
- }
- wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher);
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
- }
-
- if (params->group_suite != CIPHER_NONE) {
- int cipher;
-
- switch (params->group_suite) {
- case CIPHER_WEP40:
- cipher = WLAN_CIPHER_SUITE_WEP40;
- break;
- case CIPHER_WEP104:
- cipher = WLAN_CIPHER_SUITE_WEP104;
- break;
- case CIPHER_CCMP:
- cipher = WLAN_CIPHER_SUITE_CCMP;
- break;
- case CIPHER_GCMP:
- cipher = WLAN_CIPHER_SUITE_GCMP;
- break;
- case CIPHER_TKIP:
- default:
- cipher = WLAN_CIPHER_SUITE_TKIP;
- break;
- }
- wpa_printf(MSG_DEBUG, " * group=0x%x", cipher);
- NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
- }
-
-#ifdef CONFIG_IEEE80211W
- if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
- NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
-#endif /* CONFIG_IEEE80211W */
-
- NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+ ret = nl80211_connect_common(drv, params, msg);
+ if (ret)
+ goto nla_put_failure;
if (params->prev_bssid) {
wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR,
@@ -8025,33 +8992,6 @@
params->prev_bssid);
}
- 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");
-
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
@@ -8085,7 +9025,7 @@
return -ENOMEM;
nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE);
- if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
+ if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
goto nla_put_failure;
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
@@ -8101,26 +9041,29 @@
}
-static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
- enum nl80211_iftype nlmode)
+static int wpa_driver_nl80211_set_mode_impl(
+ struct i802_bss *bss,
+ enum nl80211_iftype nlmode,
+ struct hostapd_freq_params *desired_freq_params)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1;
int i;
int was_ap = is_ap_interface(drv->nlmode);
int res;
+ int mode_switch_res;
- res = nl80211_set_mode(drv, drv->ifindex, nlmode);
- if (res && nlmode == nl80211_get_ifmode(bss))
- res = 0;
+ mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
+ mode_switch_res = 0;
- if (res == 0) {
+ if (mode_switch_res == 0) {
drv->nlmode = nlmode;
ret = 0;
goto done;
}
- if (res == -ENODEV)
+ if (mode_switch_res == -ENODEV)
return -1;
if (nlmode == drv->nlmode) {
@@ -8140,21 +9083,35 @@
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
+ if (res != 0) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
"interface down");
- os_sleep(0, 100000);
+ os_sleep(0, 100000);
+ continue;
+ }
+
+ /*
+ * Setting the mode will fail for some drivers if the phy is
+ * on a frequency that the mode is disallowed in.
+ */
+ if (desired_freq_params) {
+ res = i802_set_freq(bss, desired_freq_params);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to set frequency on interface");
+ }
+ }
+
+ /* Try to set the mode again while the interface is down */
+ mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (mode_switch_res == -EBUSY) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Delaying mode set while interface going down");
+ os_sleep(0, 100000);
+ continue;
+ }
+ ret = mode_switch_res;
+ break;
}
if (!ret) {
@@ -8164,6 +9121,14 @@
drv->ignore_if_down_event = 1;
}
+ /* Bring the interface back up */
+ res = linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to set interface up after switching mode");
+ ret = -1;
+ }
+
done:
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
@@ -8197,11 +9162,61 @@
}
+static int dfs_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ int *dfs_capability_ptr = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_VENDOR_DATA]) {
+ struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+ nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
+ u32 val;
+ val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
+ wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
+ val);
+ *dfs_capability_ptr = val;
+ }
+ }
+
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+ enum nl80211_iftype nlmode)
+{
+ return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, int freq)
+{
+ struct hostapd_freq_params freq_params;
+ os_memset(&freq_params, 0, sizeof(freq_params));
+ freq_params.freq = freq;
+ return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
+ &freq_params);
+}
+
+
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;
+ struct nl_msg *msg;
+ int dfs_capability = 0;
+ int ret = 0;
+
if (!drv->has_capability)
return -1;
os_memcpy(capa, &drv->capa, sizeof(*capa));
@@ -8217,7 +9232,31 @@
capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
}
- return 0;
+ if (drv->dfs_vendor_cmd_avail == 1) {
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA);
+ NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY);
+
+ ret = send_and_recv_msgs(drv, msg, dfs_info_handler,
+ &dfs_capability);
+ if (!ret) {
+ if (dfs_capability)
+ capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+ }
+ }
+
+ return ret;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
}
@@ -8226,8 +9265,9 @@
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
- __func__, drv->operstate, state, state ? "UP" : "DORMANT");
+ 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);
@@ -8240,6 +9280,12 @@
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));
@@ -8260,10 +9306,15 @@
upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (!ret)
+ return 0;
nla_put_failure:
nlmsg_free(msg);
- return -ENOBUFS;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
+ ret, strerror(-ret));
+ return ret;
}
@@ -8271,12 +9322,10 @@
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);
+ return nl80211_set_channel(bss, freq, 0);
}
-#if defined(HOSTAPD) || defined(CONFIG_AP)
-
static inline int min_int(int a, int b)
{
if (a < b)
@@ -8431,8 +9480,6 @@
return -ENOBUFS;
}
-#endif /* HOSTAPD || CONFIG_AP */
-
static int get_sta_handler(struct nl_msg *msg, void *arg)
{
@@ -8513,8 +9560,6 @@
}
-#if defined(HOSTAPD) || defined(CONFIG_AP)
-
static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
int cw_min, int cw_max, int burst_time)
{
@@ -8585,6 +9630,10 @@
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,
@@ -8676,9 +9725,29 @@
0);
}
-#endif /* HOSTAPD || CONFIG_AP */
-#ifdef HOSTAPD
+static void dump_ifidx(struct wpa_driver_nl80211_data *drv)
+{
+ char buf[200], *pos, *end;
+ int i, res;
+
+ pos = buf;
+ end = pos + sizeof(buf);
+
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (!drv->if_indices[i])
+ continue;
+ res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]);
+ if (res < 0 || res >= end - pos)
+ break;
+ pos += res;
+ }
+ *pos = '\0';
+
+ wpa_printf(MSG_DEBUG, "nl80211: if_indices[%d]:%s",
+ drv->num_if_indices, buf);
+}
+
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
{
@@ -8687,9 +9756,15 @@
wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
ifidx);
+ if (have_ifidx(drv, ifidx)) {
+ wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
+ ifidx);
+ return;
+ }
for (i = 0; i < drv->num_if_indices; i++) {
if (drv->if_indices[i] == 0) {
drv->if_indices[i] = ifidx;
+ dump_ifidx(drv);
return;
}
}
@@ -8715,6 +9790,7 @@
sizeof(drv->default_if_indices));
drv->if_indices[drv->num_if_indices] = ifidx;
drv->num_if_indices++;
+ dump_ifidx(drv);
}
@@ -8728,6 +9804,7 @@
break;
}
}
+ dump_ifidx(drv);
}
@@ -8744,7 +9821,7 @@
static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
- const char *bridge_ifname, char *ifname_wds)
+ const char *bridge_ifname, char *ifname_wds)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -8760,7 +9837,8 @@
if (!if_nametoindex(name)) {
if (nl80211_create_iface(drv, name,
NL80211_IFTYPE_AP_VLAN,
- bss->addr, 1, NULL, NULL) < 0)
+ bss->addr, 1, NULL, NULL, 0) <
+ 0)
return -1;
if (bridge_ifname &&
linux_br_add_if(drv->global->ioctl_sock,
@@ -8778,8 +9856,8 @@
name);
i802_set_sta_vlan(priv, addr, bss->ifname, 0);
- return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
- name);
+ nl80211_remove_iface(drv, if_nametoindex(name));
+ return 0;
}
}
@@ -8795,7 +9873,8 @@
len = recvfrom(sock, buf, sizeof(buf), 0,
(struct sockaddr *)&lladdr, &fromlen);
if (len < 0) {
- perror("recv");
+ wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s",
+ strerror(errno));
return;
}
@@ -8868,14 +9947,13 @@
int ifindex, br_ifindex;
int br_added = 0;
- bss = wpa_driver_nl80211_init(hapd, params->ifname,
- params->global_priv);
+ bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+ params->global_priv, 1,
+ params->bssid);
if (bss == NULL)
return NULL;
drv = bss->drv;
- drv->nlmode = NL80211_IFTYPE_AP;
- drv->eapol_sock = -1;
if (linux_br_get(brname, params->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
@@ -8886,8 +9964,6 @@
br_ifindex = 0;
}
- drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
- drv->if_indices = drv->default_if_indices;
for (i = 0; i < params->num_bridge; i++) {
if (params->bridge[i]) {
ifindex = if_nametoindex(params->bridge[i]);
@@ -8904,37 +9980,20 @@
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex);
- if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0))
- goto failed;
-
- if (params->bssid) {
- if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
- params->bssid))
- goto failed;
- }
-
- if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
- "into AP mode", bss->ifname);
- goto failed;
- }
-
if (params->num_bridge && params->bridge[0] &&
i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
goto failed;
- if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1))
- goto failed;
-
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
if (drv->eapol_sock < 0) {
- perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
+ 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))
{
- printf("Could not register read socket for eapol\n");
+ wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol");
goto failed;
}
@@ -8958,8 +10017,6 @@
wpa_driver_nl80211_deinit(bss);
}
-#endif /* HOSTAPD */
-
static enum nl80211_iftype wpa_driver_nl80211_if_type(
enum wpa_driver_if_type type)
@@ -8990,7 +10047,7 @@
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)
+ if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0)
return 1;
}
return 0;
@@ -9005,9 +10062,9 @@
if (!drv->global)
return -1;
- os_memcpy(new_addr, drv->first_bss.addr, ETH_ALEN);
+ 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] = drv->first_bss->addr[0] | 0x02;
new_addr[0] ^= idx << 2;
if (!nl80211_addr_in_use(drv->global, new_addr))
break;
@@ -9055,21 +10112,13 @@
const char *ifname, const u8 *addr,
void *bss_ctx, void **drv_priv,
char *force_ifname, u8 *if_addr,
- const char *bridge)
+ 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;
-#ifdef HOSTAPD
- struct i802_bss *new_bss = NULL;
-
- if (type == WPA_IF_AP_BSS) {
- new_bss = os_zalloc(sizeof(*new_bss));
- if (new_bss == NULL)
- return -1;
- }
-#endif /* HOSTAPD */
+ int added = 1;
if (addr)
os_memcpy(if_addr, addr, ETH_ALEN);
@@ -9080,7 +10129,7 @@
os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
0, nl80211_wdev_handler,
- &p2pdev_info);
+ &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);
@@ -9096,11 +10145,11 @@
(long long unsigned int) p2pdev_info.wdev_id);
} else {
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
- 0, NULL, NULL);
- if (ifidx < 0) {
-#ifdef HOSTAPD
- os_free(new_bss);
-#endif /* HOSTAPD */
+ 0, NULL, NULL, use_existing);
+ if (use_existing && ifidx == -ENFILE) {
+ added = 0;
+ ifidx = if_nametoindex(ifname);
+ } else if (ifidx < 0) {
return -1;
}
}
@@ -9110,7 +10159,8 @@
os_memcpy(if_addr, bss->addr, ETH_ALEN);
else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
bss->ifname, if_addr) < 0) {
- nl80211_remove_iface(drv, ifidx);
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
return -1;
}
}
@@ -9124,19 +10174,22 @@
if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
new_addr) < 0) {
- nl80211_remove_iface(drv, ifidx);
+ if (added)
+ 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);
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
return -1;
}
if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
new_addr) < 0) {
- nl80211_remove_iface(drv, ifidx);
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
return -1;
}
}
@@ -9144,20 +10197,29 @@
}
#endif /* CONFIG_P2P */
-#ifdef HOSTAPD
- 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);
- nl80211_remove_iface(drv, ifidx);
- os_free(new_bss);
- return -1;
- }
-
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);
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
os_free(new_bss);
return -1;
}
@@ -9165,10 +10227,11 @@
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->next = drv->first_bss->next;
+ new_bss->freq = drv->first_bss->freq;
new_bss->ctx = bss_ctx;
- drv->first_bss.next = new_bss;
+ new_bss->added_if = added;
+ drv->first_bss->next = new_bss;
if (drv_priv)
*drv_priv = new_bss;
nl80211_init_bss(new_bss);
@@ -9177,11 +10240,21 @@
if (nl80211_setup_ap(new_bss))
return -1;
}
-#endif /* HOSTAPD */
if (drv->global)
drv->global->if_add_ifindex = ifidx;
+ /*
+ * Some virtual interfaces need to process EAPOL packets and events on
+ * the parent interface. This is used mainly with hostapd.
+ */
+ if (ifidx > 0 &&
+ (drv->hostapd ||
+ nlmode == NL80211_IFTYPE_AP_VLAN ||
+ nlmode == NL80211_IFTYPE_WDS ||
+ nlmode == NL80211_IFTYPE_MONITOR))
+ add_ifidx(drv, ifidx);
+
return 0;
}
@@ -9193,14 +10266,17 @@
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",
- __func__, type, ifname, ifindex);
- if (ifindex <= 0)
- return -1;
+ 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);
+ else if (ifindex > 0 && !bss->added_if) {
+ struct wpa_driver_nl80211_data *drv2;
+ dl_list_for_each(drv2, &drv->global->interfaces,
+ struct wpa_driver_nl80211_data, list)
+ del_ifidx(drv2, ifindex);
+ }
- nl80211_remove_iface(drv, ifindex);
-
-#ifdef HOSTAPD
if (type != WPA_IF_AP_BSS)
return 0;
@@ -9218,15 +10294,18 @@
bss->brname, strerror(errno));
}
- if (bss != &drv->first_bss) {
+ if (bss != drv->first_bss) {
struct i802_bss *tbss;
- for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
+ 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);
+ if (!bss->added_if)
+ i802_set_iface_flags(bss, 0);
os_free(bss);
bss = NULL;
break;
@@ -9235,8 +10314,22 @@
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");
+ }
}
-#endif /* HOSTAPD */
return 0;
}
@@ -9282,7 +10375,8 @@
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))
+ if (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+ drv->test_use_roc_tx))
NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
if (no_cck)
NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
@@ -9341,7 +10435,10 @@
os_memcpy(hdr->addr2, src, ETH_ALEN);
os_memcpy(hdr->addr3, bssid, ETH_ALEN);
- if (is_ap_interface(drv->nlmode))
+ 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);
@@ -9473,7 +10570,8 @@
if (!report) {
if (bss->nl_preq && drv->device_ap_sme &&
- is_ap_interface(drv->nlmode)) {
+ is_ap_interface(drv->nlmode) && !bss->in_deinit &&
+ !bss->static_ap) {
/*
* Do not disable Probe Request reporting that was
* enabled in nl80211_setup_ap().
@@ -9484,9 +10582,7 @@
} else if (bss->nl_preq) {
wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
"reporting nl_preq=%p", bss->nl_preq);
- eloop_unregister_read_sock(
- nl_socket_get_fd(bss->nl_preq));
- nl_destroy_handles(&bss->nl_preq);
+ nl80211_destroy_eloop_handle(&bss->nl_preq);
}
return 0;
}
@@ -9509,9 +10605,9 @@
NULL, 0) < 0)
goto out_err;
- eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq),
- wpa_driver_nl80211_event_receive, bss->nl_cb,
- bss->nl_preq);
+ nl80211_register_eloop_read(&bss->nl_preq,
+ wpa_driver_nl80211_event_receive,
+ bss->nl_cb);
return 0;
@@ -9712,27 +10808,6 @@
}
-/* 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 get_channel_width(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -9818,19 +10893,15 @@
struct wpa_driver_nl80211_data, list) {
if (drv == driver ||
os_strcmp(drv->phyname, driver->phyname) != 0 ||
-#ifdef ANDROID_P2P
- (!driver->associated && !is_ap_interface(driver->nlmode)))
-#else
!driver->associated)
-#endif
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));
+ driver->phyname, driver->first_bss->ifname,
+ MAC2STR(driver->first_bss->addr));
if (is_ap_interface(driver->nlmode))
- freq = driver->first_bss.freq;
+ freq = driver->first_bss->freq;
else
freq = nl80211_get_assoc_freq(driver);
wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
@@ -9876,17 +10947,26 @@
struct wpa_driver_nl80211_data *drv = bss->drv;
drv->allow_p2p_device = 1;
}
+#endif /* CONFIG_P2P */
-#ifdef ANDROID_P2P
- if(os_strstr(param, "use_multi_chan_concurrent=1")) {
+ if (os_strstr(param, "use_monitor=1")) {
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- wpa_printf(MSG_DEBUG, "nl80211: Use Multi channel "
- "concurrency");
- drv->capa.num_multichan_concurrent = 2;
+ drv->use_monitor = 1;
}
-#endif
-#endif /* CONFIG_P2P */
+
+ 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;
+ }
+
+ if (os_strstr(param, "no_offchannel_tx=1")) {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+ drv->test_use_roc_tx = 1;
+ }
return 0;
}
@@ -9922,7 +11002,8 @@
global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (global->ioctl_sock < 0) {
- perror("socket(PF_INET,SOCK_DGRAM)");
+ wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
+ strerror(errno));
goto err;
}
@@ -9950,11 +11031,8 @@
nl_destroy_handles(&global->nl);
- if (global->nl_event) {
- eloop_unregister_read_sock(
- nl_socket_get_fd(global->nl_event));
- nl_destroy_handles(&global->nl_event);
- }
+ if (global->nl_event)
+ nl80211_destroy_eloop_handle(&global->nl_event);
nl_cb_put(global->nl_cb);
@@ -10122,6 +11200,9 @@
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])
@@ -10328,12 +11409,13 @@
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)
+ if (opp_ps != -1 || ctwindow != -1) {
#ifdef ANDROID_P2P
wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow);
-#else
+#else /* ANDROID_P2P */
return -1; /* Not yet supported */
-#endif
+#endif /* ANDROID_P2P */
+ }
if (legacy_ps == -1)
return 0;
@@ -10344,14 +11426,18 @@
}
-static int nl80211_start_radar_detection(void *priv, int freq)
+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)");
+ 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");
@@ -10364,10 +11450,53 @@
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);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
- /* only HT20 is supported at this point */
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20);
+ 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)
@@ -10382,7 +11511,7 @@
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)
+ u32 peer_capab, const u8 *buf, size_t len)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -10404,6 +11533,15 @@
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);
+ if (peer_capab) {
+ /*
+ * The internal enum tdls_peer_capability definition is
+ * currently identical with the nl80211 enum
+ * nl80211_tdls_peer_capability, so no conversion is needed
+ * here.
+ */
+ NLA_PUT_U32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY, peer_capab);
+ }
NLA_PUT(msg, NL80211_ATTR_IE, len, buf);
return send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -10570,7 +11708,7 @@
memset(&ifr, 0, sizeof(ifr));
memset(&priv_cmd, 0, sizeof(priv_cmd));
- os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+ os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
priv_cmd.buf = buf;
priv_cmd.used_len = bp;
@@ -10667,14 +11805,12 @@
}
-#if defined(HOSTAPD) || defined(CONFIG_AP)
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);
}
-#endif /* HOSTAPD || CONFIG_AP */
static int driver_nl80211_read_sta_data(void *priv,
@@ -10751,6 +11887,471 @@
}
+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%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->ignore_next_local_deauth ?
+ "ignore_next_local_deauth=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, bss->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;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int cmd_reply_handler(struct nl_msg *msg, void *arg)
+{
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpabuf *buf = arg;
+
+ if (!buf)
+ return NL_SKIP;
+
+ if ((size_t) genlmsg_attrlen(gnlh, 0) > wpabuf_tailroom(buf)) {
+ wpa_printf(MSG_INFO, "nl80211: insufficient buffer space for reply");
+ return NL_SKIP;
+ }
+
+ wpabuf_put_data(buf, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0));
+
+ return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int vendor_reply_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct nlattr *nl_vendor_reply, *nl;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpabuf *buf = arg;
+ int rem;
+
+ if (!buf)
+ return NL_SKIP;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ nl_vendor_reply = tb[NL80211_ATTR_VENDOR_DATA];
+
+ if (!nl_vendor_reply)
+ return NL_SKIP;
+
+ if ((size_t) nla_len(nl_vendor_reply) > wpabuf_tailroom(buf)) {
+ wpa_printf(MSG_INFO, "nl80211: Vendor command: insufficient buffer space for reply");
+ return NL_SKIP;
+ }
+
+ nla_for_each_nested(nl, nl_vendor_reply, rem) {
+ wpabuf_put_data(buf, nla_data(nl), nla_len(nl));
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
+ unsigned int subcmd, const u8 *data,
+ size_t data_len, struct wpabuf *buf)
+{
+ 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;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (vendor_id == 0xffffffff) {
+ nl80211_cmd(drv, msg, 0, subcmd);
+ if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
+ 0)
+ goto nla_put_failure;
+ ret = send_and_recv_msgs(drv, msg, cmd_reply_handler, buf);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
+ ret);
+ return ret;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
+ if (nl80211_set_iface_id(msg, bss) < 0)
+ goto nla_put_failure;
+ NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, vendor_id);
+ NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd);
+ if (data)
+ NLA_PUT(msg, NL80211_ATTR_VENDOR_DATA, data_len, data);
+
+ ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
+ ret);
+ return ret;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+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;
+}
+
+
+static int nl80211_set_wowlan(void *priv,
+ const struct wowlan_triggers *triggers)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *wowlan_triggers;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WOWLAN);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ wowlan_triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+ if (!wowlan_triggers)
+ goto nla_put_failure;
+
+ if (triggers->any)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+ if (triggers->disconnect)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
+ if (triggers->magic_pkt)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+ if (triggers->gtk_rekey_failure)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
+ if (triggers->eap_identity_req)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
+ if (triggers->four_way_handshake)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
+ if (triggers->rfkill_release)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
+
+ nla_nest_end(msg, wowlan_triggers);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan 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",
@@ -10772,6 +12373,7 @@
.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,
@@ -10782,12 +12384,9 @@
.sta_remove = driver_nl80211_sta_remove,
.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
-#ifdef HOSTAPD
.hapd_init = i802_init,
.hapd_deinit = i802_deinit,
.set_wds_sta = i802_set_wds_sta,
-#endif /* HOSTAPD */
-#if defined(HOSTAPD) || defined(CONFIG_AP)
.get_seqnum = i802_get_seqnum,
.flush = i802_flush,
.get_inact_sec = i802_get_inact_sec,
@@ -10798,7 +12397,6 @@
.set_sta_vlan = driver_nl80211_set_sta_vlan,
.sta_deauth = i802_sta_deauth,
.sta_disassoc = i802_sta_disassoc,
-#endif /* HOSTAPD || CONFIG_AP */
.read_sta_data = driver_nl80211_read_sta_data,
.set_freq = i802_set_freq,
.send_action = driver_nl80211_send_action,
@@ -10832,12 +12430,17 @@
.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
+#endif /* ANDROID_P2P */
#ifdef ANDROID
.driver_cmd = wpa_driver_nl80211_driver_cmd,
-#endif
+#endif /* ANDROID */
+ .vendor_cmd = nl80211_vendor_cmd,
+ .set_qos_map = nl80211_set_qos_map,
+ .set_wowlan = nl80211_set_wowlan,
};
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index 0a9078a..9ce3fa2 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -260,17 +260,17 @@
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)
+ if (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) ||
+ 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])
+ if (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) ||
+ read1[0] != read2[0])
return -1;
wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
index 541ebcc..3608b52 100644
--- a/src/drivers/driver_test.c
+++ b/src/drivers/driver_test.c
@@ -28,7 +28,6 @@
#include "common/ieee802_11_defs.h"
#include "crypto/sha1.h"
#include "l2_packet/l2_packet.h"
-#include "p2p/p2p.h"
#include "wps/wps.h"
#include "driver.h"
@@ -102,20 +101,6 @@
unsigned int remain_on_channel_duration;
int current_freq;
-
- struct p2p_data *p2p;
- unsigned int off_channel_freq;
- struct wpabuf *pending_action_tx;
- u8 pending_action_src[ETH_ALEN];
- u8 pending_action_dst[ETH_ALEN];
- u8 pending_action_bssid[ETH_ALEN];
- unsigned int pending_action_freq;
- unsigned int pending_action_no_cck;
- unsigned int pending_listen_freq;
- unsigned int pending_listen_duration;
- int pending_p2p_scan;
- struct sockaddr *probe_from;
- socklen_t probe_from_len;
};
@@ -125,7 +110,6 @@
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 int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv);
static void test_driver_free_bss(struct test_driver_bss *bss)
@@ -479,34 +463,6 @@
event.tx_status.ack = ret >= 0;
wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
-#ifdef CONFIG_P2P
- if (drv->p2p &&
- WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
- WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
- if (drv->pending_action_tx == NULL) {
- wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
- "no pending operation");
- return ret;
- }
-
- if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) !=
- 0) {
- wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
- "unknown destination address");
- return ret;
- }
-
- wpabuf_free(drv->pending_action_tx);
- drv->pending_action_tx = NULL;
-
- p2p_send_action_cb(drv->p2p, drv->pending_action_freq,
- drv->pending_action_dst,
- drv->pending_action_src,
- drv->pending_action_bssid,
- ret >= 0);
- }
-#endif /* CONFIG_P2P */
-
return ret;
}
@@ -553,10 +509,6 @@
event.rx_probe_req.ie = ie;
event.rx_probe_req.ie_len = ielen;
wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event);
-#ifdef CONFIG_P2P
- if (drv->p2p)
- p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
-#endif /* CONFIG_P2P */
}
dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
@@ -1059,7 +1011,7 @@
const char *ifname, const u8 *addr,
void *bss_ctx, void **drv_priv,
char *force_ifname, u8 *if_addr,
- const char *bridge)
+ const char *bridge, int use_existing)
{
struct test_driver_bss *dbss = priv;
struct wpa_driver_test_data *drv = dbss->drv;
@@ -1313,25 +1265,7 @@
static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
- struct wpa_driver_test_data *drv = eloop_ctx;
wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
- if (drv->pending_p2p_scan && drv->p2p) {
-#ifdef CONFIG_P2P
- size_t i;
- struct os_time now;
- os_get_time(&now);
- for (i = 0; i < drv->num_scanres; i++) {
- struct wpa_scan_res *bss = drv->scanres[i];
- if (p2p_scan_res_handler(drv->p2p, bss->bssid,
- bss->freq, &now, bss->level,
- (const u8 *) (bss + 1),
- bss->ie_len) > 0)
- return;
- }
- p2p_scan_res_handled(drv->p2p);
-#endif /* CONFIG_P2P */
- return;
- }
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
}
@@ -1595,7 +1529,8 @@
#endif /* DRIVER_TEST_UNIX */
if (params->mode == IEEE80211_MODE_AP) {
- os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+ if (params->ssid)
+ 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) {
@@ -1616,8 +1551,9 @@
MAC2STR(drv->own_addr));
if (ret >= 0 && ret < end - pos)
pos += ret;
- pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
- params->ssid_len);
+ if (params->ssid)
+ 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;
@@ -1641,12 +1577,15 @@
return -1;
}
- os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+ if (params->ssid)
+ 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);
+ if (params->ssid)
+ os_memcpy(dbss->ssid, params->ssid,
+ params->ssid_len);
dbss->ssid_len = params->ssid_len;
if (params->bssid)
os_memcpy(dbss->bssid, params->bssid,
@@ -1949,30 +1888,8 @@
data_len - (mgmt->u.probe_req.variable - data);
wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
&event);
-#ifdef CONFIG_P2P
- if (drv->p2p)
- p2p_probe_req_rx(drv->p2p, mgmt->sa,
- mgmt->da, mgmt->bssid,
- event.rx_probe_req.ie,
- event.rx_probe_req.ie_len);
-#endif /* CONFIG_P2P */
}
}
-
-#ifdef CONFIG_P2P
- if (drv->p2p &&
- WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
- WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
- size_t hdr_len;
- hdr_len = (const u8 *)
- &mgmt->u.action.u.vs_public_action.action - data;
- p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
- mgmt->u.action.category,
- &mgmt->u.action.u.vs_public_action.action,
- data_len - hdr_len, freq);
- }
-#endif /* CONFIG_P2P */
-
}
@@ -1988,31 +1905,8 @@
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
/* data: optional [ STA-addr | ' ' | IEs(hex) ] */
-#ifdef CONFIG_P2P
- if (drv->probe_req_report && drv->p2p && data_len) {
- const char *d = (const char *) data;
- u8 sa[ETH_ALEN];
- u8 ie[512];
- size_t ielen;
- if (hwaddr_aton(d, sa))
- return;
- d += 18;
- while (*d == ' ')
- d++;
- ielen = os_strlen(d) / 2;
- if (ielen > sizeof(ie))
- ielen = sizeof(ie);
- if (hexstr2bin(d, ie, ielen) < 0)
- ielen = 0;
- drv->probe_from = from;
- drv->probe_from_len = fromlen;
- p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
- drv->probe_from = NULL;
- }
-#endif /* CONFIG_P2P */
-
- if (!drv->ibss)
+ if (bss == NULL || !drv->ibss)
return;
pos = buf;
@@ -2167,12 +2061,6 @@
struct test_client_socket *cli, *prev;
int i;
-#ifdef CONFIG_P2P
- if (drv->p2p)
- p2p_deinit(drv->p2p);
- wpabuf_free(drv->pending_action_tx);
-#endif /* CONFIG_P2P */
-
cli = drv->cli;
while (cli) {
prev = cli;
@@ -2369,13 +2257,6 @@
drv->use_associnfo = 1;
}
- if (os_strstr(param, "p2p_mgmt=1")) {
- wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P "
- "management");
- if (wpa_driver_test_init_p2p(drv) < 0)
- return -1;
- }
-
return 0;
}
@@ -2465,8 +2346,6 @@
static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
os_memset(capa, 0, sizeof(*capa));
capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
@@ -2482,8 +2361,6 @@
capa->auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
- if (drv->p2p)
- capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT;
capa->flags |= WPA_DRIVER_FLAGS_AP;
capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE;
@@ -2691,33 +2568,6 @@
}
-#ifdef CONFIG_P2P
-static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx)
-{
- struct wpa_driver_test_data *drv = eloop_ctx;
-
- if (drv->pending_action_tx == NULL)
- return;
-
- if (drv->off_channel_freq != drv->pending_action_freq) {
- wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
- "waiting for another freq=%u",
- drv->pending_action_freq);
- return;
- }
- wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
- MACSTR, MAC2STR(drv->pending_action_dst));
- wpa_driver_test_send_action(drv, drv->pending_action_freq, 0,
- drv->pending_action_dst,
- drv->pending_action_src,
- drv->pending_action_bssid,
- wpabuf_head(drv->pending_action_tx),
- wpabuf_len(drv->pending_action_tx),
- drv->pending_action_no_cck);
-}
-#endif /* CONFIG_P2P */
-
-
static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_driver_test_data *drv = eloop_ctx;
@@ -2729,9 +2579,6 @@
data.remain_on_channel.freq = drv->remain_on_channel_freq;
data.remain_on_channel.duration = drv->remain_on_channel_duration;
- if (drv->p2p)
- drv->off_channel_freq = 0;
-
drv->remain_on_channel_freq = 0;
wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
@@ -2765,18 +2612,6 @@
data.remain_on_channel.duration = duration;
wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
-#ifdef CONFIG_P2P
- if (drv->p2p) {
- drv->off_channel_freq = drv->remain_on_channel_freq;
- test_send_action_cb(drv, NULL);
- if (drv->off_channel_freq == drv->pending_listen_freq) {
- p2p_listen_cb(drv->p2p, drv->pending_listen_freq,
- drv->pending_listen_duration);
- drv->pending_listen_freq = 0;
- }
- }
-#endif /* CONFIG_P2P */
-
return 0;
}
@@ -2804,470 +2639,6 @@
}
-#ifdef CONFIG_P2P
-
-static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type)
-{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
- wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
- if (!drv->p2p)
- return -1;
- return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL, 0);
-}
-
-
-static int wpa_driver_test_p2p_stop_find(void *priv)
-{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
- wpa_printf(MSG_DEBUG, "%s", __func__);
- if (!drv->p2p)
- return -1;
- p2p_stop_find(drv->p2p);
- return 0;
-}
-
-
-static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout)
-{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
- wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
- if (!drv->p2p)
- return -1;
- return p2p_listen(drv->p2p, timeout);
-}
-
-
-static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr,
- int wps_method, int go_intent,
- const u8 *own_interface_addr,
- unsigned int force_freq,
- int persistent_group)
-{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
- wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d "
- "go_intent=%d "
- "own_interface_addr=" MACSTR " force_freq=%u "
- "persistent_group=%d)",
- __func__, MAC2STR(peer_addr), wps_method, go_intent,
- MAC2STR(own_interface_addr), force_freq, persistent_group);
- if (!drv->p2p)
- return -1;
- return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent,
- own_interface_addr, force_freq, persistent_group,
- NULL, 0, 0, 0);
-}
-
-
-static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr)
-{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
- wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")",
- __func__, MAC2STR(peer_addr));
- if (!drv->p2p)
- return -1;
- p2p_wps_success_cb(drv->p2p, peer_addr);
- return 0;
-}
-
-
-static int wpa_driver_test_p2p_group_formation_failed(void *priv)
-{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
- wpa_printf(MSG_DEBUG, "%s", __func__);
- if (!drv->p2p)
- return -1;
- p2p_group_formation_failed(drv->p2p);
- return 0;
-}
-
-
-static int wpa_driver_test_p2p_set_params(void *priv,
- const struct p2p_params *params)
-{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
- wpa_printf(MSG_DEBUG, "%s", __func__);
- if (!drv->p2p)
- return -1;
- if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 ||
- p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 ||
- p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type,
- params->num_sec_dev_types) < 0)
- return -1;
- return 0;
-}
-
-
-static int test_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)
-{
- struct wpa_driver_test_data *drv = ctx;
- struct wpa_driver_scan_params params;
- int ret;
- struct wpabuf *wps_ie, *ies;
- int social_channels[] = { 2412, 2437, 2462, 0, 0 };
- size_t ielen;
-
- wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)",
- __func__, type, freq);
-
- os_memset(¶ms, 0, sizeof(params));
-
- /* P2P Wildcard SSID */
- params.num_ssids = 1;
- params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
- params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
-
-#if 0 /* TODO: WPS IE */
- wpa_s->wps->dev.p2p = 1;
- wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
- wpa_s->wps->uuid, WPS_REQ_ENROLLEE);
-#else
- wps_ie = wpabuf_alloc(1);
-#endif
- if (wps_ie == NULL)
- return -1;
-
- ielen = p2p_scan_ie_buf_len(drv->p2p);
- ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
- if (ies == NULL) {
- wpabuf_free(wps_ie);
- return -1;
- }
- wpabuf_put_buf(ies, wps_ie);
- wpabuf_free(wps_ie);
-
- p2p_scan_ie(drv->p2p, ies, dev_id);
-
- params.extra_ies = wpabuf_head(ies);
- params.extra_ies_len = wpabuf_len(ies);
-
- switch (type) {
- case P2P_SCAN_SOCIAL:
- params.freqs = social_channels;
- break;
- case P2P_SCAN_FULL:
- break;
- case P2P_SCAN_SOCIAL_PLUS_ONE:
- social_channels[3] = freq;
- params.freqs = social_channels;
- break;
- }
-
- drv->pending_p2p_scan = 1;
- ret = wpa_driver_test_scan(drv, ¶ms);
-
- wpabuf_free(ies);
-
- return ret;
-}
-
-
-static int test_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)
-{
- struct wpa_driver_test_data *drv = ctx;
-
- wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR
- " bssid=" MACSTR " len=%d",
- __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
- (int) len);
- if (freq <= 0) {
- wpa_printf(MSG_WARNING, "P2P: No frequency specified for "
- "action frame TX");
- return -1;
- }
-
- if (drv->pending_action_tx) {
- wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
- "to " MACSTR, MAC2STR(drv->pending_action_dst));
- wpabuf_free(drv->pending_action_tx);
- }
- drv->pending_action_tx = wpabuf_alloc(len);
- if (drv->pending_action_tx == NULL)
- return -1;
- wpabuf_put_data(drv->pending_action_tx, buf, len);
- os_memcpy(drv->pending_action_src, src, ETH_ALEN);
- os_memcpy(drv->pending_action_dst, dst, ETH_ALEN);
- os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN);
- drv->pending_action_freq = freq;
- drv->pending_action_no_cck = 1;
-
- if (drv->off_channel_freq == freq) {
- /* Already on requested channel; send immediately */
- /* TODO: Would there ever be need to extend the current
- * duration on the channel? */
- eloop_cancel_timeout(test_send_action_cb, drv, NULL);
- eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL);
- return 0;
- }
-
- wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
- "once the driver gets to the requested channel");
- if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) {
- wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
- "to remain on channel (%u MHz) for Action "
- "Frame TX", freq);
- return -1;
- }
-
- return 0;
-}
-
-
-static void test_send_action_done(void *ctx)
-{
- wpa_printf(MSG_DEBUG, "%s", __func__);
- /* TODO */
-}
-
-
-static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
-{
- struct wpa_driver_test_data *drv = ctx;
- union wpa_event_data event;
- wpa_printf(MSG_DEBUG, "%s", __func__);
- os_memset(&event, 0, sizeof(event));
- event.p2p_go_neg_completed.res = res;
- wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event);
-}
-
-
-static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
-{
- struct wpa_driver_test_data *drv = ctx;
- union wpa_event_data event;
- wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src));
- os_memset(&event, 0, sizeof(event));
- event.p2p_go_neg_req_rx.src = src;
- event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id;
- wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event);
-}
-
-
-static void test_dev_found(void *ctx, const u8 *addr,
- const struct p2p_peer_info *info, int new_device)
-{
- struct wpa_driver_test_data *drv = ctx;
- union wpa_event_data event;
- char devtype[WPS_DEV_TYPE_BUFSIZE];
- wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR
- " pri_dev_type=%s name='%s' config_methods=0x%x "
- "dev_capab=0x%x group_capab=0x%x)",
- __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr),
- wps_dev_type_bin2str(info->pri_dev_type, devtype,
- sizeof(devtype)),
- info->device_name, info->config_methods, info->dev_capab,
- info->group_capab);
-
- os_memset(&event, 0, sizeof(event));
- event.p2p_dev_found.addr = addr;
- event.p2p_dev_found.dev_addr = info->p2p_device_addr;
- event.p2p_dev_found.pri_dev_type = info->pri_dev_type;
- event.p2p_dev_found.dev_name = info->device_name;
- event.p2p_dev_found.config_methods = info->config_methods;
- event.p2p_dev_found.dev_capab = info->dev_capab;
- event.p2p_dev_found.group_capab = info->group_capab;
- wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event);
-}
-
-
-static int test_start_listen(void *ctx, unsigned int freq,
- unsigned int duration,
- const struct wpabuf *probe_resp_ie)
-{
- struct wpa_driver_test_data *drv = ctx;
-
- wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)",
- __func__, freq, duration);
-
- if (wpa_driver_test_probe_req_report(drv, 1) < 0)
- return -1;
-
- drv->pending_listen_freq = freq;
- drv->pending_listen_duration = duration;
-
- if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) {
- drv->pending_listen_freq = 0;
- return -1;
- }
-
- return 0;
-}
-
-
-static void test_stop_listen(void *ctx)
-{
- wpa_printf(MSG_DEBUG, "%s", __func__);
- /* TODO */
-}
-
-
-static int test_send_probe_resp(void *ctx, const struct wpabuf *buf)
-{
- struct wpa_driver_test_data *drv = ctx;
- char resp[512], *pos, *end;
- int ret;
- const struct ieee80211_mgmt *mgmt;
- const u8 *ie, *ie_end;
-
- wpa_printf(MSG_DEBUG, "%s", __func__);
- wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf);
- if (wpabuf_len(buf) < 24)
- return -1;
- if (!drv->probe_from) {
- wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__);
- return -1;
- }
-
- pos = resp;
- end = resp + sizeof(resp);
-
- mgmt = wpabuf_head(buf);
-
- /* reply: SCANRESP BSSID SSID IEs */
- ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
- MAC2STR(mgmt->bssid));
- if (ret < 0 || ret >= end - pos)
- return -1;
- pos += ret;
-
- ie = mgmt->u.probe_resp.variable;
- ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf);
- if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID ||
- ie + 2 + ie[1] > ie_end)
- return -1;
- pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]);
-
- ret = os_snprintf(pos, end - pos, " ");
- if (ret < 0 || ret >= end - pos)
- return -1;
- pos += ret;
- pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie);
-
- sendto(drv->test_socket, resp, pos - resp, 0,
- drv->probe_from, drv->probe_from_len);
-
- return 0;
-}
-
-
-static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
- u16 update_indic, const u8 *tlvs, size_t tlvs_len)
-{
- wpa_printf(MSG_DEBUG, "%s", __func__);
- /* TODO */
-}
-
-
-static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic,
- const u8 *tlvs, size_t tlvs_len)
-{
- wpa_printf(MSG_DEBUG, "%s", __func__);
- /* TODO */
-}
-
-
-static void test_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)
-{
- wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
- __func__, MAC2STR(peer), config_methods);
- /* TODO */
-}
-
-
-static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
-{
- wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
- __func__, MAC2STR(peer), config_methods);
- /* TODO */
-}
-
-
-static void test_p2p_debug_print(void *ctx, int level, const char *msg)
-{
- wpa_printf(level, "P2P: %s", msg);
-}
-
-#endif /* CONFIG_P2P */
-
-
-static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv)
-{
-#ifdef CONFIG_P2P
- struct p2p_config p2p;
- unsigned int r;
- int i;
-
- os_memset(&p2p, 0, sizeof(p2p));
- p2p.cb_ctx = drv;
- p2p.debug_print = test_p2p_debug_print;
- p2p.p2p_scan = test_p2p_scan;
- p2p.send_action = test_send_action;
- p2p.send_action_done = test_send_action_done;
- p2p.go_neg_completed = test_go_neg_completed;
- p2p.go_neg_req_rx = test_go_neg_req_rx;
- p2p.dev_found = test_dev_found;
- p2p.start_listen = test_start_listen;
- p2p.stop_listen = test_stop_listen;
- p2p.send_probe_resp = test_send_probe_resp;
- p2p.sd_request = test_sd_request;
- p2p.sd_response = test_sd_response;
- p2p.prov_disc_req = test_prov_disc_req;
- p2p.prov_disc_resp = test_prov_disc_resp;
-
- os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN);
-
- p2p.reg_class = 12; /* TODO: change depending on location */
- /*
- * Pick one of the social channels randomly as the listen
- * channel.
- */
- os_get_random((u8 *) &r, sizeof(r));
- p2p.channel = 1 + (r % 3) * 5;
-
- /* TODO: change depending on location */
- p2p.op_reg_class = 12;
- /*
- * For initial tests, pick the operation channel randomly.
- * TODO: Use scan results (etc.) to select the best channel.
- */
- p2p.op_channel = 1 + r % 11;
-
- os_memcpy(p2p.country, "US ", 3);
-
- /* FIX: fetch available channels from the driver */
- p2p.channels.reg_classes = 1;
- p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */
- p2p.channels.reg_class[0].channels = 11;
- for (i = 0; i < 11; i++)
- p2p.channels.reg_class[0].channel[i] = i + 1;
-
- p2p.max_peers = 100;
-
- drv->p2p = p2p_init(&p2p);
- if (drv->p2p == NULL)
- return -1;
- return 0;
-#else /* CONFIG_P2P */
- wpa_printf(MSG_INFO, "driver_test: P2P support not included");
- return -1;
-#endif /* CONFIG_P2P */
-}
-
-
const struct wpa_driver_ops wpa_driver_test_ops = {
"test",
"wpa_supplicant test driver",
@@ -3309,14 +2680,4 @@
.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,
-#ifdef CONFIG_P2P
- .p2p_find = wpa_driver_test_p2p_find,
- .p2p_stop_find = wpa_driver_test_p2p_stop_find,
- .p2p_listen = wpa_driver_test_p2p_listen,
- .p2p_connect = wpa_driver_test_p2p_connect,
- .wps_success_cb = wpa_driver_test_wps_success_cb,
- .p2p_group_formation_failed =
- wpa_driver_test_p2p_group_formation_failed,
- .p2p_set_params = wpa_driver_test_p2p_set_params,
-#endif /* CONFIG_P2P */
};
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 1401050..459ac48 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -31,10 +31,6 @@
#include "driver.h"
#include "driver_wext.h"
-#ifdef ANDROID
-#include "android_drv.h"
-#endif /* ANDROID */
-
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);
@@ -299,14 +295,6 @@
}
wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
#endif /* CONFIG_PEERKEY */
-#ifdef ANDROID
- } else if (os_strncmp(custom, "STOP", 4) == 0) {
- wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
- } else if (os_strncmp(custom, "START", 5) == 0) {
- wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
- } else if (os_strncmp(custom, "HANG", 4) == 0) {
- wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
-#endif /* ANDROID */
}
}
@@ -858,12 +846,6 @@
drv->mlme_sock = -1;
-#ifdef ANDROID
- drv->errors = 0;
- drv->driver_is_started = TRUE;
- drv->bgscan_enabled = 0;
-#endif /* ANDROID */
-
if (wpa_driver_wext_finish_drv_init(drv) < 0)
goto err3;
@@ -1869,10 +1851,8 @@
{
struct iwreq iwr;
const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-#ifndef ANDROID
u8 ssid[32];
int i;
-#endif /* ANDROID */
/*
* Only force-disconnect when the card is in infrastructure mode,
@@ -1893,7 +1873,6 @@
"selection on disconnect");
}
-#ifndef ANDROID
if (drv->cfg80211) {
/*
* cfg80211 supports SIOCSIWMLME commands, so there is
@@ -1919,7 +1898,6 @@
wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
"SSID to disconnect");
}
-#endif /* ANDROID */
}
}
@@ -1960,15 +1938,15 @@
int wpa_driver_wext_cipher2wext(int cipher)
{
switch (cipher) {
- case CIPHER_NONE:
+ case WPA_CIPHER_NONE:
return IW_AUTH_CIPHER_NONE;
- case CIPHER_WEP40:
+ case WPA_CIPHER_WEP40:
return IW_AUTH_CIPHER_WEP40;
- case CIPHER_TKIP:
+ case WPA_CIPHER_TKIP:
return IW_AUTH_CIPHER_TKIP;
- case CIPHER_CCMP:
+ case WPA_CIPHER_CCMP:
return IW_AUTH_CIPHER_CCMP;
- case CIPHER_WEP104:
+ case WPA_CIPHER_WEP104:
return IW_AUTH_CIPHER_WEP104;
default:
return 0;
@@ -1979,10 +1957,10 @@
int wpa_driver_wext_keymgmt2wext(int keymgmt)
{
switch (keymgmt) {
- case KEY_MGMT_802_1X:
- case KEY_MGMT_802_1X_NO_WPA:
+ case WPA_KEY_MGMT_IEEE8021X:
+ case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
return IW_AUTH_KEY_MGMT_802_1X;
- case KEY_MGMT_PSK:
+ case WPA_KEY_MGMT_PSK:
return IW_AUTH_KEY_MGMT_PSK;
default:
return 0;
@@ -2049,7 +2027,11 @@
* 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_ssid(drv, (u8 *) "", 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "WEXT: Failed to clear SSID to stop pending cfg80211 association attempts (if any)");
+ /* continue anyway */
+ }
}
if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted)
@@ -2099,9 +2081,9 @@
if (wpa_driver_wext_set_auth_param(drv,
IW_AUTH_KEY_MGMT, value) < 0)
ret = -1;
- value = params->key_mgmt_suite != KEY_MGMT_NONE ||
- params->pairwise_suite != CIPHER_NONE ||
- params->group_suite != CIPHER_NONE ||
+ 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)
@@ -2110,8 +2092,8 @@
/* 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 == KEY_MGMT_802_1X ||
- params->key_mgmt_suite == KEY_MGMT_PSK)
+ 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;
@@ -2338,129 +2320,37 @@
}
-#ifdef ANDROID
-
-static int android_wext_cmd(struct wpa_driver_wext_data *drv, const char *cmd)
+static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si)
{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iw_statistics stats;
struct iwreq iwr;
- char buf[MAX_DRV_CMD_SIZE];
- int ret;
+
+ os_memset(si, 0, sizeof(*si));
+ si->current_signal = -9999;
+ si->current_noise = 9999;
+ si->chanwidth = CHAN_WIDTH_UNKNOWN;
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (caddr_t) &stats;
+ iwr.u.data.length = sizeof(stats);
+ iwr.u.data.flags = 1;
- os_memset(buf, 0, sizeof(buf));
- os_strlcpy(buf, cmd, sizeof(buf));
-
- iwr.u.data.pointer = buf;
- iwr.u.data.length = sizeof(buf);
-
- ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
-
- if (ret < 0) {
- wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret,
- cmd);
- drv->errors++;
- if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
- drv->errors = 0;
- wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE
- "HANGED");
- }
- return ret;
+ if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr) < 0) {
+ wpa_printf(MSG_ERROR, "WEXT: SIOCGIWSTATS: %s",
+ strerror(errno));
+ return -1;
}
- drv->errors = 0;
+ si->current_signal = stats.qual.level -
+ ((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
+ si->current_noise = stats.qual.noise -
+ ((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
return 0;
}
-static int wext_sched_scan(void *priv, struct wpa_driver_scan_params *params,
- u32 interval)
-{
- struct wpa_driver_wext_data *drv = priv;
- struct iwreq iwr;
- 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 + IW_ESSID_MAX_SIZE +
- 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;
- /* TODO: consider using interval parameter (interval in msec) instead
- * of hardcoded value here */
- 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;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
- iwr.u.data.pointer = buf;
- iwr.u.data.length = bp;
-
- ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
- if (ret < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
- ret);
- drv->errors++;
- if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
- drv->errors = 0;
- wpa_msg(drv->ctx, MSG_INFO,
- WPA_EVENT_DRIVER_STATE "HANGED");
- }
- return ret;
- }
-
- drv->errors = 0;
- drv->bgscan_enabled = 1;
-
- return android_wext_cmd(drv, "PNOFORCE 1");
-}
-
-
-static int wext_stop_sched_scan(void *priv)
-{
- struct wpa_driver_wext_data *drv = priv;
- drv->bgscan_enabled = 0;
- return android_wext_cmd(drv, "PNOFORCE 0");
-}
-
-#endif /* ANDROID */
-
-
const struct wpa_driver_ops wpa_driver_wext_ops = {
.name = "wext",
.desc = "Linux wireless extensions (generic)",
@@ -2480,8 +2370,5 @@
.get_capa = wpa_driver_wext_get_capa,
.set_operstate = wpa_driver_wext_set_operstate,
.get_radio_name = wext_get_radio_name,
-#ifdef ANDROID
- .sched_scan = wext_sched_scan,
- .stop_sched_scan = wext_stop_sched_scan,
-#endif /* ANDROID */
+ .signal_poll = wpa_driver_wext_signal_poll,
};
diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h
index c4a5bc9..b4b5960 100644
--- a/src/drivers/driver_wext.h
+++ b/src/drivers/driver_wext.h
@@ -44,12 +44,6 @@
int cfg80211; /* whether driver is using cfg80211 */
u8 max_level;
-
-#ifdef ANDROID
- int errors;
- int driver_is_started;
- int bgscan_enabled;
-#endif /* ANDROID */
};
int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
index 04eb4fd..d0e42ec 100644
--- a/src/drivers/drivers.c
+++ b/src/drivers/drivers.c
@@ -6,8 +6,9 @@
* See README for more details.
*/
-#include "includes.h"
-
+#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 */
@@ -33,6 +34,10 @@
#ifdef CONFIG_DRIVER_WIRED
extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+ /* driver_macsec_qca.c */
+extern struct wpa_driver_ops wpa_driver_macsec_qca_ops;
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
#ifdef CONFIG_DRIVER_TEST
extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */
#endif /* CONFIG_DRIVER_TEST */
@@ -74,6 +79,9 @@
#ifdef CONFIG_DRIVER_WIRED
&wpa_driver_wired_ops,
#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+ &wpa_driver_macsec_qca_ops,
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
#ifdef CONFIG_DRIVER_TEST
&wpa_driver_test_ops,
#endif /* CONFIG_DRIVER_TEST */
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 68ff910..40aaba5 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -17,6 +17,11 @@
DRV_OBJS += ../src/drivers/driver_wired.o
endif
+ifdef CONFIG_DRIVER_MACSEC_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
+DRV_OBJS += ../src/drivers/driver_macsec_qca.o
+endif
+
ifdef CONFIG_DRIVER_NL80211
DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
DRV_OBJS += ../src/drivers/driver_nl80211.o
@@ -100,6 +105,9 @@
CONFIG_L2_PACKET=linux
NEED_NETLINK=y
NEED_LINUX_IOCTL=y
+ifdef ATH_GCM_SUPPORT
+CFLAGS += -DATH_GCM_SUPPORT
+endif
endif
##### PURE CLIENT DRIVERS
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index 4380428..837971d 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -204,11 +204,14 @@
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);
- os_memset(brlink, 0, sizeof(brlink));
- if (readlink(path, brlink, sizeof(brlink) - 1) < 0)
+ 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;
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
index 6c60550..2fa20b1 100644
--- a/src/drivers/netlink.c
+++ b/src/drivers/netlink.c
@@ -1,6 +1,6 @@
/*
* Netlink helper functions for driver wrappers
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -137,6 +137,35 @@
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)
{
@@ -184,8 +213,9 @@
RTA_LENGTH(sizeof(char));
}
- wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
- linkmode, operstate);
+ 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) {
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 32b060e..406010d 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -126,6 +126,31 @@
*/
/**
+ * 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
@@ -278,8 +303,9 @@
* 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_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
+ * scheduled scan is not running. The caller may assume that as soon
+ * as the call returns, it is safe to start a new scheduled scan again.
* @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
* results available.
* @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
@@ -393,8 +419,18 @@
* %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.
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ * %NL80211_ATTR_WIPHY_FREQ_HINT.
+ * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ * restrictions on BSS selection, i.e., they effectively prevent roaming
+ * within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ * can be included to provide a recommendation of the initial BSS while
+ * allowing the driver to roam to other BSSes within the ESS and also to
+ * ignore this recommendation if the indicated BSS is not ideal. Only one
+ * set of BSSID,frequency parameters is used (i.e., either the enforcing
+ * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
* Background scan period can optionally be
* specified in %NL80211_ATTR_BG_SCAN_PERIOD,
* if not specified default background scan configuration
@@ -556,7 +592,14 @@
* 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.
+ * @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
@@ -648,6 +691,34 @@
* @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
*/
@@ -810,6 +881,15 @@
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 */
@@ -945,7 +1025,7 @@
* 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.11d country information element to identify a country.
+ * 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
@@ -1436,6 +1516,73 @@
* 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_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ * associated stations are supported in AP mode (including P2P GO); u32.
+ * Since drivers may not have a fixed limit on the maximum number (e.g.,
+ * other concurrent operations may affect this), drivers are allowed to
+ * advertise values that cannot always be met. In such cases, an attempt
+ * to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ * As specified in the &enum nl80211_tdls_peer_capability.
+ *
+ * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface
+ * creation then the new interface will be owned by the netlink socket
+ * that created it and will be destroyed when the socket is closed
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1736,6 +1883,43 @@
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,
+
+ NL80211_ATTR_MAC_HINT,
+ NL80211_ATTR_WIPHY_FREQ_HINT,
+
+ NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+ NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
+ NL80211_ATTR_IFACE_SOCKET_OWNER,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2136,10 +2320,9 @@
* @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
* @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
* regulatory domain.
- * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is
- * permitted on this channel in current regulatory domain.
- * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted
- * on this channel 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
@@ -2158,16 +2341,42 @@
* @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_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this
+ * channel. A channel that has the INDOOR_ONLY attribute can only be
+ * used when there is a clear assessment that the device is operating in
+ * an indoor surroundings, i.e., it is connected to AC power (and not
+ * through portable DC inverters) or is under the control of a master
+ * that is acting as an AP and is connected to AC power.
+ * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this
+ * channel if it's connected concurrently to a BSS on the same channel on
+ * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
+ * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a
+ * channel that has the GO_CONCURRENT attribute set can be done when there
+ * is a clear assessment that the device is operating under the guidance of
+ * an authorized master, i.e., setting up a GO while the device is also
+ * connected to an AP with DFS and radar detection on the UNII band (it is
+ * up to user-space, i.e., wpa_supplicant to perform the required
+ * verifications)
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ * on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ *
+ * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
+ * for more information on the FCC description of the relaxations allowed
+ * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
+ * NL80211_FREQUENCY_ATTR_GO_CONCURRENT.
*/
enum nl80211_frequency_attr {
__NL80211_FREQUENCY_ATTR_INVALID,
NL80211_FREQUENCY_ATTR_FREQ,
NL80211_FREQUENCY_ATTR_DISABLED,
- NL80211_FREQUENCY_ATTR_PASSIVE_SCAN,
- NL80211_FREQUENCY_ATTR_NO_IBSS,
+ 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,
@@ -2176,6 +2385,11 @@
NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
NL80211_FREQUENCY_ATTR_NO_80MHZ,
NL80211_FREQUENCY_ATTR_NO_160MHZ,
+ NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+ NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
+ NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+ NL80211_FREQUENCY_ATTR_NO_20MHZ,
+ NL80211_FREQUENCY_ATTR_NO_10MHZ,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2183,6 +2397,9 @@
};
#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
@@ -2263,12 +2480,14 @@
* 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.
+ * 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_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * If not present or 0 default CAC time will be used.
* @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
* currently defined
* @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
@@ -2284,6 +2503,8 @@
NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
NL80211_ATTR_POWER_RULE_MAX_EIRP,
+ NL80211_ATTR_DFS_CAC_TIME,
+
/* keep last */
__NL80211_REG_RULE_ATTR_AFTER_LAST,
NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
@@ -2293,9 +2514,15 @@
* 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.
+ * 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.
+ * BSS in scan results. Filtering is turned off if not specified. Note that
+ * if this attribute is in a match set of its own, then it is treated as
+ * the default value for all matchsets with an SSID, rather than being a
+ * matchset of its own without an RSSI filter. This is due to problems with
+ * how this API was implemented in the past. Also, due to the same problem,
+ * the only way to create a matchset with only an RSSI filter (with this
+ * attribute) is if there's only a single matchset with the RSSI attribute.
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -2325,8 +2552,12 @@
* @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_PASSIVE_SCAN: passive scan is required
- * @NL80211_RRF_NO_IBSS: no IBSS is allowed
+ * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+ * this includes probe requests or modes of operation that require
+ * beaconing.
+ * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+ * base on contiguous rules and wider channels will be allowed to cross
+ * multiple contiguous/overlapping frequency ranges.
*/
enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1<<0,
@@ -2336,10 +2567,18 @@
NL80211_RRF_DFS = 1<<4,
NL80211_RRF_PTP_ONLY = 1<<5,
NL80211_RRF_PTMP_ONLY = 1<<6,
- NL80211_RRF_PASSIVE_SCAN = 1<<7,
- NL80211_RRF_NO_IBSS = 1<<8,
+ NL80211_RRF_NO_IR = 1<<7,
+ __NL80211_RRF_NO_IBSS = 1<<8,
+ NL80211_RRF_AUTO_BW = 1<<11,
};
+#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
*
@@ -2369,10 +2608,13 @@
* present has been registered with the wireless core that
* has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
* supported feature.
+ * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
+ * platform is operating in an indoor environment.
*/
enum nl80211_user_reg_hint_type {
NL80211_USER_REG_HINT_USER = 0,
NL80211_USER_REG_HINT_CELL_BASE = 1,
+ NL80211_USER_REG_HINT_INDOOR = 2,
};
/**
@@ -2428,6 +2670,8 @@
* @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
@@ -2439,6 +2683,7 @@
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,
@@ -2574,6 +2819,10 @@
*
* @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 {
@@ -2605,6 +2854,7 @@
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
NL80211_MESHCONF_POWER_MODE,
NL80211_MESHCONF_AWAKE_WINDOW,
+ NL80211_MESHCONF_PLINK_TIMEOUT,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -2750,6 +3000,8 @@
* 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,
@@ -2758,6 +3010,23 @@
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,
};
/**
@@ -2784,6 +3053,8 @@
* @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
*/
@@ -2800,6 +3071,7 @@
NL80211_BSS_STATUS,
NL80211_BSS_SEEN_MS_AGO,
NL80211_BSS_BEACON_IES,
+ NL80211_BSS_CHAN_WIDTH,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -2940,21 +3212,43 @@
* 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_MCS: HT (MCS) rates allowed for TX rate selection
+ * @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_GI: configure GI, see &enum nl80211_txrate_gi
* @__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_MCS,
+ NL80211_TXRATE_HT,
+ NL80211_TXRATE_VHT,
+ NL80211_TXRATE_GI,
/* 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_txrate_gi {
+ NL80211_TXRATE_DEFAULT_GI,
+ NL80211_TXRATE_FORCE_SGI,
+ NL80211_TXRATE_FORCE_LGI,
+};
+
/**
* enum nl80211_band - Frequency band
* @NL80211_BAND_2GHZ: 2.4 GHz ISM band
@@ -3048,11 +3342,11 @@
};
/**
- * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
- * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
- * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * 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_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * @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
@@ -3063,39 +3357,50 @@
* 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_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
* these fixed number of bytes of received packet
- * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
- * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
*/
-enum nl80211_wowlan_packet_pattern_attr {
- __NL80211_WOWLAN_PKTPAT_INVALID,
- NL80211_WOWLAN_PKTPAT_MASK,
- NL80211_WOWLAN_PKTPAT_PATTERN,
- NL80211_WOWLAN_PKTPAT_OFFSET,
+enum nl80211_packet_pattern_attr {
+ __NL80211_PKTPAT_INVALID,
+ NL80211_PKTPAT_MASK,
+ NL80211_PKTPAT_PATTERN,
+ NL80211_PKTPAT_OFFSET,
- NUM_NL80211_WOWLAN_PKTPAT,
- MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+ NUM_NL80211_PKTPAT,
+ MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
};
/**
- * struct nl80211_wowlan_pattern_support - pattern support information
+ * 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 in the
- * capability information given by the kernel to userspace.
+ * 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_wowlan_pattern_support {
+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
@@ -3115,7 +3420,7 @@
* 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_wowlan_pattern_support.
+ * 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
@@ -3272,7 +3577,7 @@
* @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_WOWLAN_PKTPAT_MASK
+ * 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
@@ -3297,6 +3602,55 @@
};
/**
+ * 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
@@ -3540,11 +3894,6 @@
* @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
@@ -3576,13 +3925,20 @@
* 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.
+ * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
+ * channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
+ * lifetime of a BSS.
*/
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,
+ /* bit 4 is reserved - don't use */
NL80211_FEATURE_SAE = 1 << 5,
NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6,
NL80211_FEATURE_SCAN_FLUSH = 1 << 7,
@@ -3595,6 +3951,8 @@
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,
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18,
};
/**
@@ -3695,13 +4053,12 @@
*
* Channel states used by the DFS code.
*
- * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
* check (CAC) must be performed before using it for AP or IBSS.
- * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
* is therefore marked as not available.
- * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
*/
-
enum nl80211_dfs_state {
NL80211_DFS_USABLE,
NL80211_DFS_UNAVAILABLE,
@@ -3741,4 +4098,51 @@
/* 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;
+};
+
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ */
+enum nl80211_tdls_peer_capability {
+ NL80211_TDLS_PEER_HT = 1<<0,
+ NL80211_TDLS_PEER_VHT = 1<<1,
+ NL80211_TDLS_PEER_WMM = 1<<2,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h
index 74d6ce5..6232088 100644
--- a/src/drivers/priv_netlink.h
+++ b/src/drivers/priv_netlink.h
@@ -69,6 +69,7 @@
(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
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
index 9c41962..adfd3df 100644
--- a/src/eap_common/Makefile
+++ b/src/eap_common/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
index f5890be..4f14a01 100644
--- a/src/eap_common/eap_defs.h
+++ b/src/eap_common/eap_defs.h
@@ -72,13 +72,16 @@
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 */
+ EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance (moved to WBA) */,
+ EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */,
+ EAP_VENDOR_WFA_NEW = 40808 /* Wi-Fi Alliance */
};
#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP
#define EAP_VENDOR_TYPE_UNAUTH_TLS 1
+#define EAP_VENDOR_WFA_UNAUTH_TLS 13
+
#define EAP_MSK_LEN 64
#define EAP_EMSK_LEN 64
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index a62ac8e..4dfdb3f 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -692,7 +692,7 @@
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) {
+ if (os_memcmp_const(icv, prot + prot_len - icv_len, icv_len) != 0) {
wpa_printf(MSG_INFO, "EAP-EKE: ICV mismatch in Prot() data");
return -1;
}
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
index 04b987d..fceb1b0 100644
--- a/src/eap_common/eap_fast_common.c
+++ b/src/eap_common/eap_fast_common.c
@@ -174,7 +174,7 @@
int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
- int tlv_type, u8 *pos, int len)
+ int tlv_type, u8 *pos, size_t len)
{
switch (tlv_type) {
case EAP_TLV_EAP_PAYLOAD_TLV:
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
index 8955617..d59a845 100644
--- a/src/eap_common/eap_fast_common.h
+++ b/src/eap_common/eap_fast_common.h
@@ -102,6 +102,6 @@
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);
+ int tlv_type, u8 *pos, size_t len);
#endif /* EAP_FAST_H */
diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c
index 7a33215..8c7ae27 100644
--- a/src/eap_common/eap_gpsk_common.c
+++ b/src/eap_common/eap_gpsk_common.c
@@ -284,7 +284,6 @@
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)",
@@ -296,8 +295,7 @@
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);
+ seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
if (seed == NULL) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
"for key derivation");
@@ -313,17 +311,18 @@
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);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
switch (specifier) {
case EAP_GPSK_CIPHER_AES:
- ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
+ ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed,
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,
+ ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed,
+ pos - seed,
msk, emsk, sk, sk_len);
break;
#endif /* EAP_GPSK_SHA256 */
@@ -423,7 +422,6 @@
{
u8 *seed, *pos;
u8 kdf_out[16];
- size_t seed_len;
int ret;
wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
@@ -441,8 +439,7 @@
* 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);
+ seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
if (seed == NULL) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
"for Session-Id derivation");
@@ -458,11 +455,11 @@
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);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
ret = eap_gpsk_derive_mid_helper(specifier,
kdf_out, sizeof(kdf_out),
- psk, seed, seed_len,
+ psk, seed, pos - seed,
method_type);
sid[0] = method_type;
diff --git a/src/eap_common/eap_ikev2_common.c b/src/eap_common/eap_ikev2_common.c
index 6095fd8..585c79c 100644
--- a/src/eap_common/eap_ikev2_common.c
+++ b/src/eap_common/eap_ikev2_common.c
@@ -52,22 +52,12 @@
{
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");
@@ -110,7 +100,7 @@
return -1;
}
- if (os_memcmp(icv, end - icv_len, icv_len) != 0) {
+ if (os_memcmp_const(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);
diff --git a/src/eap_common/eap_ikev2_common.h b/src/eap_common/eap_ikev2_common.h
index 329ccc4..e7502d7 100644
--- a/src/eap_common/eap_ikev2_common.h
+++ b/src/eap_common/eap_ikev2_common.h
@@ -9,16 +9,9 @@
#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
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 7d6e6b8..fdcff7f 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -263,18 +263,18 @@
fail:
EC_GROUP_free(grp->group);
grp->group = NULL;
- EC_POINT_free(grp->pwe);
+ EC_POINT_clear_free(grp->pwe);
grp->pwe = NULL;
- BN_free(grp->order);
+ BN_clear_free(grp->order);
grp->order = NULL;
- BN_free(grp->prime);
+ BN_clear_free(grp->prime);
grp->prime = NULL;
ret = 1;
}
/* cleanliness and order.... */
- BN_free(cofactor);
- BN_free(x_candidate);
- BN_free(rnd);
+ BN_clear_free(cofactor);
+ BN_clear_free(x_candidate);
+ BN_clear_free(rnd);
os_free(prfbuf);
return ret;
@@ -284,11 +284,10 @@
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)
+ u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
{
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;
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index 816e58c..c54c441 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -59,7 +59,7 @@
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 *);
+ u8 *, u8 *, u32 *, u8 *, 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);
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
index e1773bf..2adc3b3 100644
--- a/src/eap_common/eap_sim_common.c
+++ b/src/eap_common/eap_sim_common.c
@@ -198,7 +198,7 @@
hmac, EAP_SIM_MAC_LEN);
os_free(tmp);
- return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+ return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
}
@@ -393,7 +393,7 @@
hmac, EAP_SIM_MAC_LEN);
os_free(tmp);
- return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+ return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
}
@@ -893,7 +893,7 @@
if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
"AT_KDF attributes - ignore this");
- continue;
+ break;
}
attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
attr->kdf_count++;
@@ -972,7 +972,6 @@
struct eap_sim_msg {
struct wpabuf *buf;
size_t mac, iv, encr; /* index from buf */
- int type;
};
@@ -986,7 +985,6 @@
if (msg == NULL)
return NULL;
- msg->type = type;
msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
if (msg->buf == NULL) {
os_free(msg);
@@ -1006,7 +1004,8 @@
}
-struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
+ const u8 *k_aut,
const u8 *extra, size_t extra_len)
{
struct eap_hdr *eap;
@@ -1019,7 +1018,7 @@
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) {
+ if (k_aut && msg->mac && 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) +
diff --git a/src/eap_common/eap_sim_common.h b/src/eap_common/eap_sim_common.h
index 6021bd2..daeb0e2 100644
--- a/src/eap_common/eap_sim_common.h
+++ b/src/eap_common/eap_sim_common.h
@@ -211,7 +211,8 @@
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,
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
+ 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,
diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c
index 376fcad..3d4fb6f 100644
--- a/src/eap_common/ikev2_common.c
+++ b/src/eap_common/ikev2_common.c
@@ -21,7 +21,7 @@
{ AUTH_HMAC_MD5_96, 16, 12 }
};
-#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0]))
+#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs)
static struct ikev2_prf_alg ikev2_prf_algs[] = {
@@ -29,7 +29,7 @@
{ PRF_HMAC_MD5, 16, 16 }
};
-#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0]))
+#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs)
static struct ikev2_encr_alg ikev2_encr_algs[] = {
@@ -37,7 +37,7 @@
{ ENCR_3DES, 24, 8 }
};
-#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0]))
+#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs)
const struct ikev2_integ_alg * ikev2_get_integ(int id)
@@ -173,46 +173,12 @@
}
-#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;
@@ -237,9 +203,6 @@
return -1;
}
crypto_cipher_deinit(cipher);
-#ifdef CCNS_PL
- }
-#endif /* CCNS_PL */
return 0;
}
@@ -251,31 +214,6 @@
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;
@@ -300,9 +238,6 @@
return -1;
}
crypto_cipher_deinit(cipher);
-#ifdef CCNS_PL
- }
-#endif /* CCNS_PL */
return 0;
}
@@ -542,7 +477,7 @@
"hash");
return NULL;
}
- if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) {
+ if (os_memcmp_const(integ, hash, integ_alg->hash_len) != 0) {
wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum "
"Data");
return NULL;
@@ -706,10 +641,6 @@
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;
diff --git a/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h
index 45c970b..8a7982a 100644
--- a/src/eap_common/ikev2_common.h
+++ b/src/eap_common/ikev2_common.h
@@ -70,11 +70,7 @@
/* 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 {
diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
index 3651056..f79519b 100644
--- a/src/eap_peer/Makefile
+++ b/src/eap_peer/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.so *.d
+ rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov
install:
if ls *.so >/dev/null 2>&1; then \
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 554e7e9..9880d3b 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1,6 +1,6 @@
/*
* EAP peer state machines (RFC 4137)
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -92,6 +92,15 @@
}
+static void eap_sm_free_key(struct eap_sm *sm)
+{
+ if (sm->eapKeyData) {
+ bin_clear_free(sm->eapKeyData, sm->eapKeyDataLen);
+ sm->eapKeyData = NULL;
+ }
+}
+
+
static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
{
ext_password_free(sm->ext_pw_buf);
@@ -144,11 +153,13 @@
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) {
+ !sm->prev_failure &&
+ sm->last_config == eap_get_config(sm)) {
wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
"fast reauthentication");
sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
} else {
+ sm->last_config = eap_get_config(sm);
eap_deinit_prev_method(sm, "INITIALIZE");
}
sm->selectedMethod = EAP_TYPE_NONE;
@@ -159,8 +170,7 @@
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;
+ eap_sm_free_key(sm);
os_free(sm->eapSessionId);
sm->eapSessionId = NULL;
sm->eapKeyAvailable = FALSE;
@@ -179,6 +189,7 @@
eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
sm->num_rounds = 0;
sm->prev_failure = 0;
+ sm->expected_failure = 0;
}
@@ -388,10 +399,11 @@
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",
+ "methodState=%s decision=%s eapRespData=%p",
ret.ignore ? "TRUE" : "FALSE",
eap_sm_method_state_txt(ret.methodState),
- eap_sm_decision_txt(ret.decision));
+ eap_sm_decision_txt(ret.decision),
+ sm->eapRespData);
sm->ignore = ret.ignore;
if (sm->ignore)
@@ -402,7 +414,7 @@
if (sm->m->isKeyAvailable && sm->m->getKey &&
sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
- os_free(sm->eapKeyData);
+ eap_sm_free_key(sm);
sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
&sm->eapKeyDataLen);
os_free(sm->eapSessionId);
@@ -432,8 +444,10 @@
sm->lastId = sm->reqId;
sm->lastRespData = wpabuf_dup(sm->eapRespData);
eapol_set_bool(sm, EAPOL_eapResp, TRUE);
- } else
+ } 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);
}
@@ -724,8 +738,19 @@
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;
@@ -1473,8 +1498,7 @@
sm->lastRespData = NULL;
wpabuf_free(sm->eapRespData);
sm->eapRespData = NULL;
- os_free(sm->eapKeyData);
- sm->eapKeyData = NULL;
+ eap_sm_free_key(sm);
os_free(sm->eapSessionId);
sm->eapSessionId = NULL;
@@ -1638,7 +1662,8 @@
const char *msg, size_t msglen)
{
struct eap_peer_config *config;
- char *txt = NULL, *tmp;
+ const char *txt = NULL;
+ char *tmp;
if (sm == NULL)
return;
@@ -1681,6 +1706,9 @@
case WPA_CTRL_REQ_EAP_PASSPHRASE:
config->pending_req_passphrase++;
break;
+ case WPA_CTRL_REQ_SIM:
+ txt = msg;
+ break;
default:
return;
}
@@ -1792,6 +1820,17 @@
/**
+ * 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()
*
@@ -2017,6 +2056,8 @@
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);
}
@@ -2304,6 +2345,17 @@
}
+/**
+ * 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()
@@ -2375,3 +2427,9 @@
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;
+}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index f87f9b3..712e929 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -296,6 +296,7 @@
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,
@@ -303,6 +304,7 @@
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);
@@ -318,6 +320,7 @@
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 */
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index dc424d7..0662ae7 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -42,7 +42,7 @@
u8 *last_eap_identity;
size_t last_eap_identity_len;
enum {
- CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+ CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
} state;
struct wpabuf *id_msgs;
@@ -64,8 +64,6 @@
return "CONTINUE";
case RESULT_SUCCESS:
return "RESULT_SUCCESS";
- case RESULT_FAILURE:
- return "RESULT_FAILURE";
case SUCCESS:
return "SUCCESS";
case FAILURE:
@@ -128,6 +126,21 @@
#endif /* EAP_AKA_PRIME */
+static void eap_aka_clear_keys(struct eap_aka_data *data, int reauth)
+{
+ if (!reauth) {
+ os_memset(data->mk, 0, EAP_SIM_MK_LEN);
+ os_memset(data->k_aut, 0, EAP_AKA_PRIME_K_AUT_LEN);
+ os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN);
+ os_memset(data->k_re, 0, EAP_AKA_PRIME_K_RE_LEN);
+ }
+ os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN);
+ os_memset(data->emsk, 0, EAP_EMSK_LEN);
+ os_memset(data->autn, 0, EAP_AKA_AUTN_LEN);
+ os_memset(data->auts, 0, EAP_AKA_AUTS_LEN);
+}
+
+
static void eap_aka_deinit(struct eap_sm *sm, void *priv)
{
struct eap_aka_data *data = priv;
@@ -137,11 +150,95 @@
os_free(data->last_eap_identity);
wpabuf_free(data->id_msgs);
os_free(data->network_name);
+ eap_aka_clear_keys(data, 0);
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, ":");
+ 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;
@@ -151,6 +248,14 @@
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,
@@ -205,7 +310,7 @@
{
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) {
+ if (os_memcmp_const(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match "
"with expected value");
return -1;
@@ -225,7 +330,7 @@
#else /* CONFIG_USIM_HARDCODED */
- wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith "
+ wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorithm "
"enabled");
return -1;
@@ -420,7 +525,7 @@
#endif /* EAP_AKA_PRIME */
sha1_vector(1, &addr, &len, hash);
- if (os_memcmp(hash, checkcode, hash_len) != 0) {
+ if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
return -1;
}
@@ -443,7 +548,7 @@
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);
+ return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
}
@@ -460,7 +565,7 @@
"(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);
+ return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
}
@@ -479,7 +584,7 @@
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);
+ return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
}
@@ -523,7 +628,7 @@
identity, identity_len);
}
- return eap_sim_msg_finish(msg, NULL, NULL, 0);
+ return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
}
@@ -545,7 +650,8 @@
}
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);
+ return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "",
+ 0);
}
@@ -587,7 +693,7 @@
}
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,
+ return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s,
EAP_SIM_NONCE_S_LEN);
}
@@ -621,7 +727,7 @@
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);
+ return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0);
}
@@ -701,7 +807,7 @@
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);
+ return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
}
@@ -861,6 +967,9 @@
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,
@@ -931,7 +1040,7 @@
if (data->result_ind && attr->result_ind)
data->use_result_ind = 1;
- if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ if (data->state != FAILURE) {
eap_aka_state(data, data->use_result_ind ?
RESULT_SUCCESS : SUCCESS);
}
@@ -1147,7 +1256,7 @@
if (data->result_ind && attr->result_ind)
data->use_result_ind = 1;
- if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ if (data->state != FAILURE) {
eap_aka_state(data, data->use_result_ind ?
RESULT_SUCCESS : SUCCESS);
}
@@ -1253,9 +1362,7 @@
*/
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)
+ } else if (data->state == RESULT_SUCCESS)
ret->methodState = METHOD_CONT;
if (ret->methodState == METHOD_DONE) {
@@ -1282,6 +1389,7 @@
data->id_msgs = NULL;
data->use_result_ind = 0;
data->kdf_negotiation = 0;
+ eap_aka_clear_keys(data, 1);
}
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 42f525b..2591e11 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -157,7 +157,7 @@
*
* If left out, this will be asked through control interface.
*/
- u8 *private_key_passwd;
+ char *private_key_passwd;
/**
* dh_file - File path to DH/DSA parameters file (in PEM format)
@@ -208,6 +208,24 @@
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
@@ -271,7 +289,7 @@
* This field is like private_key_passwd, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
- u8 *private_key2_passwd;
+ char *private_key2_passwd;
/**
* dh_file2 - File path to DH/DSA parameters file (in PEM format)
@@ -303,6 +321,14 @@
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
@@ -643,6 +669,23 @@
* 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;
+
+ /**
+ * sim_num - User selected SIM identifier
+ *
+ * This variable is used for identifying which SIM is used if the system
+ * has more than one.
+ */
+ int sim_num;
};
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index c71db5f..9fec66c 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -28,6 +28,10 @@
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 */
};
@@ -66,6 +70,7 @@
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) {
@@ -89,6 +94,39 @@
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;
}
@@ -100,7 +138,7 @@
os_free(data->serverid);
os_free(data->peerid);
wpabuf_free(data->msgs);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -226,16 +264,20 @@
i, pos[0], pos[1], pos[2], pos[3]);
pos += 4;
- if (!eap_eke_supp_dhgroup(*tmp))
+ if ((data->dhgroup && data->dhgroup != *tmp) ||
+ !eap_eke_supp_dhgroup(*tmp))
continue;
tmp++;
- if (!eap_eke_supp_encr(*tmp))
+ if ((data->encr && data->encr != *tmp) ||
+ !eap_eke_supp_encr(*tmp))
continue;
tmp++;
- if (!eap_eke_supp_prf(*tmp))
+ if ((data->prf && data->prf != *tmp) ||
+ !eap_eke_supp_prf(*tmp))
continue;
tmp++;
- if (!eap_eke_supp_mac(*tmp))
+ if ((data->mac && data->mac != *tmp) ||
+ !eap_eke_supp_mac(*tmp))
continue;
prop = tmp - 3;
@@ -409,7 +451,7 @@
/* 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");
+ wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P");
os_memset(key, 0, sizeof(key));
return eap_eke_build_fail(data, ret, reqData,
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
@@ -481,7 +523,7 @@
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");
+ wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
return eap_eke_build_fail(data, ret, reqData,
EAP_EKE_FAIL_PROTO_ERROR);
}
@@ -501,7 +543,7 @@
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");
+ wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P");
return eap_eke_build_fail(data, ret, reqData,
EAP_EKE_FAIL_AUTHENTICATION_FAIL);
}
@@ -524,8 +566,8 @@
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) {
+ if (os_memcmp_const(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);
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index 3b8d803..0739187 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -149,14 +149,16 @@
struct eap_fast_data *data;
struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+
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) {
+ if (config->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) {
eap_fast_deinit(sm, data);
return NULL;
}
@@ -196,14 +198,22 @@
"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;
}
@@ -240,6 +250,8 @@
pac = pac->next;
eap_fast_free_pac(prev);
}
+ os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
+ os_memset(data->emsk, 0, EAP_EMSK_LEN);
os_free(data->session_id);
wpabuf_free(data->pending_phase2_req);
os_free(data);
@@ -757,7 +769,7 @@
"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));
+ res = os_memcmp_const(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",
@@ -1047,6 +1059,7 @@
}
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
@@ -1069,7 +1082,8 @@
struct eap_fast_tlv_parse *tlv,
struct wpabuf **resp)
{
- int mandatory, tlv_type, len, res;
+ int mandatory, tlv_type, res;
+ size_t len;
u8 *pos, *end;
os_memset(tlv, 0, sizeof(*tlv));
@@ -1083,13 +1097,14 @@
pos += 2;
len = WPA_GET_BE16(pos);
pos += 2;
- if (pos + len > end) {
+ if (len > (size_t) (end - pos)) {
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)" : "");
+ "TLV type %d length %u%s",
+ tlv_type, (unsigned int) len,
+ mandatory ? " (mandatory)" : "");
res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
if (res == -2)
@@ -1244,6 +1259,7 @@
"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.");
@@ -1622,6 +1638,8 @@
os_free(data);
return NULL;
}
+ os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
+ os_memset(data->emsk, 0, EAP_EMSK_LEN);
os_free(data->session_id);
data->session_id = NULL;
if (data->phase2_priv && data->phase2_method &&
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index 8c480b9..21d6098 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -330,6 +330,8 @@
static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
char *pos)
{
+ if (!pos)
+ return "Cannot parse pac type";
pac->pac_type = atoi(pos);
if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
index 8a0644d..c54bf11 100644
--- a/src/eap_peer/eap_gpsk.c
+++ b/src/eap_peer/eap_gpsk.c
@@ -1,6 +1,6 @@
/*
* EAP peer method: EAP-GPSK (RFC 5433)
- * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -33,6 +33,7 @@
int specifier; /* CSuite/Specifier */
u8 *psk;
size_t psk_len;
+ u16 forced_cipher; /* force cipher or 0 to allow all supported */
};
@@ -80,6 +81,7 @@
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) {
@@ -103,6 +105,18 @@
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);
@@ -120,8 +134,11 @@
struct eap_gpsk_data *data = priv;
os_free(data->id_server);
os_free(data->id_peer);
- os_free(data->psk);
- os_free(data);
+ if (data->psk) {
+ os_memset(data->psk, 0, data->psk_len);
+ os_free(data->psk);
+ }
+ bin_clear_free(data, sizeof(*data));
}
@@ -195,7 +212,9 @@
i, vendor, specifier);
if (data->vendor == EAP_GPSK_VENDOR_IETF &&
data->specifier == EAP_GPSK_CIPHER_RESERVED &&
- eap_gpsk_supported_ciphersuite(vendor, specifier)) {
+ eap_gpsk_supported_ciphersuite(vendor, specifier) &&
+ (!data->forced_cipher || data->forced_cipher == specifier))
+ {
data->vendor = vendor;
data->specifier = specifier;
}
@@ -220,6 +239,8 @@
size_t *list_len,
const u8 *pos, const u8 *end)
{
+ size_t len;
+
if (pos == NULL)
return NULL;
@@ -227,23 +248,25 @@
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
return NULL;
}
- *list_len = WPA_GET_BE16(pos);
+ len = WPA_GET_BE16(pos);
pos += 2;
- if (end - pos < (int) *list_len) {
+ if (len > (size_t) (end - pos)) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
return NULL;
}
- if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
+ if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
- (unsigned long) *list_len);
+ (unsigned long) len);
return NULL;
}
- *list = pos;
- pos += *list_len;
- if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
+ if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
return NULL;
+ *list = pos;
+ *list_len = len;
+ pos += len;
+
return pos;
}
@@ -273,6 +296,7 @@
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;
}
@@ -544,7 +568,7 @@
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
return NULL;
}
- if (os_memcmp(mic, pos, miclen) != 0) {
+ if (os_memcmp_const(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);
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 62c867c..fde809c 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -345,9 +345,14 @@
struct wps_context *wps;
int prev_failure;
+ struct eap_peer_config *last_config;
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);
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
index 09a655e..c12b519 100644
--- a/src/eap_peer/eap_ikev2.c
+++ b/src/eap_peer/eap_ikev2.c
@@ -1,6 +1,6 @@
/*
* EAP-IKEv2 peer (RFC 5106)
- * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -60,6 +60,7 @@
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) {
@@ -71,7 +72,11 @@
if (data == NULL)
return NULL;
data->state = WAIT_START;
- data->fragment_size = IKEV2_FRAGMENT_SIZE;
+ 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");
@@ -108,7 +113,7 @@
wpabuf_free(data->in_buf);
wpabuf_free(data->out_buf);
ikev2_responder_deinit(&data->ikev2);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -149,12 +154,6 @@
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)
@@ -246,7 +245,8 @@
static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
const struct wpabuf *reqData,
- u8 flags, const u8 *pos, const u8 **end)
+ u8 flags, const u8 *pos, const u8 **end,
+ int frag_ack)
{
if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
int icv_len = eap_ikev2_validate_icv(
@@ -256,7 +256,7 @@
return -1;
/* Hide Integrity Checksum Data from further processing */
*end -= icv_len;
- } else if (data->keys_ready) {
+ } else if (data->keys_ready && !frag_ack) {
wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
"included integrity checksum");
return -1;
@@ -346,7 +346,9 @@
else
flags = *pos++;
- if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) {
+ if (eap_ikev2_process_icv(data, reqData, flags, pos, &end,
+ data->state == WAIT_FRAG_ACK && len == 0) < 0)
+ {
ret->ignore = TRUE;
return NULL;
}
@@ -373,12 +375,7 @@
"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 */
- {
+ if (len != 0) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
"in WAIT_FRAG_ACK state");
ret->ignore = TRUE;
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
index df34013..e0f8bcf 100644
--- a/src/eap_peer/eap_leap.c
+++ b/src/eap_peer/eap_leap.c
@@ -244,7 +244,7 @@
ret->methodState = METHOD_DONE;
ret->allowNotifications = FALSE;
- if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
+ if (os_memcmp_const(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",
@@ -383,6 +383,9 @@
wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
*len = LEAP_KEY_LEN;
+ os_memset(pw_hash, 0, sizeof(pw_hash));
+ os_memset(pw_hash_hash, 0, sizeof(pw_hash_hash));
+
return key;
}
diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
index a465fd2..e35c919 100644
--- a/src/eap_peer/eap_methods.h
+++ b/src/eap_peer/eap_methods.h
@@ -86,6 +86,7 @@
int eap_peer_md5_register(void);
int eap_peer_tls_register(void);
int eap_peer_unauth_tls_register(void);
+int eap_peer_wfa_unauth_tls_register(void);
int eap_peer_mschapv2_register(void);
int eap_peer_peap_register(void);
int eap_peer_ttls_register(void);
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index f9aa742..430c501 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -140,7 +140,7 @@
os_free(data->peer_challenge);
os_free(data->auth_challenge);
wpabuf_free(data->prev_challenge);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -303,18 +303,23 @@
WPA_EVENT_PASSWORD_CHANGED
"EAP-MSCHAPV2: Password changed successfully");
data->prev_error = 0;
- os_free(config->password);
+ bin_clear_free(config->password, config->password_len);
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);
+ if (config->password &&
+ nt_password_hash(config->new_password,
+ config->new_password_len,
+ config->password)) {
+ bin_clear_free(config->password,
+ config->password_len);
+ config->password = NULL;
+ config->password_len = 0;
}
- os_free(config->new_password);
+ bin_clear_free(config->new_password,
+ config->new_password_len);
} else {
config->password = config->new_password;
config->password_len = config->new_password_len;
@@ -549,15 +554,17 @@
/* Encrypted-Hash */
if (pwhash) {
u8 new_password_hash[16];
- nt_password_hash(new_password, new_password_len,
- new_password_hash);
+ if (nt_password_hash(new_password, new_password_len,
+ new_password_hash))
+ goto fail;
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);
+ if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ new_password, new_password_len,
+ password, password_len, cp->encr_hash))
+ goto fail;
}
/* Peer-Challenge */
@@ -594,9 +601,13 @@
/* 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);
+ if (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->auth_response_valid = 0;
+ goto fail;
+ }
data->master_key_valid = 1;
/* Flags */
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
index 7f87052..1c111c2 100644
--- a/src/eap_peer/eap_pax.c
+++ b/src/eap_peer/eap_pax.c
@@ -86,7 +86,7 @@
{
struct eap_pax_data *data = priv;
os_free(data->cid);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -278,7 +278,7 @@
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) {
+ if (os_memcmp_const(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)",
@@ -415,7 +415,7 @@
wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
icvbuf);
}
- if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
+ if (os_memcmp_const(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",
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 3b93209..472e861 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -22,7 +22,6 @@
/* 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
- * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
*/
#define EAP_PEAP_VERSION 1
@@ -171,6 +170,15 @@
}
+static void eap_peap_free_key(struct eap_peap_data *data)
+{
+ if (data->key_data) {
+ bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
+ data->key_data = NULL;
+ }
+}
+
+
static void eap_peap_deinit(struct eap_sm *sm, void *priv)
{
struct eap_peap_data *data = priv;
@@ -180,7 +188,7 @@
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);
+ eap_peap_free_key(data);
os_free(data->session_id);
wpabuf_free(data->pending_phase2_req);
os_free(data);
@@ -315,8 +323,6 @@
len[1] = 1;
tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
- if (data->peap_version >= 2)
- tlv_type |= EAP_TLV_TYPE_MANDATORY;
wpabuf_put_be16(buf, tlv_type);
wpabuf_put_be16(buf, 56);
@@ -426,7 +432,7 @@
buf, sizeof(buf));
hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
- if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+ if (os_memcmp_const(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",
@@ -580,33 +586,6 @@
}
-static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
-{
- struct wpabuf *e;
- struct eap_tlv_hdr *tlv;
-
- if (buf == NULL)
- return NULL;
-
- /* Encapsulate EAP packet in EAP-Payload TLV */
- wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
- e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
- if (e == NULL) {
- wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
- "for TLV encapsulation");
- wpabuf_free(buf);
- return NULL;
- }
- tlv = wpabuf_put(e, sizeof(*tlv));
- tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
- EAP_TLV_EAP_PAYLOAD_TLV);
- tlv->length = host_to_be16(wpabuf_len(buf));
- wpabuf_put_buf(e, buf);
- wpabuf_free(buf);
- return e;
-}
-
-
static int eap_peap_phase2_request(struct eap_sm *sm,
struct eap_peap_data *data,
struct eap_method_ret *ret,
@@ -837,49 +816,6 @@
in_decrypted = nmsg;
}
- if (data->peap_version >= 2) {
- struct eap_tlv_hdr *tlv;
- struct wpabuf *nmsg;
-
- if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
- "EAP TLV");
- wpabuf_free(in_decrypted);
- return 0;
- }
- tlv = wpabuf_mhead(in_decrypted);
- if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=
- EAP_TLV_EAP_PAYLOAD_TLV) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
- wpabuf_free(in_decrypted);
- return 0;
- }
- if (sizeof(*tlv) + be_to_host16(tlv->length) >
- wpabuf_len(in_decrypted)) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
- "length");
- wpabuf_free(in_decrypted);
- return 0;
- }
- hdr = (struct eap_hdr *) (tlv + 1);
- if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
- "EAP packet in EAP TLV");
- wpabuf_free(in_decrypted);
- return 0;
- }
-
- nmsg = wpabuf_alloc(be_to_host16(hdr->length));
- if (nmsg == NULL) {
- wpabuf_free(in_decrypted);
- return 0;
- }
-
- wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
- 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 "
@@ -996,11 +932,6 @@
wpa_hexdump_buf_key(MSG_DEBUG,
"EAP-PEAP: Encrypting Phase 2 data", resp);
/* PEAP version changes */
- if (data->peap_version >= 2) {
- resp = eap_peapv2_tlv_eap_payload(resp);
- if (resp == NULL)
- return -1;
- }
if (wpabuf_len(resp) >= 5 &&
wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
eap_get_type(resp) == EAP_TYPE_TLV)
@@ -1083,7 +1014,7 @@
char *label;
wpa_printf(MSG_DEBUG,
"EAP-PEAP: TLS done, proceed to Phase 2");
- os_free(data->key_data);
+ eap_peap_free_key(data);
/* draft-josefsson-ppext-eap-tls-eap-05.txt
* specifies that PEAPv1 would use "client PEAP
* encryption" as the label. However, most existing
@@ -1091,7 +1022,7 @@
* label, "client EAP encryption", instead. Use the old
* label by default, but allow it to be configured with
* phase1 parameter peaplabel=1. */
- if (data->peap_version > 1 || data->force_new_label)
+ if (data->force_new_label)
label = "client PEAP encryption";
else
label = "client EAP encryption";
@@ -1193,8 +1124,7 @@
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;
+ eap_peap_free_key(data);
os_free(data->session_id);
data->session_id = NULL;
if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h
index 3b4dcef..23cdbe6 100644
--- a/src/eap_peer/eap_proxy.h
+++ b/src/eap_peer/eap_proxy.h
@@ -40,7 +40,8 @@
int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
int verbose);
-int eap_proxy_get_imsi(char *imsi_buf, size_t *imsi_len);
+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);
diff --git a/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c
index cd97fb6..d84f012 100644
--- a/src/eap_peer/eap_proxy_dummy.c
+++ b/src/eap_peer/eap_proxy_dummy.c
@@ -63,7 +63,8 @@
}
-int eap_proxy_get_imsi(char *imsi_buf, size_t *imsi_len)
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
+ size_t *imsi_len)
{
return -1;
}
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
index cd0e3f9..f012663 100644
--- a/src/eap_peer/eap_psk.c
+++ b/src/eap_peer/eap_psk.c
@@ -76,7 +76,7 @@
struct eap_psk_data *data = priv;
os_free(data->id_s);
os_free(data->id_p);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -237,7 +237,7 @@
return NULL;
}
os_free(buf);
- if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
+ if (os_memcmp_const(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;
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 267d0a5..1c915ed 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -16,7 +16,8 @@
struct eap_pwd_data {
enum {
- PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
+ PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req,
+ SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE
} state;
u8 *id_peer;
size_t id_peer_len;
@@ -42,6 +43,7 @@
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
+ u8 session_id[1 + SHA256_MAC_LEN];
BN_CTX *bnctx;
};
@@ -57,6 +59,8 @@
return "PWD-Commit-Req";
case PWD_Confirm_Req:
return "PWD-Confirm-Req";
+ case SUCCESS_ON_FRAG_COMPLETION:
+ return "SUCCESS_ON_FRAG_COMPLETION";
case SUCCESS:
return "SUCCESS";
case FAILURE:
@@ -81,6 +85,7 @@
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) {
@@ -118,7 +123,7 @@
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);
+ bin_clear_free(data->id_peer, data->id_peer_len);
os_free(data);
return NULL;
}
@@ -127,7 +132,11 @@
data->out_frag_pos = data->in_frag_pos = 0;
data->inbuf = data->outbuf = NULL;
- data->mtu = 1020; /* default from RFC 5931, make it configurable! */
+ 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;
@@ -139,24 +148,26 @@
{
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_clear_free(data->private_value);
+ BN_clear_free(data->server_scalar);
+ BN_clear_free(data->my_scalar);
+ BN_clear_free(data->k);
BN_CTX_free(data->bnctx);
- EC_POINT_free(data->my_element);
- EC_POINT_free(data->server_element);
- os_free(data->id_peer);
- os_free(data->id_server);
- os_free(data->password);
+ EC_POINT_clear_free(data->my_element);
+ EC_POINT_clear_free(data->server_element);
+ bin_clear_free(data->id_peer, data->id_peer_len);
+ bin_clear_free(data->id_server, data->id_server_len);
+ bin_clear_free(data->password, data->password_len);
if (data->grp) {
EC_GROUP_free(data->grp->group);
- EC_POINT_free(data->grp->pwe);
- BN_free(data->grp->order);
- BN_free(data->grp->prime);
+ EC_POINT_clear_free(data->grp->pwe);
+ BN_clear_free(data->grp->order);
+ BN_clear_free(data->grp->prime);
os_free(data->grp);
}
- os_free(data);
+ wpabuf_free(data->inbuf);
+ wpabuf_free(data->outbuf);
+ bin_clear_free(data, sizeof(*data));
}
@@ -179,6 +190,25 @@
}
+static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *id;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ id = os_malloc(1 + SHA256_MAC_LEN);
+ if (id == NULL)
+ return NULL;
+
+ os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
+ *len = 1 + SHA256_MAC_LEN;
+
+ return id;
+}
+
+
static void
eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
struct eap_method_ret *ret,
@@ -222,8 +252,8 @@
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) {
+ data->grp = os_zalloc(sizeof(EAP_PWD_group));
+ if (data->grp == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
"group");
eap_pwd_state(data, FAILURE);
@@ -287,11 +317,15 @@
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 (BN_rand_range(data->private_value, data->grp->order) != 1 ||
+ BN_rand_range(mask, data->grp->order) != 1 ||
+ BN_add(data->my_scalar, data->private_value, mask) != 1 ||
+ BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+ data->bnctx) != 1) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd (peer): unable to get randomness");
+ goto fin;
+ }
if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
data->grp->pwe, mask, data->bnctx)) {
@@ -306,7 +340,7 @@
wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
goto fin;
}
- BN_free(mask);
+ BN_clear_free(mask);
if (((x = BN_new()) == NULL) ||
((y = BN_new()) == NULL)) {
@@ -441,11 +475,11 @@
fin:
os_free(scalar);
os_free(element);
- BN_free(x);
- BN_free(y);
- BN_free(cofactor);
- EC_POINT_free(K);
- EC_POINT_free(point);
+ BN_clear_free(x);
+ BN_clear_free(y);
+ BN_clear_free(cofactor);
+ EC_POINT_clear_free(K);
+ EC_POINT_clear_free(point);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
else
@@ -559,7 +593,7 @@
eap_pwd_h_final(hash, conf);
ptr = (u8 *) payload;
- if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
+ if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
goto fin;
}
@@ -637,7 +671,7 @@
if (compute_keys(data->grp, data->bnctx, data->k,
data->my_scalar, data->server_scalar, conf, ptr,
- &cs, data->msk, data->emsk) < 0) {
+ &cs, data->msk, data->emsk, data->session_id) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
"EMSK");
goto fin;
@@ -650,16 +684,15 @@
wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
fin:
- os_free(cruft);
- BN_free(x);
- BN_free(y);
- ret->methodState = METHOD_DONE;
+ bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+ BN_clear_free(x);
+ BN_clear_free(y);
if (data->outbuf == NULL) {
+ ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
eap_pwd_state(data, FAILURE);
} else {
- ret->decision = DECISION_UNCOND_SUCC;
- eap_pwd_state(data, SUCCESS);
+ eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
}
}
@@ -736,6 +769,11 @@
wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
data->out_frag_pos == 0 ? "last" : "next",
(int) len);
+ if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ eap_pwd_state(data, SUCCESS);
+ }
return resp;
}
@@ -748,6 +786,8 @@
tot_len = WPA_GET_BE16(pos);
wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose "
"total length = %d", tot_len);
+ if (tot_len > 15000)
+ return NULL;
data->inbuf = wpabuf_alloc(tot_len);
if (data->inbuf == NULL) {
wpa_printf(MSG_INFO, "Out of memory to buffer "
@@ -768,6 +808,7 @@
(int) data->in_frag_pos,
(int) wpabuf_len(data->inbuf));
wpabuf_free(data->inbuf);
+ data->inbuf = NULL;
data->in_frag_pos = 0;
return NULL;
}
@@ -819,11 +860,15 @@
*/
if (data->in_frag_pos) {
wpabuf_free(data->inbuf);
+ data->inbuf = NULL;
data->in_frag_pos = 0;
}
- if (data->outbuf == NULL)
+ if (data->outbuf == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
return NULL; /* generic failure */
+ }
/*
* we have output! Do we need to fragment it?
@@ -866,6 +911,11 @@
wpabuf_free(data->outbuf);
data->outbuf = NULL;
data->out_frag_pos = 0;
+ if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ eap_pwd_state(data, SUCCESS);
+ }
}
return resp;
@@ -913,6 +963,7 @@
eap->process = eap_pwd_process;
eap->isKeyAvailable = eap_pwd_key_available;
eap->getKey = eap_pwd_getkey;
+ eap->getSessionId = eap_pwd_get_session_id;
eap->get_emsk = eap_pwd_get_emsk;
ret = eap_peer_method_register(eap);
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 431519c..7d14907 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -108,7 +108,7 @@
struct eap_sake_data *data = priv;
os_free(data->serverid);
os_free(data->peerid);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -315,7 +315,7 @@
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) {
+ if (os_memcmp_const(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;
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 82ea18d..bd06df7 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -43,7 +43,7 @@
u8 *last_eap_identity;
size_t last_eap_identity_len;
enum {
- CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+ CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
} state;
int result_ind, use_result_ind;
};
@@ -57,8 +57,6 @@
return "CONTINUE";
case RESULT_SUCCESS:
return "RESULT_SUCCESS";
- case RESULT_FAILURE:
- return "RESULT_FAILURE";
case SUCCESS:
return "SUCCESS";
case FAILURE:
@@ -132,6 +130,20 @@
}
+static void eap_sim_clear_keys(struct eap_sim_data *data, int reauth)
+{
+ if (!reauth) {
+ os_memset(data->mk, 0, EAP_SIM_MK_LEN);
+ os_memset(data->k_aut, 0, EAP_SIM_K_AUT_LEN);
+ os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN);
+ }
+ os_memset(data->kc, 0, 3 * EAP_SIM_KC_LEN);
+ os_memset(data->sres, 0, 3 * EAP_SIM_SRES_LEN);
+ os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN);
+ os_memset(data->emsk, 0, EAP_EMSK_LEN);
+}
+
+
static void eap_sim_deinit(struct eap_sm *sm, void *priv)
{
struct eap_sim_data *data = priv;
@@ -140,11 +152,86 @@
os_free(data->pseudonym);
os_free(data->reauth_id);
os_free(data->last_eap_identity);
+ eap_sim_clear_keys(data, 0);
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;
@@ -154,6 +241,14 @@
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]) ||
@@ -369,7 +464,7 @@
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);
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
}
@@ -422,7 +517,7 @@
identity, identity_len);
}
- return eap_sim_msg_finish(msg, NULL, NULL, 0);
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
}
@@ -440,7 +535,8 @@
}
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,
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
+ (u8 *) data->sres,
data->num_chal * EAP_SIM_SRES_LEN);
}
@@ -482,7 +578,7 @@
}
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,
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, nonce_s,
EAP_SIM_NONCE_S_LEN);
}
@@ -516,7 +612,7 @@
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);
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, k_aut, (u8 *) "", 0);
}
@@ -605,6 +701,7 @@
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;
@@ -648,8 +745,13 @@
os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
data->num_chal = attr->num_chal;
-
- if (eap_sim_gsm_auth(sm, data)) {
+
+ 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);
@@ -700,7 +802,7 @@
if (data->result_ind && attr->result_ind)
data->use_result_ind = 1;
- if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ if (data->state != FAILURE) {
eap_sim_state(data, data->use_result_ind ?
RESULT_SUCCESS : SUCCESS);
}
@@ -864,9 +966,11 @@
}
if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+ struct wpabuf *res;
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
@@ -877,8 +981,11 @@
data->last_eap_identity_len = data->reauth_id_len;
data->reauth_id = NULL;
data->reauth_id_len = 0;
+
+ res = eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
os_free(decrypted);
- return eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
+
+ return res;
}
data->counter = eattr.counter;
@@ -896,7 +1003,7 @@
if (data->result_ind && attr->result_ind)
data->use_result_ind = 1;
- if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ if (data->state != FAILURE) {
eap_sim_state(data, data->use_result_ind ?
RESULT_SUCCESS : SUCCESS);
}
@@ -995,9 +1102,7 @@
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)
+ } else if (data->state == RESULT_SUCCESS)
ret->methodState = METHOD_CONT;
if (ret->methodState == METHOD_DONE) {
@@ -1020,6 +1125,7 @@
struct eap_sim_data *data = priv;
eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
data->use_result_ind = 0;
+ eap_sim_clear_keys(data, 1);
}
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index d2066cd..5aa3fd5 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -98,13 +98,49 @@
#endif /* EAP_UNAUTH_TLS */
+#ifdef CONFIG_HS20
+static void * eap_wfa_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_WFA_UNAUTH_TLS_TYPE)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
+
+ return data;
+}
+#endif /* CONFIG_HS20 */
+
+
+static void eap_tls_free_key(struct eap_tls_data *data)
+{
+ if (data->key_data) {
+ bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+ data->key_data = NULL;
+ }
+}
+
+
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);
+ eap_tls_free_key(data);
os_free(data->session_id);
os_free(data);
}
@@ -154,7 +190,7 @@
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
- os_free(data->key_data);
+ eap_tls_free_key(data);
data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
"client EAP encryption",
EAP_TLS_KEY_LEN +
@@ -240,8 +276,7 @@
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;
+ eap_tls_free_key(data);
os_free(data->session_id);
data->session_id = NULL;
if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
@@ -382,3 +417,35 @@
return ret;
}
#endif /* EAP_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+int eap_peer_wfa_unauth_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS,
+ "WFA-UNAUTH-TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_wfa_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 /* CONFIG_HS20 */
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index be8c301..fe9bfe0 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -23,6 +23,10 @@
return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
code, identifier);
+ if (type == EAP_WFA_UNAUTH_TLS_TYPE)
+ return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
+ code, identifier);
return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
identifier);
}
@@ -64,6 +68,14 @@
params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
if (os_strstr(txt, "tls_disable_session_ticket=0"))
params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
+ if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
+ params->flags |= TLS_CONN_DISABLE_TLSv1_1;
+ if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
+ params->flags &= ~TLS_CONN_DISABLE_TLSv1_1;
+ if (os_strstr(txt, "tls_disable_tlsv1_2=1"))
+ params->flags |= TLS_CONN_DISABLE_TLSv1_2;
+ if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
+ params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
}
@@ -78,6 +90,7 @@
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;
@@ -99,6 +112,7 @@
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;
@@ -836,6 +850,10 @@
pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
&left);
+ else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+ pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS, reqData,
+ &left);
else
pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
&left);
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 1a5e0f8..390c216 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -87,6 +87,7 @@
/* dummy type used as a flag for UNAUTH-TLS */
#define EAP_UNAUTH_TLS_TYPE 255
+#define EAP_WFA_UNAUTH_TLS_TYPE 254
int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c
index bc13647..25b9f12 100644
--- a/src/eap_peer/eap_tnc.c
+++ b/src/eap_peer/eap_tnc.c
@@ -243,7 +243,8 @@
message_length = WPA_GET_BE32(pos);
pos += 4;
- if (message_length < (u32) (end - pos)) {
+ if (message_length < (u32) (end - pos) ||
+ message_length > 75000) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
"Length (%d; %ld remaining in this msg)",
message_length, (long) (end - pos));
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 5091bf0..771da58 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -133,6 +133,15 @@
}
+static void eap_ttls_free_key(struct eap_ttls_data *data)
+{
+ if (data->key_data) {
+ bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
+ data->key_data = NULL;
+ }
+}
+
+
static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
{
struct eap_ttls_data *data = priv;
@@ -141,7 +150,7 @@
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);
+ eap_ttls_free_key(data);
os_free(data->session_id);
wpabuf_free(data->pending_phase2_req);
os_free(data);
@@ -213,7 +222,7 @@
static int eap_ttls_v0_derive_key(struct eap_sm *sm,
struct eap_ttls_data *data)
{
- os_free(data->key_data);
+ eap_ttls_free_key(data);
data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
"ttls keying material",
EAP_TLS_KEY_LEN);
@@ -492,16 +501,6 @@
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");
@@ -1540,8 +1539,7 @@
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;
+ eap_ttls_free_key(data);
os_free(data->session_id);
data->session_id = NULL;
if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 8edb1ca..23e9823 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -106,8 +106,10 @@
}
if (os_strncmp(pos + 9, "NONE", 4) == 0)
cred->encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
else if (os_strncmp(pos + 9, "WEP", 3) == 0)
cred->encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
cred->encr_type = WPS_ENCR_TKIP;
else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
@@ -144,12 +146,13 @@
size_t identity_len;
int registrar;
struct wps_config cfg;
- const char *pos;
+ 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) {
@@ -209,6 +212,15 @@
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");
@@ -216,9 +228,23 @@
return NULL;
}
- pos = os_strstr(phase1, "dev_pw_id=");
- if (pos && cfg.pin)
- cfg.dev_pw_id = atoi(pos + 10);
+ 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) {
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index fcf4712..8186afb 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -72,27 +72,10 @@
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);
@@ -179,21 +162,12 @@
"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 "
@@ -456,14 +430,6 @@
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",
@@ -599,7 +565,7 @@
return -1;
if (auth_len != prf->hash_len ||
- os_memcmp(auth, auth_data, auth_len) != 0) {
+ os_memcmp_const(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);
@@ -887,16 +853,7 @@
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;
@@ -906,11 +863,7 @@
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;
@@ -1082,11 +1035,7 @@
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);
@@ -1130,13 +1079,6 @@
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);
@@ -1257,6 +1199,7 @@
wpabuf_free(msg);
return NULL;
}
+ wpabuf_free(plain);
data->state = IKEV2_FAILED;
} else {
/* HDR, N */
diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c
index 37e6735..9bc7370 100644
--- a/src/eap_peer/mschapv2.c
+++ b/src/eap_peer/mschapv2.c
@@ -117,8 +117,8 @@
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)
+ os_memcmp_const(auth_response, recv_response,
+ MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
return -1;
return 0;
}
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index a3ec395..7ca956e 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -13,6 +13,7 @@
#include "common.h"
#include "base64.h"
+#include "common/tnc.h"
#include "tncc.h"
#include "eap_common/eap_tlv_common.h"
#include "eap_common/eap_defs.h"
@@ -25,7 +26,9 @@
#endif /* UNICODE */
+#ifndef TNC_CONFIG_FILE
#define TNC_CONFIG_FILE "/etc/tnc_config"
+#endif /* TNC_CONFIG_FILE */
#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
#define IF_TNCCS_START \
"<?xml version=\"1.0\"?>\n" \
@@ -38,56 +41,6 @@
/* 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,
@@ -1139,8 +1092,10 @@
int error = 0;
imc = tncc_parse_imc(pos + 4, line_end, &error);
- if (error)
+ if (error) {
+ os_free(config);
return -1;
+ }
if (imc) {
if (last == NULL)
tncc->imc = imc;
diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile
index 9c41962..adfd3df 100644
--- a/src/eap_server/Makefile
+++ b/src/eap_server/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 36b230b..1253bd6 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -32,8 +32,11 @@
* nt_password_hash() */
int phase2;
int force_version;
+ unsigned int remediation:1;
+ unsigned int macacl:1;
int ttls_auth; /* bitfield of
* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
+ struct hostapd_radius_attr *accept_attr;
};
struct eap_eapol_interface {
@@ -79,6 +82,7 @@
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);
+ void (*log_msg)(void *ctx, const char *msg);
};
struct eap_config {
@@ -107,6 +111,10 @@
const u8 *server_id;
size_t server_id_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ u32 tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
};
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 003e202..3a6802b 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -191,10 +191,16 @@
const u8 *server_id;
size_t server_id_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ u32 tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
};
int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
int phase2);
+void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len);
#endif /* EAP_I_H */
diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h
index 429cb72..0baa327 100644
--- a/src/eap_server/eap_methods.h
+++ b/src/eap_server/eap_methods.h
@@ -27,6 +27,7 @@
int eap_server_md5_register(void);
int eap_server_tls_register(void);
int eap_server_unauth_tls_register(void);
+int eap_server_wfa_unauth_tls_register(void);
int eap_server_mschapv2_register(void);
int eap_server_peap_register(void);
int eap_server_tlv_register(void);
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 54b7533..c1bb6b8 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -119,6 +119,32 @@
}
+void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+
+ if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL)
+ return;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ va_start(ap, fmt);
+ vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+
+ sm->eapol_cb->log_msg(sm->eapol_ctx, buf);
+
+ os_free(buf);
+}
+
+
SM_STATE(EAP, DISABLED)
{
SM_ENTRY(EAP, DISABLED);
@@ -142,7 +168,7 @@
sm->eap_if.eapSuccess = FALSE;
sm->eap_if.eapFail = FALSE;
sm->eap_if.eapTimeout = FALSE;
- os_free(sm->eap_if.eapKeyData);
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
sm->eap_if.eapKeyData = NULL;
sm->eap_if.eapKeyDataLen = 0;
sm->eap_if.eapKeyAvailable = FALSE;
@@ -320,7 +346,7 @@
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);
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
if (sm->m->getKey) {
sm->eap_if.eapKeyData = sm->m->getKey(
sm, sm->eap_method_priv,
@@ -343,6 +369,7 @@
SM_ENTRY(EAP, PROPOSE_METHOD);
+try_another_method:
type = eap_sm_Policy_getNextMethod(sm, &vendor);
if (vendor == EAP_VENDOR_IETF)
sm->currentMethod = type;
@@ -360,8 +387,15 @@
"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");
+ eap_log_msg(sm, "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;
@@ -370,6 +404,8 @@
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
"vendor=%u method=%u", vendor, sm->currentMethod);
+ eap_log_msg(sm, "Propose EAP method vendor=%u method=%u",
+ vendor, sm->currentMethod);
}
@@ -596,7 +632,7 @@
if (sm->eap_if.aaaEapKeyAvailable) {
EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
} else {
- os_free(sm->eap_if.eapKeyData);
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
sm->eap_if.eapKeyData = NULL;
sm->eap_if.eapKeyDataLen = 0;
}
@@ -686,6 +722,7 @@
"respMethod=%d currentMethod=%d",
sm->rxResp, sm->respId, sm->currentId,
sm->respMethod, sm->currentMethod);
+ eap_log_msg(sm, "Discard received EAP message");
SM_ENTER(EAP, DISCARD);
}
break;
@@ -702,6 +739,15 @@
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:
@@ -1214,7 +1260,7 @@
{
if (user == NULL)
return;
- os_free(user->password);
+ bin_clear_free(user->password, user->password_len);
user->password = NULL;
os_free(user);
}
@@ -1281,6 +1327,10 @@
sm->server_id = conf->server_id;
sm->server_id_len = conf->server_id_len;
+#ifdef CONFIG_TESTING_OPTIONS
+ sm->tls_test_flags = conf->tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
+
wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
return sm;
@@ -1302,7 +1352,7 @@
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);
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
wpabuf_free(sm->lastReqData);
wpabuf_free(sm->eap_if.eapRespData);
os_free(sm->identity);
@@ -1311,7 +1361,7 @@
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);
+ bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen);
eap_user_free(sm->user);
wpabuf_free(sm->assoc_wps_ie);
wpabuf_free(sm->assoc_p2p_ie);
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 469b9a0..09b976e 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -241,7 +241,7 @@
os_free(data->next_reauth_id);
wpabuf_free(data->id_msgs);
os_free(data->network_name);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -336,7 +336,7 @@
else
sha1_vector(1, &addr, &len, hash);
- if (os_memcmp(hash, checkcode, hash_len) != 0) {
+ if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
return -1;
}
@@ -377,7 +377,7 @@
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);
+ buf = eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
if (eap_aka_add_id_msg(data, buf) < 0) {
wpabuf_free(buf);
return NULL;
@@ -534,7 +534,7 @@
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);
+ return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
}
@@ -581,7 +581,7 @@
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);
+ return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
}
@@ -620,7 +620,7 @@
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);
+ return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
}
@@ -963,7 +963,7 @@
*/
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) {
+ os_memcmp_const(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)",
@@ -1040,6 +1040,7 @@
data->auts_reported = 1;
/* Remain in CHALLENGE state to re-try after resynchronization */
+ eap_aka_fullauth(sm, data);
}
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index b19a321..966f511 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -104,7 +104,7 @@
eap_eke_session_clean(&data->sess);
os_free(data->peerid);
wpabuf_free(data->msgs);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -635,8 +635,8 @@
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) {
+ if (os_memcmp_const(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;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index fcb80dc..4691e72 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -187,7 +187,7 @@
switch (*pos) {
case PAC_OPAQUE_TYPE_PAD:
pos = end;
- break;
+ goto done;
case PAC_OPAQUE_TYPE_KEY:
if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
@@ -218,6 +218,7 @@
pos += 2 + pos[1];
}
+done:
if (pac_key == NULL) {
wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in "
@@ -511,7 +512,7 @@
os_free(data->key_block_p);
wpabuf_free(data->pending_phase2_resp);
os_free(data->identity);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -1123,7 +1124,8 @@
static int eap_fast_parse_tlvs(struct wpabuf *data,
struct eap_fast_tlv_parse *tlv)
{
- int mandatory, tlv_type, len, res;
+ int mandatory, tlv_type, res;
+ size_t len;
u8 *pos, *end;
os_memset(tlv, 0, sizeof(*tlv));
@@ -1136,13 +1138,14 @@
pos += 2;
len = WPA_GET_BE16(pos);
pos += 2;
- if (pos + len > end) {
+ if (len > (size_t) (end - pos)) {
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)" : "");
+ "TLV type %d length %u%s",
+ tlv_type, (unsigned int) len,
+ mandatory ? " (mandatory)" : "");
res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
if (res == -2)
@@ -1196,7 +1199,7 @@
return -1;
}
- if (os_memcmp(data->crypto_binding_nonce, b->nonce, 31) != 0 ||
+ if (os_memcmp_const(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");
@@ -1210,7 +1213,7 @@
(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) {
+ if (os_memcmp_const(cmac, b->compound_mac, sizeof(cmac)) != 0) {
wpa_hexdump(MSG_MSGDUMP,
"EAP-FAST: Calculated Compound MAC",
b->compound_mac, sizeof(cmac));
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index 66f4271..cb369e4 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -95,7 +95,7 @@
{
struct eap_gpsk_data *data = priv;
os_free(data->id_peer);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -433,7 +433,7 @@
eap_gpsk_state(data, FAILURE);
return;
}
- if (os_memcmp(mic, pos, miclen) != 0) {
+ if (os_memcmp_const(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);
@@ -502,7 +502,7 @@
eap_gpsk_state(data, FAILURE);
return;
}
- if (os_memcmp(mic, pos, miclen) != 0) {
+ if (os_memcmp_const(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);
diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c
index f423106..98ac3c6 100644
--- a/src/eap_server/eap_server_gtc.c
+++ b/src/eap_server/eap_server_gtc.c
@@ -175,7 +175,7 @@
}
if (rlen != sm->user->password_len ||
- os_memcmp(pos, sm->user->password, rlen) != 0) {
+ os_memcmp_const(pos, sm->user->password, rlen) != 0) {
wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
data->state = FAILURE;
} else {
diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c
index 51dc4e8..4501533 100644
--- a/src/eap_server/eap_server_identity.c
+++ b/src/eap_server/eap_server_identity.c
@@ -102,6 +102,7 @@
struct eap_identity_data *data = priv;
const u8 *pos;
size_t len;
+ char *buf;
if (data->pick_up) {
if (eap_identity_check(sm, data, respData)) {
@@ -119,6 +120,12 @@
return; /* Should not happen - frame already validated */
wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len);
+ buf = os_malloc(len * 4 + 1);
+ if (buf) {
+ printf_encode(buf, len * 4 + 1, pos, len);
+ eap_log_msg(sm, "EAP-Response/Identity '%s'", buf);
+ os_free(buf);
+ }
if (sm->identity)
sm->update_user = TRUE;
os_free(sm->identity);
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 1ada0c8..65b2ef6 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -127,7 +127,7 @@
wpabuf_free(data->in_buf);
wpabuf_free(data->out_buf);
ikev2_initiator_deinit(&data->ikev2);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -256,7 +256,8 @@
static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
const struct wpabuf *respData,
- u8 flags, const u8 *pos, const u8 **end)
+ u8 flags, const u8 *pos, const u8 **end,
+ int frag_ack)
{
if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
int icv_len = eap_ikev2_validate_icv(
@@ -266,7 +267,7 @@
return -1;
/* Hide Integrity Checksum Data from further processing */
*end -= icv_len;
- } else if (data->keys_ready) {
+ } else if (data->keys_ready && !frag_ack) {
wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
"included integrity checksum");
return -1;
@@ -365,7 +366,9 @@
} else
flags = *pos++;
- if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) {
+ if (eap_ikev2_process_icv(data, respData, flags, pos, &end,
+ data->state == WAIT_FRAG_ACK && len == 0) < 0)
+ {
eap_ikev2_state(data, FAIL);
return;
}
diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c
index 5a5e290..71e8d59 100644
--- a/src/eap_server/eap_server_md5.c
+++ b/src/eap_server/eap_server_md5.c
@@ -126,7 +126,7 @@
return;
}
- if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) {
+ if (os_memcmp_const(hash, pos, CHAP_MD5_LEN) == 0) {
wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
data->state = SUCCESS;
} else {
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 3153d2e..f7a753d 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -91,7 +91,7 @@
return;
os_free(data->peer_challenge);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -290,6 +290,7 @@
const u8 *username, *user;
size_t username_len, user_len;
int res;
+ char *buf;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
&len);
@@ -329,6 +330,13 @@
wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
+ buf = os_malloc(name_len * 4 + 1);
+ if (buf) {
+ printf_encode(buf, name_len * 4 + 1, name, name_len);
+ eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
+ os_free(buf);
+ }
+
/* MSCHAPv2 does not include optional domain name in the
* challenge-response calculation, so remove domain prefix
* (if present). */
@@ -385,7 +393,7 @@
return;
}
- if (os_memcmp(nt_response, expected, 24) == 0) {
+ if (os_memcmp_const(nt_response, expected, 24) == 0) {
const u8 *pw_hash;
u8 pw_hash_buf[16], pw_hash_hash[16];
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 35a42ad..c87848c 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -64,7 +64,7 @@
{
struct eap_pax_data *data = priv;
os_free(data->cid);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -268,7 +268,7 @@
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) {
+ if (os_memcmp_const(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);
@@ -395,7 +395,7 @@
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) {
+ if (os_memcmp_const(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)",
@@ -417,7 +417,7 @@
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) {
+ if (os_memcmp_const(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);
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 68253c4..594e02d 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -22,7 +22,6 @@
/* 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
- * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
*/
#define EAP_PEAP_VERSION 1
@@ -99,33 +98,6 @@
}
-static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
-{
- struct wpabuf *e;
- struct eap_tlv_hdr *tlv;
-
- if (buf == NULL)
- return NULL;
-
- /* Encapsulate EAP packet in EAP-Payload TLV */
- wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
- e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
- if (e == NULL) {
- wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
- "for TLV encapsulation");
- wpabuf_free(buf);
- return NULL;
- }
- tlv = wpabuf_put(e, sizeof(*tlv));
- tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
- EAP_TLV_EAP_PAYLOAD_TLV);
- tlv->length = host_to_be16(wpabuf_len(buf));
- wpabuf_put_buf(e, buf);
- wpabuf_free(buf);
- return e;
-}
-
-
static void eap_peap_req_success(struct eap_sm *sm,
struct eap_peap_data *data)
{
@@ -200,7 +172,7 @@
wpabuf_free(data->pending_phase2_resp);
os_free(data->phase2_key);
wpabuf_free(data->soh_response);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -239,8 +211,6 @@
return NULL;
}
buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
- if (data->peap_version >= 2 && buf)
- buf = eap_peapv2_tlv_eap_payload(buf);
if (buf == NULL)
return NULL;
@@ -425,8 +395,6 @@
len[1] = 1;
tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
- if (data->peap_version >= 2)
- tlv_type |= EAP_TLV_TYPE_MANDATORY;
wpabuf_put_be16(buf, tlv_type);
wpabuf_put_be16(buf, 56);
@@ -505,8 +473,7 @@
return eap_peap_build_start(sm, data, id);
case PHASE1:
case PHASE1_ID2:
- if (data->peap_version < 2 &&
- tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ 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);
@@ -626,7 +593,7 @@
buf[60] = EAP_TYPE_PEAP;
hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
- if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+ if (os_memcmp_const(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);
@@ -1079,47 +1046,6 @@
wpabuf_free(in_decrypted);
in_decrypted = nbuf;
- } else if (data->peap_version >= 2) {
- struct eap_tlv_hdr *tlv;
- struct wpabuf *nmsg;
-
- if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
- "EAP TLV");
- wpabuf_free(in_decrypted);
- return;
- }
- tlv = wpabuf_mhead(in_decrypted);
- if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) !=
- EAP_TLV_EAP_PAYLOAD_TLV) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
- wpabuf_free(in_decrypted);
- return;
- }
- if (sizeof(*tlv) + be_to_host16(tlv->length) >
- wpabuf_len(in_decrypted)) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
- "length");
- wpabuf_free(in_decrypted);
- return;
- }
- hdr = (struct eap_hdr *) (tlv + 1);
- if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
- "EAP packet in EAP TLV");
- wpabuf_free(in_decrypted);
- return;
- }
-
- nmsg = wpabuf_alloc(be_to_host16(hdr->length));
- if (nmsg == NULL) {
- wpabuf_free(in_decrypted);
- return;
- }
-
- wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
- wpabuf_free(in_decrypted);
- in_decrypted = nmsg;
}
hdr = wpabuf_head(in_decrypted);
@@ -1168,53 +1094,6 @@
}
-static int eap_peapv2_start_phase2(struct eap_sm *sm,
- struct eap_peap_data *data)
-{
- struct wpabuf *buf, *buf2;
-
- wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 "
- "payload in the same message");
- eap_peap_state(data, PHASE1_ID2);
- if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY))
- return -1;
-
- /* TODO: which Id to use here? */
- buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6);
- if (buf == NULL)
- return -1;
-
- buf2 = eap_peapv2_tlv_eap_payload(buf);
- if (buf2 == NULL)
- return -1;
-
- wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2);
-
- buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
- buf2);
- wpabuf_free(buf2);
-
- if (buf == NULL) {
- wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 "
- "data");
- return -1;
- }
-
- wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request",
- buf);
-
- /* Append TLS data into the pending buffer after the Server Finished */
- if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) {
- wpabuf_free(buf);
- return -1;
- }
- wpabuf_put_buf(data->ssl.tls_out, buf);
- wpabuf_free(buf);
-
- return 0;
-}
-
-
static int eap_peap_process_version(struct eap_sm *sm, void *priv,
int peer_version)
{
@@ -1249,14 +1128,6 @@
eap_peap_state(data, FAILURE);
break;
}
-
- if (data->peap_version >= 2 &&
- tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
- if (eap_peapv2_start_phase2(sm, data)) {
- eap_peap_state(data, FAILURE);
- break;
- }
- }
break;
case PHASE2_START:
eap_peap_state(data, PHASE2_ID);
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
index 46bedd9..db394e9 100644
--- a/src/eap_server/eap_server_psk.c
+++ b/src/eap_server/eap_server_psk.c
@@ -47,7 +47,7 @@
{
struct eap_psk_data *data = priv;
os_free(data->id_p);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -314,7 +314,7 @@
}
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) {
+ if (os_memcmp_const(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);
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index b61061b..7e1278d 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -45,6 +45,7 @@
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
+ u8 session_id[1 + SHA256_MAC_LEN];
BN_CTX *bnctx;
};
@@ -105,7 +106,7 @@
if (data->password == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password "
"fail");
- os_free(data->id_server);
+ bin_clear_free(data->id_server, data->id_server_len);
os_free(data);
return NULL;
}
@@ -115,15 +116,16 @@
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);
+ bin_clear_free(data->password, data->password_len);
+ bin_clear_free(data->id_server, data->id_server_len);
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! */
+ /* use default MTU from RFC 5931 if not configured otherwise */
+ data->mtu = sm->fragment_size > 0 ? sm->fragment_size : 1020;
return data;
}
@@ -133,24 +135,26 @@
{
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_clear_free(data->private_value);
+ BN_clear_free(data->peer_scalar);
+ BN_clear_free(data->my_scalar);
+ BN_clear_free(data->k);
BN_CTX_free(data->bnctx);
- EC_POINT_free(data->my_element);
- EC_POINT_free(data->peer_element);
- os_free(data->id_peer);
- os_free(data->id_server);
- os_free(data->password);
+ EC_POINT_clear_free(data->my_element);
+ EC_POINT_clear_free(data->peer_element);
+ bin_clear_free(data->id_peer, data->id_peer_len);
+ bin_clear_free(data->id_server, data->id_server_len);
+ bin_clear_free(data->password, data->password_len);
if (data->grp) {
EC_GROUP_free(data->grp->group);
- EC_POINT_free(data->grp->pwe);
- BN_free(data->grp->order);
- BN_free(data->grp->prime);
+ EC_POINT_clear_free(data->grp->pwe);
+ BN_clear_free(data->grp->order);
+ BN_clear_free(data->grp->prime);
os_free(data->grp);
}
- os_free(data);
+ wpabuf_free(data->inbuf);
+ wpabuf_free(data->outbuf);
+ bin_clear_free(data, sizeof(*data));
}
@@ -206,11 +210,15 @@
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 (BN_rand_range(data->private_value, data->grp->order) != 1 ||
+ BN_rand_range(mask, data->grp->order) != 1 ||
+ BN_add(data->my_scalar, data->private_value, mask) != 1 ||
+ BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+ data->bnctx) != 1) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd (server): unable to get randomness");
+ goto fin;
+ }
if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
data->grp->pwe, mask, data->bnctx)) {
@@ -226,7 +234,7 @@
"fail");
goto fin;
}
- BN_free(mask);
+ BN_clear_free(mask);
if (((x = BN_new()) == NULL) ||
((y = BN_new()) == NULL)) {
@@ -278,8 +286,8 @@
fin:
os_free(scalar);
os_free(element);
- BN_free(x);
- BN_free(y);
+ BN_clear_free(x);
+ BN_clear_free(y);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
}
@@ -402,9 +410,9 @@
wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
fin:
- os_free(cruft);
- BN_free(x);
- BN_free(y);
+ bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+ BN_clear_free(x);
+ BN_clear_free(y);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
}
@@ -523,6 +531,7 @@
*/
if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
wpabuf_free(data->outbuf);
+ data->outbuf = NULL;
data->out_frag_pos = 0;
}
@@ -595,7 +604,8 @@
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) {
+ data->grp = os_zalloc(sizeof(EAP_PWD_group));
+ if (data->grp == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
"group");
return;
@@ -718,11 +728,11 @@
res = 1;
fin:
- EC_POINT_free(K);
- EC_POINT_free(point);
- BN_free(cofactor);
- BN_free(x);
- BN_free(y);
+ EC_POINT_clear_free(K);
+ EC_POINT_clear_free(point);
+ BN_clear_free(cofactor);
+ BN_clear_free(x);
+ BN_clear_free(y);
if (res)
eap_pwd_state(data, PWD_Confirm_Req);
@@ -829,7 +839,7 @@
eap_pwd_h_final(hash, conf);
ptr = (u8 *) payload;
- if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
+ if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
"verify");
goto fin;
@@ -838,15 +848,16 @@
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)
+ data->my_confirm, &cs, data->msk, data->emsk,
+ data->session_id) < 0)
eap_pwd_state(data, FAILURE);
else
eap_pwd_state(data, SUCCESS);
fin:
- os_free(cruft);
- BN_free(x);
- BN_free(y);
+ bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+ BN_clear_free(x);
+ BN_clear_free(y);
}
@@ -893,6 +904,8 @@
tot_len = WPA_GET_BE16(pos);
wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total "
"length = %d", tot_len);
+ if (tot_len > 15000)
+ return;
data->inbuf = wpabuf_alloc(tot_len);
if (data->inbuf == NULL) {
wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to "
@@ -949,6 +962,7 @@
*/
if (data->in_frag_pos) {
wpabuf_free(data->inbuf);
+ data->inbuf = NULL;
data->in_frag_pos = 0;
}
}
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 68dd76b..1937621 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -83,7 +83,7 @@
{
struct eap_sake_data *data = priv;
os_free(data->peerid);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -351,7 +351,7 @@
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) {
+ if (os_memcmp_const(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;
@@ -388,7 +388,7 @@
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) {
+ if (os_memcmp_const(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
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index b531241..23ee2b6 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -94,7 +94,7 @@
struct eap_sim_data *data = priv;
os_free(data->next_pseudonym);
os_free(data->next_reauth_id);
- os_free(data);
+ bin_clear_free(data, sizeof(*data));
}
@@ -140,7 +140,7 @@
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);
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
}
@@ -240,8 +240,8 @@
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);
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
+ data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
}
@@ -278,7 +278,7 @@
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);
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
}
@@ -317,7 +317,7 @@
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);
+ return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
}
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 447f47c..6bed62f 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -94,6 +94,28 @@
#endif /* EAP_SERVER_UNAUTH_TLS */
+#ifdef CONFIG_HS20
+static void * eap_wfa_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_WFA_UNAUTH_TLS_TYPE;
+ return data;
+}
+#endif /* CONFIG_HS20 */
+
+
static void eap_tls_reset(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
@@ -178,6 +200,10 @@
pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
&len);
+ else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+ pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS, respData,
+ &len);
else
pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
respData, &len);
@@ -340,3 +366,34 @@
return ret;
}
#endif /* EAP_SERVER_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+int eap_server_wfa_unauth_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS,
+ "WFA-UNAUTH-TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_wfa_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 /* CONFIG_HS20 */
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 9efb5b2..01853e6 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -25,14 +25,32 @@
return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
code, identifier);
+ else if (type == EAP_WFA_UNAUTH_TLS_TYPE)
+ return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
+ code, identifier);
return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
identifier);
}
+#ifdef CONFIG_TLS_INTERNAL
+static void eap_server_tls_log_cb(void *ctx, const char *msg)
+{
+ struct eap_sm *sm = ctx;
+ eap_log_msg(sm, "TLS: %s", msg);
+}
+#endif /* CONFIG_TLS_INTERNAL */
+
+
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;
@@ -43,6 +61,13 @@
return -1;
}
+#ifdef CONFIG_TLS_INTERNAL
+ tls_connection_set_log_cb(data->conn, eap_server_tls_log_cb, sm);
+#ifdef CONFIG_TESTING_OPTIONS
+ tls_connection_set_test_flags(data->conn, sm->tls_test_flags);
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_TLS_INTERNAL */
+
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");
@@ -388,6 +413,10 @@
pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
&left);
+ else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+ pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS, respData,
+ &left);
else
pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData,
&left);
diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c
index 67a3dfa..21bd26f 100644
--- a/src/eap_server/eap_server_tnc.c
+++ b/src/eap_server/eap_server_tnc.c
@@ -480,7 +480,8 @@
message_length = WPA_GET_BE32(pos);
pos += 4;
- if (message_length < (u32) (end - pos)) {
+ if (message_length < (u32) (end - pos) ||
+ message_length > 75000) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
"Length (%d; %ld remaining in this msg)",
message_length, (long) (end - pos));
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index 647bd2f..31e3871 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -336,7 +336,7 @@
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);
+ bin_clear_free(data, sizeof(*data));
}
@@ -509,8 +509,8 @@
}
if (sm->user->password_len != user_password_len ||
- os_memcmp(sm->user->password, user_password, user_password_len) !=
- 0) {
+ os_memcmp_const(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;
@@ -558,7 +558,8 @@
return;
}
- if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 ||
+ if (os_memcmp_const(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);
@@ -571,7 +572,8 @@
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) {
+ if (os_memcmp_const(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 {
@@ -616,7 +618,8 @@
return;
}
- if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 ||
+ if (os_memcmp_const(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);
@@ -631,7 +634,7 @@
nt_challenge_response(challenge, sm->user->password,
sm->user->password_len, nt_response);
- if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) {
+ if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
eap_ttls_state(data, SUCCESS);
} else {
@@ -703,7 +706,8 @@
return;
}
- if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 ||
+ if (os_memcmp_const(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);
@@ -736,7 +740,7 @@
}
rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
- if (os_memcmp(nt_response, rx_resp, 24) == 0) {
+ if (os_memcmp_const(nt_response, rx_resp, 24) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
"NT-Response");
data->mschapv2_resp_ok = 1;
@@ -984,6 +988,16 @@
}
if (parse.user_name) {
+ char *nbuf;
+ nbuf = os_malloc(parse.user_name_len * 4 + 1);
+ if (nbuf) {
+ printf_encode(nbuf, parse.user_name_len * 4 + 1,
+ parse.user_name,
+ parse.user_name_len);
+ eap_log_msg(sm, "TTLS-User-Name '%s'", nbuf);
+ os_free(nbuf);
+ }
+
os_free(sm->identity);
sm->identity = os_malloc(parse.user_name_len);
if (sm->identity == NULL) {
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index 1b9d701..bc2cbe5 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -38,7 +38,6 @@
char imsi[20];
enum { PENDING, SUCCESS, FAILURE } state;
void *cb_session_ctx;
- struct os_time timestamp;
int aka;
union {
struct {
@@ -630,7 +629,7 @@
data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if (data->sock < 0) {
- perror("socket(eap_sim_db)");
+ wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno));
return -1;
}
@@ -640,8 +639,13 @@
"/tmp/eap_sim_db_%d-%d", getpid(), counter++);
os_free(data->local_sock);
data->local_sock = os_strdup(addr.sun_path);
+ if (data->local_sock == NULL) {
+ close(data->sock);
+ data->sock = -1;
+ return -1;
+ }
if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- perror("bind(eap_sim_db)");
+ wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno));
close(data->sock);
data->sock = -1;
return -1;
@@ -651,12 +655,16 @@
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) {
- perror("connect(eap_sim_db)");
+ 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;
+ unlink(data->local_sock);
+ os_free(data->local_sock);
+ data->local_sock = NULL;
return -1;
}
@@ -804,7 +812,8 @@
if (send(data->sock, msg, len, 0) < 0) {
_errno = errno;
- perror("send[EAP-SIM DB UNIX]");
+ wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
+ strerror(errno));
}
if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
@@ -816,7 +825,8 @@
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
"external server");
if (send(data->sock, msg, len, 0) < 0) {
- perror("send[EAP-SIM DB UNIX]");
+ wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
+ strerror(errno));
return -1;
}
}
@@ -932,7 +942,6 @@
if (entry == NULL)
return EAP_SIM_DB_FAILURE;
- os_get_time(&entry->timestamp);
os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
entry->cb_session_ctx = cb_session_ctx;
entry->state = PENDING;
@@ -1392,7 +1401,6 @@
if (entry == NULL)
return EAP_SIM_DB_FAILURE;
- os_get_time(&entry->timestamp);
entry->aka = 1;
os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
entry->cb_session_ctx = cb_session_ctx;
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index 11f5827..91449af 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -64,6 +64,7 @@
/* dummy type used as a flag for UNAUTH-TLS */
#define EAP_UNAUTH_TLS_TYPE 255
+#define EAP_WFA_UNAUTH_TLS_TYPE 254
struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
index 512ba30..632598f 100644
--- a/src/eap_server/ikev2.c
+++ b/src/eap_server/ikev2.c
@@ -633,7 +633,7 @@
return -1;
if (auth_len != prf->hash_len ||
- os_memcmp(auth, auth_data, auth_len) != 0) {
+ os_memcmp_const(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);
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
index e429f1e..dc6f689 100644
--- a/src/eap_server/tncs.c
+++ b/src/eap_server/tncs.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "base64.h"
+#include "common/tnc.h"
#include "tncs.h"
#include "eap_common/eap_tlv_common.h"
#include "eap_common/eap_defs.h"
@@ -19,7 +20,9 @@
/* TODO: TNCS must be thread-safe; review the code and add locking etc. if
* needed.. */
+#ifndef TNC_CONFIG_FILE
#define TNC_CONFIG_FILE "/etc/tnc_config"
+#endif /* TNC_CONFIG_FILE */
#define IF_TNCCS_START \
"<?xml version=\"1.0\"?>\n" \
"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
@@ -31,75 +34,6 @@
/* 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;
@@ -1181,6 +1115,9 @@
{
struct tnc_if_imv *imv;
+ if (tncs_global_data)
+ return 0;
+
tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
if (tncs_global_data == NULL)
return -1;
diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile
index 9c41962..adfd3df 100644
--- a/src/eapol_auth/Makefile
+++ b/src/eapol_auth/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/eapol_auth/eapol_auth_dump.c b/src/eapol_auth/eapol_auth_dump.c
index b6e0b13..6c6969b 100644
--- a/src/eapol_auth/eapol_auth_dump.c
+++ b/src/eapol_auth/eapol_auth_dump.c
@@ -1,6 +1,6 @@
/*
* IEEE 802.1X-2004 Authenticator - State dump
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -118,108 +118,172 @@
}
-void eapol_auth_dump_state(FILE *f, const char *prefix,
- struct eapol_state_machine *sm)
+int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
+ size_t buflen)
{
- fprintf(f, "%sEAPOL state machine:\n", prefix);
- fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix,
- sm->aWhile, sm->quietWhile, sm->reAuthWhen);
+ 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")
- fprintf(f,
- "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n"
- "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n"
- "%s eapSuccess=%s eapTimeout=%s initialize=%s "
- "keyAvailable=%s\n"
- "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n"
- "%s portEnabled=%s portValid=%s reAuthenticate=%s\n",
- prefix, _SB(sm->authAbort), _SB(sm->authFail),
- port_state_txt(sm->authPortStatus), _SB(sm->authStart),
- prefix, _SB(sm->authTimeout), _SB(sm->authSuccess),
- _SB(sm->eap_if->eapFail), _SB(sm->eapolEap),
- prefix, _SB(sm->eap_if->eapSuccess),
- _SB(sm->eap_if->eapTimeout),
- _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable),
- prefix, _SB(sm->keyDone), _SB(sm->keyRun),
- _SB(sm->keyTxEnabled), port_type_txt(sm->portControl),
- prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid),
- _SB(sm->reAuthenticate));
+ 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;
- fprintf(f, "%s Authenticator PAE:\n"
- "%s state=%s\n"
- "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n"
- "%s portMode=%s reAuthCount=%d\n"
- "%s quietPeriod=%d reAuthMax=%d\n"
- "%s authEntersConnecting=%d\n"
- "%s authEapLogoffsWhileConnecting=%d\n"
- "%s authEntersAuthenticating=%d\n"
- "%s authAuthSuccessesWhileAuthenticating=%d\n"
- "%s authAuthTimeoutsWhileAuthenticating=%d\n"
- "%s authAuthFailWhileAuthenticating=%d\n"
- "%s authAuthEapStartsWhileAuthenticating=%d\n"
- "%s authAuthEapLogoffWhileAuthenticating=%d\n"
- "%s authAuthReauthsWhileAuthenticated=%d\n"
- "%s authAuthEapStartsWhileAuthenticated=%d\n"
- "%s authAuthEapLogoffWhileAuthenticated=%d\n",
- prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix,
- _SB(sm->eapolLogoff), _SB(sm->eapolStart),
- _SB(sm->eap_if->eapRestart),
- prefix, port_type_txt(sm->portMode), sm->reAuthCount,
- prefix, sm->quietPeriod, sm->reAuthMax,
- prefix, sm->authEntersConnecting,
- prefix, sm->authEapLogoffsWhileConnecting,
- prefix, sm->authEntersAuthenticating,
- prefix, sm->authAuthSuccessesWhileAuthenticating,
- prefix, sm->authAuthTimeoutsWhileAuthenticating,
- prefix, sm->authAuthFailWhileAuthenticating,
- prefix, sm->authAuthEapStartsWhileAuthenticating,
- prefix, sm->authAuthEapLogoffWhileAuthenticating,
- prefix, sm->authAuthReauthsWhileAuthenticated,
- prefix, sm->authAuthEapStartsWhileAuthenticated,
- prefix, sm->authAuthEapLogoffWhileAuthenticated);
+ 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;
- fprintf(f, "%s Backend Authentication:\n"
- "%s state=%s\n"
- "%s eapNoReq=%s eapReq=%s eapResp=%s\n"
- "%s serverTimeout=%d\n"
- "%s backendResponses=%d\n"
- "%s backendAccessChallenges=%d\n"
- "%s backendOtherRequestsToSupplicant=%d\n"
- "%s backendAuthSuccesses=%d\n"
- "%s backendAuthFails=%d\n",
- prefix, prefix,
- be_auth_state_txt(sm->be_auth_state),
- prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq),
- _SB(sm->eap_if->eapResp),
- prefix, sm->serverTimeout,
- prefix, sm->backendResponses,
- prefix, sm->backendAccessChallenges,
- prefix, sm->backendOtherRequestsToSupplicant,
- prefix, sm->backendAuthSuccesses,
- prefix, sm->backendAuthFails);
+ 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;
- fprintf(f, "%s Reauthentication Timer:\n"
- "%s state=%s\n"
- "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix,
- reauth_timer_state_txt(sm->reauth_timer_state), prefix,
- sm->reAuthPeriod, _SB(sm->reAuthEnabled));
+ 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;
- fprintf(f, "%s Authenticator Key Transmit:\n"
- "%s state=%s\n", prefix, prefix,
- auth_key_tx_state_txt(sm->auth_key_tx_state));
+ 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;
- fprintf(f, "%s Key Receive:\n"
- "%s state=%s\n"
- "%s rxKey=%s\n", prefix, prefix,
- key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey));
+ 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;
- fprintf(f, "%s Controlled Directions:\n"
- "%s state=%s\n"
- "%s adminControlledDirections=%s "
- "operControlledDirections=%s\n"
- "%s operEdge=%s\n", prefix, prefix,
- ctrl_dir_state_txt(sm->ctrl_dir_state),
- prefix, ctrl_dir_txt(sm->adminControlledDirections),
- ctrl_dir_txt(sm->operControlledDirections),
- prefix, _SB(sm->operEdge));
+ 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;
}
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index 013d781..a76fa13 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -219,7 +219,8 @@
sm->eapolLogoff = FALSE;
if (!from_initialize) {
sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
- sm->flags & EAPOL_SM_PREAUTH);
+ sm->flags & EAPOL_SM_PREAUTH,
+ sm->remediation);
}
}
@@ -276,7 +277,7 @@
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->flags & EAPOL_SM_PREAUTH, sm->remediation);
}
@@ -302,7 +303,7 @@
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->flags & EAPOL_SM_PREAUTH, sm->remediation);
}
@@ -1001,8 +1002,13 @@
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);
+ int ret;
+
+ ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
+ identity_len, phase2, user);
+ if (user->remediation)
+ sm->remediation = 1;
+ return ret;
}
@@ -1017,7 +1023,8 @@
static struct eapol_callbacks eapol_cb =
{
eapol_sm_get_eap_user,
- eapol_sm_get_eap_req_id_text
+ eapol_sm_get_eap_req_id_text,
+ NULL
};
@@ -1062,6 +1069,10 @@
}
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
@@ -1070,6 +1081,7 @@
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,
@@ -1081,6 +1093,7 @@
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;
}
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index 3a0f450..320a0ad 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -60,7 +60,8 @@
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);
+ void (*finished)(void *ctx, void *sta_ctx, int success, int preauth,
+ int remediation);
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);
@@ -83,8 +84,8 @@
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);
-void eapol_auth_dump_state(FILE *f, const char *prefix,
- 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 */
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index d7f893a..25baddb 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -173,6 +173,8 @@
struct eapol_authenticator *eapol;
void *sta; /* station context pointer to use in callbacks */
+
+ int remediation;
};
#endif /* EAPOL_AUTH_SM_I_H */
diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile
index 9c41962..adfd3df 100644
--- a/src/eapol_supp/Makefile
+++ b/src/eapol_supp/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 9b054fc..cf3506d 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -137,6 +137,9 @@
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;
@@ -210,7 +213,6 @@
SM_ENTRY(SUPP_PAE, LOGOFF);
eapol_sm_txLogoff(sm);
sm->logoffSent = TRUE;
- sm->suppPortStatus = Unauthorized;
eapol_sm_set_port_unauthorized(sm);
}
@@ -221,7 +223,6 @@
sm->sPortMode = Auto;
sm->startCount = 0;
sm->logoffSent = FALSE;
- sm->suppPortStatus = Unauthorized;
eapol_sm_set_port_unauthorized(sm);
sm->suppAbort = TRUE;
@@ -286,7 +287,6 @@
SM_ENTRY(SUPP_PAE, HELD);
sm->heldWhile = sm->heldPeriod;
eapol_enable_timer_tick(sm);
- sm->suppPortStatus = Unauthorized;
eapol_sm_set_port_unauthorized(sm);
sm->cb_status = EAPOL_CB_FAILURE;
}
@@ -295,7 +295,6 @@
SM_STATE(SUPP_PAE, AUTHENTICATED)
{
SM_ENTRY(SUPP_PAE, AUTHENTICATED);
- sm->suppPortStatus = Authorized;
eapol_sm_set_port_authorized(sm);
sm->cb_status = EAPOL_CB_SUCCESS;
}
@@ -311,7 +310,6 @@
SM_STATE(SUPP_PAE, S_FORCE_AUTH)
{
SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
- sm->suppPortStatus = Authorized;
eapol_sm_set_port_authorized(sm);
sm->sPortMode = ForceAuthorized;
}
@@ -320,7 +318,6 @@
SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
{
SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
- sm->suppPortStatus = Unauthorized;
eapol_sm_set_port_unauthorized(sm);
sm->sPortMode = ForceUnauthorized;
eapol_sm_txLogoff(sm);
@@ -722,8 +719,8 @@
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) {
+ if (os_memcmp_const(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,
@@ -879,14 +876,24 @@
static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
{
- if (sm->ctx->port_cb)
+ 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)
{
- if (sm->ctx->port_cb)
+ 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);
}
@@ -933,9 +940,15 @@
}
if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
- int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
+ 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, success, sm->ctx->cb_ctx);
+ sm->ctx->cb(sm, result, sm->ctx->cb_ctx);
}
}
@@ -1257,6 +1270,24 @@
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
@@ -1314,6 +1345,13 @@
eapol_sm_step(sm);
}
break;
+#ifdef CONFIG_MACSEC
+ case IEEE802_1X_TYPE_EAPOL_MKA:
+ wpa_printf(MSG_EXCESSIVE,
+ "EAPOL type %d will be handled by MKA",
+ hdr->type);
+ break;
+#endif /* CONFIG_MACSEC */
default:
wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
hdr->type);
@@ -1352,6 +1390,8 @@
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);
}
@@ -1461,6 +1501,7 @@
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);
}
}
@@ -1482,7 +1523,7 @@
size_t eap_len;
#ifdef CONFIG_EAP_PROXY
- if (sm->use_eap_proxy) {
+ if (sm && 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");
@@ -1523,6 +1564,24 @@
/**
+ * eapol_sm_get_session_id - Get EAP Session-Id
+ * @sm: Pointer to EAPOL state machine allocated with eapol_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
+ *
+ * The Session-Id is available only after a successful authentication.
+ */
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+ if (sm == NULL || !eap_key_available(sm->eap)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: EAP Session-Id not available");
+ return NULL;
+ }
+ return eap_get_eapSessionId(sm->eap, len);
+}
+
+
+/**
* 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
@@ -1589,7 +1648,6 @@
return;
sm->cached_pmk = FALSE;
sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
- sm->suppPortStatus = Unauthorized;
eapol_sm_set_port_unauthorized(sm);
/* Make sure we do not start sending EAPOL-Start frames first, but
@@ -1983,6 +2041,7 @@
#endif /* CONFIG_EAP_PROXY */
/* Initialize EAPOL state machines */
+ sm->force_authorized_update = TRUE;
sm->initialize = TRUE;
eapol_sm_step(sm);
sm->initialize = FALSE;
@@ -2032,3 +2091,15 @@
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 */
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index c4b87da..d76c8c2 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -53,11 +53,22 @@
* 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
*/
@@ -78,7 +89,7 @@
/**
* cb - Function to be called when EAPOL negotiation has been completed
* @eapol: Pointer to EAPOL state machine data
- * @success: Whether the authentication was completed successfully
+ * @result: Whether the authentication was completed successfully
* @ctx: Pointer to context data (cb_ctx)
*
* This optional callback function will be called when the EAPOL
@@ -86,7 +97,8 @@
* 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, int success, void *ctx);
+ void (*cb)(struct eapol_sm *eapol, enum eapol_supp_result result,
+ void *ctx);
/**
* cb_ctx - Callback context for cb()
@@ -273,6 +285,7 @@
struct eap_peer_config *config,
const struct eapol_config *conf);
int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len);
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, 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);
@@ -287,6 +300,7 @@
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)
{
diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile
index 9c41962..adfd3df 100644
--- a/src/l2_packet/Makefile
+++ b/src/l2_packet/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c
index b01e830..6896c4e 100644
--- a/src/l2_packet/l2_packet_none.c
+++ b/src/l2_packet/l2_packet_none.c
@@ -84,7 +84,8 @@
* TODO: open connection for receiving frames
*/
l2->fd = -1;
- eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+ if (l2->fd >= 0)
+ eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
return l2;
}
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
index cffba62..adfd3df 100644
--- a/src/p2p/Makefile
+++ b/src/p2p/Makefile
@@ -2,8 +2,7 @@
@echo Nothing to be made.
clean:
- for d in $(SUBDIRS); do make -C $$d clean; done
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index fa6a849..667b96b 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -12,6 +12,7 @@
#include "eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
#include "p2p.h"
@@ -41,51 +42,32 @@
* P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
* entries will be removed
*/
-#ifdef ANDROID_P2P
-#define P2P_PEER_EXPIRATION_AGE 30
-#else
-#define P2P_PEER_EXPIRATION_AGE 300
-#endif
+#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)
-#ifdef ANDROID_P2P
-int p2p_connection_in_progress(struct p2p_data *p2p)
-{
- int ret = 0;
-
- switch (p2p->state) {
- case P2P_CONNECT:
- case P2P_CONNECT_LISTEN:
- case P2P_GO_NEG:
- case P2P_WAIT_PEER_CONNECT:
- case P2P_WAIT_PEER_IDLE:
- case P2P_PROVISIONING:
- case P2P_INVITE:
- case P2P_INVITE_LISTEN:
- ret = 1;
- break;
-
- default:
- wpa_printf(MSG_DEBUG, "p2p_connection_in_progress state %d", p2p->state);
- ret = 0;
- }
-
- return ret;
-}
-#endif
-
static void p2p_expire_peers(struct p2p_data *p2p)
{
struct p2p_device *dev, *n;
- struct os_time now;
+ struct os_reltime now;
size_t i;
- os_get_time(&now);
+ 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)) {
@@ -93,7 +75,7 @@
* 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_time(&dev->last_seen);
+ os_get_reltime(&dev->last_seen);
continue;
}
@@ -107,25 +89,12 @@
* The peer is connected as a client in a group where
* we are the GO, so do not expire the peer entry.
*/
- os_get_time(&dev->last_seen);
+ os_get_reltime(&dev->last_seen);
continue;
}
-#ifdef ANDROID_P2P
- /* If Connection is in progress, don't expire the peer
- */
- if (p2p_connection_in_progress(p2p))
- continue;
-#endif
-
p2p_dbg(p2p, "Expiring old peer entry " MACSTR,
MAC2STR(dev->info.p2p_device_addr));
-
-#ifdef ANDROID_P2P
- /* SD_FAIR_POLICY: Update the current sd_dev_list pointer to next device */
- if(&dev->list == p2p->sd_dev_list)
- p2p->sd_dev_list = dev->list.next;
-#endif
dl_list_del(&dev->list);
p2p_device_free(p2p, dev);
}
@@ -170,10 +139,6 @@
return "INVITE";
case P2P_INVITE_LISTEN:
return "INVITE_LISTEN";
- case P2P_SEARCH_WHEN_READY:
- return "SEARCH_WHEN_READY";
- case P2P_CONTINUE_SEARCH_WHEN_READY:
- return "CONTINUE_SEARCH_WHEN_READY";
default:
return "?";
}
@@ -219,6 +184,14 @@
p2p_dbg(p2p, "State %s -> %s",
p2p_state_txt(p2p->state), p2p_state_txt(new_state));
p2p->state = new_state;
+
+ if (new_state == P2P_IDLE && p2p->pending_channel) {
+ p2p_dbg(p2p, "Apply change in listen channel");
+ p2p->cfg->reg_class = p2p->pending_reg_class;
+ p2p->cfg->channel = p2p->pending_channel;
+ p2p->pending_reg_class = 0;
+ p2p->pending_channel = 0;
+ }
}
@@ -247,12 +220,15 @@
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) {
+ wpabuf_free(peer->go_neg_conf);
+ peer->go_neg_conf = NULL;
os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
ETH_ALEN);
os_memcpy(res.peer_interface_addr, peer->intended_addr,
@@ -271,6 +247,12 @@
p2p_dbg(p2p, "Starting short listen state (state=%s)",
p2p_state_txt(p2p->state));
+ if (p2p->pending_listen_freq) {
+ /* We have a pending p2p_listen request */
+ p2p_dbg(p2p, "p2p_listen command pending already");
+ return;
+ }
+
freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel);
if (freq < 0) {
p2p_dbg(p2p, "Unknown regulatory class/channel");
@@ -293,14 +275,14 @@
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;
+ p2p->pending_listen_freq = freq;
+ p2p->pending_listen_sec = 0;
+ p2p->pending_listen_usec = 1024 * tu;
+
if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
ies) < 0) {
p2p_dbg(p2p, "Failed to start listen mode");
@@ -317,13 +299,18 @@
p2p_dbg(p2p, "Going to listen(only) state");
+ if (p2p->pending_listen_freq) {
+ /* We have a pending p2p_listen request */
+ p2p_dbg(p2p, "p2p_listen command pending already");
+ return -1;
+ }
+
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;
@@ -341,6 +328,8 @@
if (ies == NULL)
return -1;
+ p2p->pending_listen_freq = freq;
+
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;
@@ -420,16 +409,11 @@
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
count++;
if (oldest == NULL ||
- os_time_before(&dev->last_seen, &oldest->last_seen))
+ 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");
-#ifdef ANDROID_P2P
- /* SD_FAIR_POLICY: Update the current sd_dev_list pointer to next device */
- if(&oldest->list == p2p->sd_dev_list)
- p2p->sd_dev_list = oldest->list.next;
-#endif
dl_list_del(&oldest->list);
p2p_device_free(p2p, oldest);
}
@@ -528,7 +512,7 @@
os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
ETH_ALEN);
- os_get_time(&dev->last_seen);
+ 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);
@@ -625,6 +609,46 @@
}
+static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies,
+ size_t ies_len)
+{
+ const u8 *pos, *end;
+ u8 id, len;
+
+ wpabuf_free(dev->info.vendor_elems);
+ dev->info.vendor_elems = NULL;
+
+ end = ies + ies_len;
+
+ for (pos = ies; pos + 1 < end; pos += len) {
+ id = *pos++;
+ len = *pos++;
+
+ if (pos + len > end)
+ break;
+
+ if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3)
+ continue;
+
+ if (len >= 4) {
+ u32 type = WPA_GET_BE32(pos);
+
+ if (type == WPA_IE_VENDOR_TYPE ||
+ type == WMM_IE_VENDOR_TYPE ||
+ type == WPS_IE_VENDOR_TYPE ||
+ type == P2P_IE_VENDOR_TYPE ||
+ type == WFD_IE_VENDOR_TYPE)
+ continue;
+ }
+
+ /* Unknown vendor element - make raw IE data available */
+ if (wpabuf_resize(&dev->info.vendor_elems, 2 + len) < 0)
+ break;
+ wpabuf_put_data(dev->info.vendor_elems, pos - 2, 2 + len);
+ }
+}
+
+
/**
* p2p_add_device - Add peer entries based on scan results or P2P frames
* @p2p: P2P module context from p2p_init()
@@ -644,14 +668,14 @@
* Info attributes.
*/
int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
- struct os_time *rx_time, int level, const u8 *ies,
+ 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_time time_now;
+ struct os_reltime time_now;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ies, ies_len, &msg)) {
@@ -685,7 +709,7 @@
}
if (rx_time == NULL) {
- os_get_time(&time_now);
+ os_get_reltime(&time_now);
rx_time = &time_now;
}
@@ -694,7 +718,7 @@
* entry is newer than the one previously stored.
*/
if (dev->last_seen.sec > 0 &&
- os_time_before(rx_time, &dev->last_seen)) {
+ 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,
@@ -704,7 +728,7 @@
return -1;
}
- os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_time));
+ os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
@@ -774,8 +798,7 @@
p2p_parse_free(&msg);
- if (p2p_pending_sd_req(p2p, dev))
- dev->flags |= P2P_DEV_SD_SCHEDULE;
+ p2p_update_peer_vendor_elems(dev, ies, ies_len);
if (dev->flags & P2P_DEV_REPORTED)
return 0;
@@ -846,6 +869,8 @@
}
wpabuf_free(dev->info.wfd_subelems);
+ wpabuf_free(dev->info.vendor_elems);
+ wpabuf_free(dev->go_neg_conf);
os_free(dev);
}
@@ -933,9 +958,6 @@
if (res < 0) {
p2p_dbg(p2p, "Scan request failed");
p2p_continue_find(p2p);
- } else if (res == 1) {
- p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes");
- p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY);
} else {
p2p_dbg(p2p, "Running p2p_scan");
p2p->p2p_scan_running = 1;
@@ -972,15 +994,7 @@
p2p->after_scan_tx->wait_time);
os_free(p2p->after_scan_tx);
p2p->after_scan_tx = NULL;
-#ifdef ANDROID_P2P
- /* For SD frames, there is a scenario, where we can receive a SD request frame during p2p_scan.
- * At that moment, we will send the SD response from this context. After sending the SD response,
- * we need to continue p2p_find. But if we return 1 from here, p2p_find is going to be stopped.
- */
- return 0;
-#else
return 1;
-#endif
}
op = p2p->start_after_scan;
@@ -1039,7 +1053,7 @@
int res;
p2p_dbg(p2p, "Starting find (type=%d)", type);
- os_get_time(&p2p->find_start);
+ os_get_reltime(&p2p->find_start);
if (p2p->p2p_scan_running) {
p2p_dbg(p2p, "p2p_scan is already running");
}
@@ -1098,11 +1112,10 @@
eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
p2p, NULL);
- } else if (res == 1) {
- p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes");
- res = 0;
- p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
- eloop_cancel_timeout(p2p_find_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 */
+ res = 0; /* do not report failure */
} else {
p2p_dbg(p2p, "Failed to start p2p_scan");
p2p_set_state(p2p, P2P_IDLE);
@@ -1112,47 +1125,13 @@
return res;
}
-#ifdef ANDROID_P2P
-int p2p_search_pending(struct p2p_data *p2p)
-{
- if(p2p == NULL)
- return 0;
-
- if(p2p->state == P2P_SEARCH_WHEN_READY)
- return 1;
-
- return 0;
-}
-#endif
-
-int p2p_other_scan_completed(struct p2p_data *p2p)
-{
- if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) {
- p2p_set_state(p2p, P2P_SEARCH);
- p2p_search(p2p);
- return 1;
- }
- if (p2p->state != P2P_SEARCH_WHEN_READY)
- return 0;
- p2p_dbg(p2p, "Starting pending P2P find now that previous scan was completed");
- if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
- p2p->num_req_dev_types, p2p->req_dev_types,
- p2p->find_dev_id, p2p->search_delay) < 0) {
- p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
- return 0;
- }
- return 1;
-}
-
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->state == P2P_CONTINUE_SEARCH_WHEN_READY ||
- p2p->state == P2P_SEARCH_WHEN_READY)
+ 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);
@@ -1203,25 +1182,28 @@
void p2p_stop_find(struct p2p_data *p2p)
{
+ p2p->pending_listen_freq = 0;
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)
+ 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",
- 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)) {
+ 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;
@@ -1247,6 +1229,9 @@
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");
@@ -1278,10 +1263,40 @@
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 {
+ } 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 if (p2p_channels_includes(&p2p->cfg->channels,
+ p2p->cfg->op_reg_class,
+ p2p->cfg->op_channel)) {
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;
+ } else if (p2p_channel_random_social(&p2p->cfg->channels,
+ &p2p->op_reg_class,
+ &p2p->op_channel) == 0) {
+ p2p_dbg(p2p, "Select random available social channel %d from 2.4 GHz band as operating channel preference",
+ p2p->op_channel);
+ } else {
+ /* Select any random available channel from the first available
+ * operating class */
+ p2p_channel_select(&p2p->cfg->channels, NULL,
+ &p2p->op_reg_class,
+ &p2p->op_channel);
+ p2p_dbg(p2p, "Select random available channel %d from operating class %d as operating channel preference",
+ p2p->op_channel, p2p->op_reg_class);
}
os_memcpy(&p2p->channels, &p2p->cfg->channels,
@@ -1295,6 +1310,7 @@
* @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
@@ -1303,16 +1319,25 @@
* is available.
*/
int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
- unsigned int force_freq, unsigned int pref_freq)
+ unsigned int force_freq, unsigned int pref_freq, int go)
{
- p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u",
- force_freq, pref_freq);
+ 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) < 0)
+ 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)" : "");
@@ -1351,15 +1376,16 @@
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)
+ 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",
+ " 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);
+ 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)) {
@@ -1368,7 +1394,8 @@
return -1;
}
- if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+ if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
+ go_intent == 15) < 0)
return -1;
if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
@@ -1442,6 +1469,7 @@
}
dev->wps_method = wps_method;
+ dev->oob_pw_id = oob_pw_id;
dev->status = P2P_SC_SUCCESS;
if (p2p->p2p_scan_running) {
@@ -1461,15 +1489,15 @@
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)
+ 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",
+ " wps_method=%d persistent_group=%d oob_pw_id=%u",
MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
- wps_method, persistent_group);
+ wps_method, persistent_group, oob_pw_id);
dev = p2p_get_device(p2p, peer_addr);
if (dev == NULL) {
@@ -1478,7 +1506,8 @@
return -1;
}
- if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+ if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent ==
+ 15) < 0)
return -1;
p2p->ssid_set = 0;
@@ -1499,6 +1528,7 @@
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;
@@ -1508,7 +1538,7 @@
void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
struct p2p_device *dev, struct p2p_message *msg)
{
- os_get_time(&dev->last_seen);
+ os_get_reltime(&dev->last_seen);
p2p_copy_wps_info(p2p, dev, 0, msg);
@@ -1576,7 +1606,7 @@
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);
+ p2p_random(params->passphrase, p2p->cfg->passphrase_len);
return 0;
}
@@ -1610,7 +1640,7 @@
p2p->op_channel);
os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
res.ssid_len = p2p->ssid_len;
- p2p_random(res.passphrase, 8);
+ p2p_random(res.passphrase, p2p->cfg->passphrase_len);
} else {
res.freq = peer->oper_freq;
if (p2p->ssid_len) {
@@ -1619,8 +1649,15 @@
}
}
+ 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];
@@ -1643,6 +1680,9 @@
p2p->ssid_set = 0;
peer->go_neg_req_sent = 0;
peer->wps_method = WPS_NOT_READY;
+ peer->oob_pw_id = 0;
+ wpabuf_free(peer->go_neg_conf);
+ peer->go_neg_conf = NULL;
p2p_set_state(p2p, P2P_PROVISIONING);
p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -1673,6 +1713,7 @@
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:
@@ -1706,20 +1747,15 @@
case WLAN_PA_VENDOR_SPECIFIC:
data++;
len--;
- if (len < 3)
+ if (len < 4)
return;
- if (WPA_GET_BE24(data) != OUI_WFA)
+ if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
return;
- data += 3;
- len -= 3;
- if (len < 1)
- return;
+ data += 4;
+ len -= 4;
- if (*data != P2P_OUI_TYPE)
- return;
-
- p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
+ p2p_rx_p2p_action(p2p, sa, data, len, freq);
break;
case WLAN_PA_GAS_INITIAL_REQ:
p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
@@ -1752,15 +1788,10 @@
if (len < 4)
return;
- if (WPA_GET_BE24(data) != OUI_WFA)
+ if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
return;
- data += 3;
- len -= 3;
-
- if (*data != P2P_OUI_TYPE)
- return;
- data++;
- len--;
+ data += 4;
+ len -= 4;
/* P2P action frame */
p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa));
@@ -1806,7 +1837,8 @@
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_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+ p2p->invite_dev_pw_id);
}
@@ -1839,7 +1871,7 @@
if (dev) {
if (dev->country[0] == 0 && msg.listen_channel)
os_memcpy(dev->country, msg.listen_channel, 3);
- os_get_time(&dev->last_seen);
+ os_get_reltime(&dev->last_seen);
p2p_parse_free(&msg);
return; /* already known */
}
@@ -1850,7 +1882,7 @@
return;
}
- os_get_time(&dev->last_seen);
+ os_get_reltime(&dev->last_seen);
dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
if (msg.listen_channel) {
@@ -1884,7 +1916,7 @@
dev = p2p_get_device(p2p, addr);
if (dev) {
- os_get_time(&dev->last_seen);
+ os_get_reltime(&dev->last_seen);
return dev; /* already known */
}
@@ -1970,6 +2002,9 @@
extra = wpabuf_len(p2p->wfd_ie_probe_resp);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
+ extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -1979,13 +2014,21 @@
pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
}
- p2p_build_wps_ie(p2p, buf, pw_id, 1);
+ 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 */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
+ wpabuf_put_buf(buf,
+ p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
+
/* P2P IE */
len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_capability(buf, p2p->dev_capab &
@@ -2012,17 +2055,21 @@
if (!p2p->in_listen || !p2p->drv_in_listen) {
/* not in Listen state - ignore Probe Request */
+ p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
+ p2p->in_listen, p2p->drv_in_listen);
return P2P_PREQ_NOT_LISTEN;
}
if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
ParseFailed) {
/* Ignore invalid Probe Request frames */
+ p2p_dbg(p2p, "Could not parse Probe Request frame - ignore it");
return P2P_PREQ_MALFORMED;
}
if (elems.p2p == NULL) {
/* not a P2P probe - ignore it */
+ p2p_dbg(p2p, "Not a P2P probe - ignore it");
return P2P_PREQ_NOT_P2P;
}
@@ -2030,11 +2077,15 @@
os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
/* Not sent to the broadcast address or our P2P Device Address
*/
+ p2p_dbg(p2p, "Probe Req DA " MACSTR " not ours - ignore it",
+ MAC2STR(dst));
return P2P_PREQ_NOT_PROCESSED;
}
if (bssid && !is_broadcast_ether_addr(bssid)) {
/* Not sent to the Wildcard BSSID */
+ p2p_dbg(p2p, "Probe Req BSSID " MACSTR " not wildcard - ignore it",
+ MAC2STR(bssid));
return P2P_PREQ_NOT_PROCESSED;
}
@@ -2042,23 +2093,28 @@
os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
0) {
/* not using P2P Wildcard SSID - ignore */
+ p2p_dbg(p2p, "Probe Req not using P2P Wildcard SSID - ignore it");
return P2P_PREQ_NOT_PROCESSED;
}
if (supp_rates_11b_only(&elems)) {
/* Indicates support for 11b rates only */
+ p2p_dbg(p2p, "Probe Req with 11b rates only supported - ignore it");
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 */
+ p2p_dbg(p2p, "Could not parse P2P attributes in Probe Req - ignore it");
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_dbg(p2p, "Probe Req requested Device ID " MACSTR " did not match - ignore it",
+ MAC2STR(msg.device_id));
p2p_parse_free(&msg);
return P2P_PREQ_NOT_PROCESSED;
}
@@ -2067,6 +2123,7 @@
if (msg.wps_attributes &&
!p2p_match_dev_type(p2p, msg.wps_attributes)) {
/* No match with Requested Device Type */
+ p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it");
p2p_parse_free(&msg);
return P2P_PREQ_NOT_PROCESSED;
}
@@ -2074,6 +2131,7 @@
if (!p2p->cfg->send_probe_resp) {
/* Response generated elsewhere */
+ p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response");
return P2P_PREQ_NOT_PROCESSED;
}
@@ -2164,10 +2222,12 @@
if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
p2p->invite_peer &&
+ (p2p->invite_peer->flags & P2P_DEV_WAIT_INV_REQ_ACK) &&
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_cancel_timeout(p2p_invite_start, p2p, NULL);
eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
return P2P_PREQ_PROCESSED;
}
@@ -2244,6 +2304,9 @@
extra = wpabuf_len(p2p->wfd_ie_assoc_req);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ])
+ extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]);
+
/*
* (Re)Association Request - P2P IE
* P2P Capability attribute (shall be present)
@@ -2259,6 +2322,10 @@
wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ])
+ wpabuf_put_buf(tmp,
+ p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]);
+
peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
lpos = p2p_buf_add_ie_hdr(tmp);
@@ -2380,7 +2447,8 @@
{
struct p2p_data *p2p;
- if (cfg->max_peers < 1)
+ if (cfg->max_peers < 1 ||
+ cfg->passphrase_len < 8 || cfg->passphrase_len > 63)
return NULL;
p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
@@ -2409,16 +2477,7 @@
p2p->cfg->num_pref_chan = 0;
}
-#ifdef ANDROID_P2P
- /* 100ms listen time is too less to receive the response frames in some scenarios
- * increasing min listen time to 200ms.
- */
- p2p->min_disc_int = 2;
- /* SD_FAIR_POLICY: Initializing the SD current serviced pointer to NULL */
- p2p->sd_dev_list = NULL;
-#else
p2p->min_disc_int = 1;
-#endif
p2p->max_disc_int = 3;
p2p->max_disc_tu = -1;
@@ -2438,6 +2497,11 @@
p2p->go_timeout = 100;
p2p->client_timeout = 20;
+ p2p->num_p2p_sd_queries = 0;
+
+ p2p_dbg(p2p, "initialized");
+ p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
+ p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
return p2p;
}
@@ -2475,6 +2539,7 @@
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);
}
@@ -2488,10 +2553,6 @@
dl_list_del(&dev->list);
p2p_device_free(p2p, dev);
}
-#ifdef ANDROID_P2P
- /* SD_FAIR_POLICY: Initializing the SD current serviced pointer to NULL */
- p2p->sd_dev_list = NULL;
-#endif
p2p_free_sd_queries(p2p);
os_free(p2p->after_scan_tx);
p2p->after_scan_tx = NULL;
@@ -2512,6 +2573,7 @@
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;
@@ -2669,44 +2731,18 @@
void p2p_continue_find(struct p2p_data *p2p)
{
struct p2p_device *dev;
-#ifdef ANDROID_P2P
- int skip=1;
-#endif
p2p_set_state(p2p, P2P_SEARCH);
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
-#ifdef ANDROID_P2P
- /* SD_FAIR_POLICY: We need to give chance to all devices in the device list
- * There may be a scenario, where a particular peer device have
- * not registered any query response. When we send a SD request to such device,
- * no response will be received. And if we continue to get probe responses from that device,
- * and if that device happens to be on top in our device list,
- * we will always continue to send SD requests always to that peer only.
- * We will not be able to send SD requests to other devices in that case.
- * This implementation keeps track of last serviced peer device.
- * And then takes the next one from the device list, in the next iteration.
- */
- if (p2p->sd_dev_list && p2p->sd_dev_list != &p2p->devices) {
- if(skip) {
- if ((&dev->list == p2p->sd_dev_list) ) {
- skip = 0;
- if (dev->list.next == &p2p->devices)
- p2p->sd_dev_list = NULL;
- }
- continue;
- }
+ if (dev->sd_pending_bcast_queries == 0) {
+ /* Initialize with total number of registered broadcast
+ * SD queries. */
+ dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries;
}
- p2p->sd_dev_list = &dev->list;
- wpa_printf(MSG_DEBUG, "P2P: ### Servicing %p dev->flags 0x%x SD schedule %s devaddr " MACSTR,
- p2p->sd_dev_list, dev->flags, dev->flags & P2P_DEV_SD_SCHEDULE ? "TRUE": "FALSE",
- MAC2STR(dev->info.p2p_device_addr));
-#endif
- 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)) {
+
+ if (p2p_start_sd(p2p, dev) == 0)
+ return;
+ 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),
@@ -2727,10 +2763,7 @@
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->sd_peer = NULL;
p2p_continue_find(p2p);
return;
}
@@ -2775,7 +2808,8 @@
MAC2STR(dev->info.p2p_device_addr),
dev->req_config_methods);
p2p_send_prov_disc_req(p2p, dev,
- dev->flags & P2P_DEV_PD_FOR_JOIN, 0);
+ dev->flags & P2P_DEV_PD_FOR_JOIN,
+ p2p->pd_force_freq);
return;
}
}
@@ -2809,11 +2843,7 @@
p2p_continue_find(p2p);
else if (p2p->user_initiated_pd) {
p2p->pending_action_state = P2P_PENDING_PD;
-#ifdef ANDROID_P2P
- p2p_set_timeout(p2p, 0, 350000);
-#else
p2p_set_timeout(p2p, 0, 300000);
-#endif
}
return;
}
@@ -2830,19 +2860,15 @@
/* Wait for response from the peer */
if (p2p->state == P2P_SEARCH)
p2p_set_state(p2p, P2P_PD_DURING_FIND);
-#ifdef ANDROID_P2P
- p2p_set_timeout(p2p, 0, 350000);
-#else
p2p_set_timeout(p2p, 0, 200000);
-#endif
}
int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
- struct os_time *rx_time, int level, const u8 *ies,
+ struct os_reltime *rx_time, int level, const u8 *ies,
size_t ies_len)
{
- if (os_time_before(rx_time, &p2p->find_start)) {
+ 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
@@ -2887,6 +2913,10 @@
wpabuf_put_buf(ies, p2p->wfd_ie_probe_req);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P])
+ wpabuf_put_buf(ies,
+ p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
+
len = p2p_buf_add_ie_hdr(ies);
p2p_buf_add_capability(ies, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
@@ -2913,6 +2943,10 @@
len += wpabuf_len(p2p->wfd_ie_probe_req);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p && p2p->vendor_elem &&
+ p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P])
+ len += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
+
return len;
}
@@ -3001,8 +3035,17 @@
struct p2p_device *dev;
dev = p2p_get_device(p2p, addr);
if (dev &&
- dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+ dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+ if ((p2p->state == P2P_SEARCH) ||
+ (p2p->state == P2P_LISTEN_ONLY)) {
+ /* Clear our search state or Listen state since
+ * now peer is awaiting response from our side.
+ */
+ p2p_dbg(p2p, "Clear the P2P discovery state");
+ p2p_stop_find(p2p);
+ }
+ }
}
}
@@ -3013,13 +3056,44 @@
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->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
return;
}
+
+ dev = p2p->go_neg_peer;
+
if (result == P2P_SEND_ACTION_NO_ACK) {
/*
+ * Retry GO Negotiation Confirmation
+ * P2P_GO_NEG_CNF_MAX_RETRY_COUNT times if we did not receive
+ * ACK for confirmation.
+ */
+ if (dev && dev->go_neg_conf &&
+ dev->go_neg_conf_sent <= P2P_GO_NEG_CNF_MAX_RETRY_COUNT) {
+ p2p_dbg(p2p, "GO Negotiation Confirm retry %d",
+ dev->go_neg_conf_sent);
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+ if (p2p_send_action(p2p, dev->go_neg_conf_freq,
+ dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr,
+ dev->info.p2p_device_addr,
+ wpabuf_head(dev->go_neg_conf),
+ wpabuf_len(dev->go_neg_conf), 0) >=
+ 0) {
+ dev->go_neg_conf_sent++;
+ return;
+ }
+ p2p_dbg(p2p, "Failed to re-send Action frame");
+
+ /*
+ * Continue with the assumption that the first attempt
+ * went through and just the ACK frame was lost.
+ */
+ }
+
+ /*
* 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
@@ -3032,7 +3106,8 @@
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;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
if (dev == NULL)
return;
@@ -3056,6 +3131,10 @@
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
switch (state) {
case P2P_NO_PENDING_ACTION:
+ if (p2p->send_action_in_progress) {
+ p2p->send_action_in_progress = 0;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ }
if (p2p->after_scan_tx_in_progress) {
p2p->after_scan_tx_in_progress = 0;
if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
@@ -3211,7 +3290,12 @@
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);
}
@@ -3240,27 +3324,28 @@
static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
{
- /*
- * TODO: could remain constantly in Listen state for some time if there
- * are no other concurrent uses for the radio. For now, go to listen
- * state once per second to give other uses a chance to use the radio.
- */
p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
- p2p_set_timeout(p2p, 0, 500000);
+
+ 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;
+ struct os_reltime now;
if (dev == NULL) {
p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait");
return;
}
- dev->wait_count++;
- if (dev->wait_count >= 120) {
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &dev->go_neg_wait_started, 120)) {
p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation");
p2p_go_neg_failed(p2p, dev, -1);
return;
@@ -3277,7 +3362,6 @@
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);
@@ -3355,14 +3439,15 @@
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_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);
+ p2p->invite_peer->info.p2p_device_addr,
+ 0, 0);
}
p2p_set_state(p2p, P2P_IDLE);
}
@@ -3437,10 +3522,6 @@
case P2P_INVITE_LISTEN:
p2p_timeout_invite_listen(p2p);
break;
- case P2P_SEARCH_WHEN_READY:
- break;
- case P2P_CONTINUE_SEARCH_WHEN_READY:
- break;
}
}
@@ -3473,6 +3554,8 @@
return "Keypad";
case WPS_PBC:
return "PBC";
+ case WPS_NFC:
+ return "NFC";
}
return "??";
@@ -3523,7 +3606,7 @@
struct p2p_device *dev;
int res;
char *pos, *end;
- struct os_time now;
+ struct os_reltime now;
if (info == NULL)
return -1;
@@ -3534,7 +3617,7 @@
pos = buf;
end = buf + buflen;
- os_get_time(&now);
+ os_get_reltime(&now);
res = os_snprintf(pos, end - pos,
"age=%d\n"
"listen_freq=%d\n"
@@ -3549,9 +3632,8 @@
"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"
+ "flags=%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,
@@ -3572,9 +3654,6 @@
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 ?
@@ -3596,7 +3675,6 @@
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;
@@ -3838,6 +3916,11 @@
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);
@@ -3864,6 +3947,15 @@
p2p_ext_listen_timeout, p2p, NULL);
}
+ if ((p2p->cfg->is_p2p_in_progress &&
+ p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) ||
+ (p2p->pending_action_state == P2P_PENDING_PD &&
+ p2p->pd_retries > 0)) {
+ p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)",
+ p2p_state_txt(p2p->state));
+ return;
+ }
+
if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
/*
* This should not really happen, but it looks like the Listen
@@ -3935,8 +4027,10 @@
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ie, ie_len, &msg))
return;
- if (msg.minor_reason_code == NULL)
+ 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",
@@ -3957,8 +4051,10 @@
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ie, ie_len, &msg))
return;
- if (msg.minor_reason_code == NULL)
+ 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",
@@ -3980,20 +4076,43 @@
}
-int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel)
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
+ u8 forced)
{
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;
+
+ /*
+ * Listen channel was set in configuration or set by control interface;
+ * cannot override it.
+ */
+ if (p2p->cfg->channel_forced && forced == 0)
+ return -1;
+
+ if (p2p->state == P2P_IDLE) {
+ p2p->cfg->reg_class = reg_class;
+ p2p->cfg->channel = channel;
+ p2p->cfg->channel_forced = forced;
+ } else {
+ p2p_dbg(p2p, "Defer setting listen channel");
+ p2p->pending_reg_class = reg_class;
+ p2p->pending_channel = channel;
+ p2p->pending_channel_forced = forced;
+ }
return 0;
}
+u8 p2p_get_listen_channel(struct p2p_data *p2p)
+{
+ return p2p->cfg->channel;
+}
+
+
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));
@@ -4046,6 +4165,31 @@
}
+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)
{
@@ -4108,10 +4252,16 @@
}
-void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+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);
}
@@ -4190,7 +4340,7 @@
dev = dl_list_first(&dev->list,
struct p2p_device,
list);
- if (&dev->list == &p2p->devices)
+ if (!dev || &dev->list == &p2p->devices)
return NULL;
} while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
}
@@ -4202,7 +4352,7 @@
dev = dl_list_first(&dev->list,
struct p2p_device,
list);
- if (&dev->list == &p2p->devices)
+ if (!dev || &dev->list == &p2p->devices)
return NULL;
}
}
@@ -4210,22 +4360,12 @@
return &dev->info;
}
-#ifdef ANDROID_P2P
-int p2p_search_in_progress(struct p2p_data *p2p)
-{
- if (p2p == NULL)
- return 0;
-
- return p2p->state == P2P_SEARCH;
-}
-#endif
int p2p_in_progress(struct p2p_data *p2p)
{
if (p2p == NULL)
return 0;
- if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY ||
- p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY)
+ if (p2p->state == P2P_SEARCH)
return 2;
return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
}
@@ -4241,13 +4381,6 @@
}
-void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay)
-{
- if (p2p && p2p->search_delay < delay)
- p2p->search_delay = delay;
-}
-
-
#ifdef CONFIG_WIFI_DISPLAY
static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
@@ -4257,7 +4390,7 @@
for (g = 0; g < p2p->num_groups; g++) {
group = p2p->groups[g];
- p2p_group_update_ies(group);
+ p2p_group_force_beacon_update_ies(group);
}
}
@@ -4435,3 +4568,259 @@
va_end(ap);
p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
}
+
+
+void p2p_loop_on_known_peers(struct p2p_data *p2p,
+ void (*peer_callback)(struct p2p_peer_info *peer,
+ void *user_data),
+ void *user_data)
+{
+ struct p2p_device *dev, *n;
+
+ dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
+ peer_callback(&dev->info, user_data);
+ }
+}
+
+
+#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) {
+ int freq = p2p_group_get_freq(p2p->groups[0]);
+ role = P2P_GO_IN_A_GROUP;
+ if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) {
+ p2p_dbg(p2p,
+ "Unknown GO operating frequency %d MHz for NFC handover",
+ freq);
+ wpabuf_free(buf);
+ return NULL;
+ }
+ } else if (client_freq > 0) {
+ role = P2P_CLIENT_IN_A_GROUP;
+ if (p2p_freq_to_channel(client_freq, &op_class, &channel) < 0) {
+ p2p_dbg(p2p,
+ "Unknown client operating frequency %d MHz for NFC handover",
+ client_freq);
+ wpabuf_free(buf);
+ return NULL;
+ }
+ }
+
+ 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);
+ }
+
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ p2p_dbg(p2p, "Do not report rejected device");
+ p2p_parse_free(&msg);
+ 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;
+ }
+ p2p_parse_free(&msg);
+
+ 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 */
+
+
+int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len)
+{
+ if (len < 8 || len > 63)
+ return -1;
+ p2p->cfg->passphrase_len = len;
+ return 0;
+}
+
+
+void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem)
+{
+ p2p->vendor_elem = vendor_elem;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 7f845b2..dee79df 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -9,6 +9,8 @@
#ifndef P2P_H
#define P2P_H
+#include "wps/wps_defs.h"
+
/**
* P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
*/
@@ -50,7 +52,7 @@
};
enum p2p_wps_method {
- WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+ WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
};
/**
@@ -77,6 +79,8 @@
int ht40;
+ int vht;
+
/**
* ssid - SSID of the group
*/
@@ -226,6 +230,14 @@
* wfd_subelems - Wi-Fi Display subelements from WFD IE(s)
*/
struct wpabuf *wfd_subelems;
+
+ /**
+ * vendor_elems - Unrecognized vendor elements
+ *
+ * This buffer includes any other vendor element than P2P, WPS, and WFD
+ * IE(s) from the frame that was used to discover the peer.
+ */
+ struct wpabuf *vendor_elems;
};
enum p2p_prov_disc_status {
@@ -263,6 +275,12 @@
u8 channel;
/**
+ * channel_forced - the listen channel was forced by configuration
+ * or by control interface and cannot be overridden
+ */
+ u8 channel_forced;
+
+ /**
* Regulatory class for own operational channel
*/
u8 op_reg_class;
@@ -287,6 +305,20 @@
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;
@@ -371,6 +403,14 @@
unsigned int max_listen;
/**
+ * passphrase_len - Passphrase length (8..63)
+ *
+ * This parameter controls the length of the random passphrase that is
+ * generated at the GO.
+ */
+ unsigned int passphrase_len;
+
+ /**
* cb_ctx - Context to use with callback functions
*/
void *cb_ctx;
@@ -690,6 +730,8 @@
* 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
@@ -711,7 +753,8 @@
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);
+ const struct p2p_channels *channels,
+ int dev_pw_id);
/**
* invitation_received - Callback on Invitation Request RX
@@ -742,6 +785,9 @@
* @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
+ * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer
+ * 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
@@ -751,7 +797,7 @@
*/
void (*invitation_result)(void *ctx, int status, const u8 *bssid,
const struct p2p_channels *channels,
- const u8 *addr);
+ const u8 *addr, int freq, int peer_oper_freq);
/**
* go_connected - Check whether we are connected to a GO
@@ -761,6 +807,34 @@
* 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);
+
+ /**
+ * is_p2p_in_progress - Check whether P2P operation is in progress
+ * @ctx: Callback context from cb_ctx
+ * Returns: 1 if P2P operation (e.g., group formation) is in progress
+ * or 0 if not.
+ */
+ int (*is_p2p_in_progress)(void *ctx);
};
@@ -935,7 +1009,7 @@
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);
+ int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id);
/**
* p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -963,7 +1037,7 @@
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);
+ unsigned int pref_freq, u16 oob_pw_id);
/**
* p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -1065,12 +1139,14 @@
* @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 persistent_group, unsigned int pref_freq, int dev_pw_id);
/**
* p2p_presence_req - Request GO presence
@@ -1228,7 +1304,7 @@
* 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_time *rx_time, int level, const u8 *ies,
+ struct os_reltime *rx_time, int level, const u8 *ies,
size_t ies_len);
/**
@@ -1335,6 +1411,11 @@
size_t ssid_len;
/**
+ * freq - Operating channel of the group
+ */
+ int freq;
+
+ /**
* cb_ctx - Context to use with callback functions
*/
void *cb_ctx;
@@ -1610,7 +1691,10 @@
*/
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_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
+ u8 forced);
+
+u8 p2p_get_listen_channel(struct p2p_data *p2p);
int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len);
@@ -1649,6 +1733,22 @@
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
@@ -1657,7 +1757,9 @@
unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
const struct p2p_channels *channels);
-void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan);
+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
@@ -1694,7 +1796,7 @@
* @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
+ * Returns: A P2P Device Address for each call and %NULL for no more members
*/
const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
@@ -1716,6 +1818,26 @@
int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr);
/**
+ * p2p_group_get_config - Get the group configuration
+ * @group: P2P group context from p2p_group_init()
+ * Returns: The group configuration pointer
+ */
+const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group);
+
+/**
+ * p2p_loop_on_all_groups - Run the given callback on all groups
+ * @p2p: P2P module context from p2p_init()
+ * @group_callback: The callback function pointer
+ * @user_data: Some user data pointer which can be %NULL
+ *
+ * The group_callback function can stop the iteration by returning 0.
+ */
+void p2p_loop_on_all_groups(struct p2p_data *p2p,
+ int (*group_callback)(struct p2p_group *group,
+ void *user_data),
+ void *user_data);
+
+/**
* 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
@@ -1765,35 +1887,21 @@
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);
-#ifdef ANDROID_P2P
-/**
- * p2p_search_in_progress - Check whether a P2P SEARCH is in 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_search_in_progress(struct p2p_data *p2p);
-
-/**
- * p2p_search_pending - Check whether there is a deferred P2P SEARCH
- * @p2p: P2P module context from p2p_init()
- * Returns: 0 if there is no deferred P2P search or 1 if there is one
- */
-int p2p_search_pending(struct p2p_data *p2p);
-#endif
-
-/**
- * p2p_other_scan_completed - Notify completion of non-P2P scan
- * @p2p: P2P module context from p2p_init()
- * Returns: 0 if P2P module is idle or 1 if an operation was started
- */
-int p2p_other_scan_completed(struct p2p_data *p2p);
-
const char * p2p_wps_method_text(enum p2p_wps_method method);
/**
@@ -1805,8 +1913,6 @@
void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
u8 client_timeout);
-void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay);
-
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);
@@ -1853,4 +1959,50 @@
*/
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);
+
+int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len);
+
+void p2p_loop_on_known_peers(struct p2p_data *p2p,
+ void (*peer_callback)(struct p2p_peer_info *peer,
+ void *user_data),
+ void *user_data);
+
+void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem);
+
#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 5838d35..e9b683d 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -17,8 +17,7 @@
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_be32(buf, P2P_IE_VENDOR_TYPE);
wpabuf_put_u8(buf, subtype); /* OUI Subtype */
wpabuf_put_u8(buf, dialog_token);
@@ -31,8 +30,7 @@
{
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_be32(buf, P2P_IE_VENDOR_TYPE);
wpabuf_put_u8(buf, subtype); /* OUI Subtype */
wpabuf_put_u8(buf, dialog_token);
@@ -47,8 +45,7 @@
/* 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);
+ wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
return len;
}
@@ -258,6 +255,7 @@
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);
}
@@ -327,13 +325,32 @@
}
-static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
- const char *val)
+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;
- wpabuf_put_be16(buf, attr);
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) {
/*
@@ -341,36 +358,46 @@
* 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;
+ return 0;
}
#endif /* CONFIG_WPS_STRICT */
wpabuf_put_be16(buf, len);
if (val)
wpabuf_put_data(buf, val, len);
+ return 0;
}
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
- int all_attr)
+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);
- wps_build_version(buf);
+ 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);
@@ -380,33 +407,47 @@
}
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);
- wps_build_uuid_e(buf, p2p->cfg->uuid);
- p2p_add_wps_string(buf, ATTR_MANUFACTURER,
- p2p->cfg->manufacturer);
- p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name);
- p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
- p2p->cfg->model_number);
- p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
- p2p->cfg->serial_number);
+ 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);
- p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name);
+ 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);
}
- wps_build_wfa_ext(buf, 0, NULL, 0);
+ 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);
@@ -428,4 +469,6 @@
}
p2p_buf_update_ie_hdr(buf, len);
+
+ return 0;
}
diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c
index 76d01cf..86bae1a 100644
--- a/src/p2p/p2p_dev_disc.c
+++ b/src/p2p/p2p_dev_disc.c
@@ -68,6 +68,7 @@
{
struct p2p_device *go;
struct wpabuf *req;
+ unsigned int wait_time;
go = p2p_get_device(p2p, dev->member_in_go_dev);
if (go == NULL || dev->oper_freq <= 0) {
@@ -88,9 +89,12 @@
os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr,
ETH_ALEN);
p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST;
+ wait_time = 1000;
+ if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
+ wait_time = p2p->cfg->max_listen;
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) {
+ wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
wpabuf_free(req);
/* TODO: how to recover from failure? */
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index bd583be..bd7a2cf 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "p2p_i.h"
#include "p2p.h"
@@ -103,6 +104,8 @@
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;
}
@@ -118,6 +121,8 @@
return "Keypad";
case WPS_PBC:
return "PBC";
+ case WPS_NFC:
+ return "NFC";
default:
return "??";
}
@@ -131,12 +136,16 @@
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 */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
+ extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -172,13 +181,23 @@
p2p_buf_update_ie_hdr(buf, len);
/* WPS IE with Device Password ID attribute */
- p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+ 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 */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
+ wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+
return buf;
}
@@ -205,6 +224,8 @@
}
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",
@@ -245,6 +266,7 @@
u8 *len;
u8 group_capab;
size_t extra = 0;
+ u16 pw_id;
p2p_dbg(p2p, "Building GO Negotiation Response");
@@ -253,6 +275,9 @@
extra = wpabuf_len(p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
+ extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -307,15 +332,22 @@
p2p_buf_update_ie_hdr(buf, len);
/* WPS IE with Device Password ID attribute */
- p2p_build_wps_ie(p2p, buf,
- p2p_wps_method_pw_id(peer ? peer->wps_method :
- WPS_NOT_READY), 0);
+ 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 */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
+ wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
return buf;
}
@@ -339,6 +371,9 @@
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,
@@ -403,44 +438,28 @@
}
}
+ /* 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 */
- for (i = 0; i < intersection->reg_classes; i++) {
- struct p2p_reg_class *c = &intersection->reg_class[i];
- if (c->reg_class == 116 || c->reg_class == 117 ||
- c->reg_class == 126 || c->reg_class == 127) {
- p2p_dbg(p2p, "Pick possible HT40 channel (reg_class %u channel %u) from intersection",
- c->reg_class, c->channel[0]);
- p2p->op_reg_class = c->reg_class;
- p2p->op_channel = c->channel[0];
- return;
- }
+ 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 */
- for (i = 0; i < intersection->reg_classes; i++) {
-#ifdef ANDROID_P2P
- struct p2p_reg_class prc;
- struct p2p_reg_class *c = &prc;
- p2p_copy_reg_class(c, &intersection->reg_class[i]);
-#else
- struct p2p_reg_class *c = &intersection->reg_class[i];
-#endif
- if ((c->reg_class == 115 || c->reg_class == 124) &&
- c->channels) {
- unsigned int r;
-
- /*
- * Pick one of the available channels in the operating
- * class at random.
- */
- os_get_random((u8 *) &r, sizeof(r));
- r %= c->channels;
- p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection",
- c->reg_class, c->channel[r]);
- p2p->op_reg_class = c->reg_class;
- p2p->op_channel = c->channel[r];
- return;
- }
+ 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;
}
/*
@@ -470,10 +489,17 @@
static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
u8 *status)
{
- struct p2p_channels intersection;
- size_t i;
+ struct p2p_channels tmp, intersection;
- p2p_channels_intersect(&p2p->channels, &dev->channels, &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;
@@ -481,14 +507,6 @@
return -1;
}
- for (i = 0; i < intersection.reg_classes; i++) {
- struct p2p_reg_class *c;
- c = &intersection.reg_class[i];
- p2p_dbg(p2p, "reg_class %u", c->reg_class);
- wpa_hexdump(MSG_DEBUG, "P2P: channels",
- c->channel, c->channels);
- }
-
if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
p2p->op_channel)) {
if (dev->flags & P2P_DEV_FORCE_FREQ) {
@@ -588,17 +606,53 @@
if (msg.status && *msg.status) {
p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request",
*msg.status);
+ if (dev && p2p->go_neg_peer == dev &&
+ *msg.status == P2P_SC_FAIL_REJECTED_BY_USER) {
+ /*
+ * This mechanism for using Status attribute in GO
+ * Negotiation Request is not compliant with the P2P
+ * specification, but some deployed devices use it to
+ * indicate rejection of GO Negotiation in a case where
+ * they have sent out GO Negotiation Response with
+ * status 1. The P2P specification explicitly disallows
+ * this. To avoid unnecessary interoperability issues
+ * and extra frames, mark the pending negotiation as
+ * failed and do not reply to this GO Negotiation
+ * Request frame.
+ */
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_go_neg_failed(p2p, dev, *msg.status);
+ p2p_parse_free(&msg);
+ return;
+ }
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)
+ else if ((dev->flags & P2P_DEV_PROBE_REQ_ONLY) ||
+ !(dev->flags & P2P_DEV_REPORTED))
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) {
+ } 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;
@@ -685,6 +739,28 @@
}
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;
@@ -780,6 +856,9 @@
extra = wpabuf_len(p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
+ extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -824,6 +903,9 @@
wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
+ wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
return buf;
}
@@ -832,7 +914,6 @@
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;
@@ -876,7 +957,7 @@
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;
+ os_get_reltime(&dev->go_neg_wait_started);
p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
p2p_set_timeout(p2p, 0, 0);
} else {
@@ -1010,6 +1091,17 @@
}
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;
@@ -1026,10 +1118,13 @@
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);
+ /* Store GO Negotiation Confirmation to allow retransmission */
+ wpabuf_free(dev->go_neg_conf);
+ dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token,
+ status, msg.operating_channel,
+ go);
p2p_parse_free(&msg);
- if (conf == NULL)
+ if (dev->go_neg_conf == NULL)
return;
p2p_dbg(p2p, "Sending GO Negotiation Confirm");
if (status == P2P_SC_SUCCESS) {
@@ -1041,12 +1136,18 @@
freq = rx_freq;
else
freq = dev->listen_freq;
+
+ dev->go_neg_conf_freq = freq;
+ dev->go_neg_conf_sent = 0;
+
if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
- wpabuf_head(conf), wpabuf_len(conf), 0) < 0) {
+ wpabuf_head(dev->go_neg_conf),
+ wpabuf_len(dev->go_neg_conf), 200) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
p2p_go_neg_failed(p2p, dev, -1);
- }
- wpabuf_free(conf);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ } else
+ dev->go_neg_conf_sent++;
if (status != P2P_SC_SUCCESS) {
p2p_dbg(p2p, "GO Negotiation failed");
p2p_go_neg_failed(p2p, dev, status);
@@ -1080,9 +1181,11 @@
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)",
@@ -1131,16 +1234,6 @@
}
}
-#ifdef ANDROID_P2P
- if (msg.operating_channel) {
- dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
- msg.operating_channel[4]);
- p2p_dbg(p2p, "P2P: Peer operating channel preference: %d MHz",
- dev->oper_freq);
- } else
- dev->oper_freq = 0;
-#endif
-
if (!msg.channel_list) {
p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation");
#ifdef CONFIG_P2P_STRICT
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 15e7622..da8588a 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
@@ -154,6 +155,7 @@
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);
}
@@ -213,6 +215,10 @@
extra = wpabuf_len(group->p2p->wfd_ie_beacon);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (group->p2p->vendor_elem &&
+ group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
+ extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
+
ie = wpabuf_alloc(257 + extra);
if (ie == NULL)
return NULL;
@@ -222,6 +228,11 @@
wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (group->p2p->vendor_elem &&
+ group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
+ wpabuf_put_buf(ie,
+ group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
+
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);
@@ -397,10 +408,38 @@
#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;
- struct p2p_group_member *m;
p2p_subelems = wpabuf_alloc(500);
if (p2p_subelems == NULL)
@@ -413,21 +452,19 @@
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) {
- u8 *group_info;
- group_info = wpabuf_put(p2p_subelems, 0);
- wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
- wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
- for (m = group->members; m; m = m->next)
- p2p_client_info(p2p_subelems, m);
- WPA_PUT_LE16(group_info + 1,
- (u8 *) wpabuf_put(p2p_subelems, 0) - group_info -
- 3);
- }
+ if (group->members)
+ p2p_buf_add_group_info(group, p2p_subelems, -1);
ie = p2p_group_encaps_probe_resp(p2p_subelems);
wpabuf_free(p2p_subelems);
+ if (group->p2p->vendor_elem &&
+ group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]) {
+ struct wpabuf *extra;
+ extra = wpabuf_dup(group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]);
+ ie = wpabuf_concat(extra, ie);
+ }
+
#ifdef CONFIG_WIFI_DISPLAY
if (group->wfd_ie) {
struct wpabuf *wfd = wpabuf_dup(group->wfd_ie);
@@ -614,6 +651,10 @@
extra = wpabuf_len(group->wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (group->p2p->vendor_elem &&
+ group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
+ extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
+
/*
* (Re)Association Response - P2P IE
* Status attribute (shall be present when association request is
@@ -629,6 +670,11 @@
wpabuf_put_buf(resp, group->wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (group->p2p->vendor_elem &&
+ group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
+ wpabuf_put_buf(resp,
+ group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
+
rlen = p2p_buf_add_ie_hdr(resp);
if (status != P2P_SC_SUCCESS)
p2p_buf_add_status(resp, status);
@@ -953,7 +999,7 @@
if (!iter)
return NULL;
- return iter->addr;
+ return iter->dev_addr;
}
@@ -980,3 +1026,36 @@
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;
+}
+
+
+const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group)
+{
+ return group->cfg;
+}
+
+
+void p2p_loop_on_all_groups(struct p2p_data *p2p,
+ int (*group_callback)(struct p2p_group *group,
+ void *user_data),
+ void *user_data)
+{
+ unsigned int i;
+
+ for (i = 0; i < p2p->num_groups; i++) {
+ if (!group_callback(p2p->groups[i], user_data))
+ break;
+ }
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 81e521e..3b60582 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -12,6 +12,10 @@
#include "utils/list.h"
#include "p2p.h"
+#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1
+
+enum p2p_role_indication;
+
enum p2p_go_state {
UNKNOWN_GO,
LOCAL_GO,
@@ -23,9 +27,11 @@
*/
struct p2p_device {
struct dl_list list;
- struct os_time last_seen;
+ 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;
@@ -77,8 +83,6 @@
#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)
@@ -93,9 +97,11 @@
#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)
+#define P2P_DEV_WAIT_INV_REQ_ACK BIT(19)
unsigned int flags;
int status; /* enum p2p_status_code */
+ struct os_reltime go_neg_wait_started;
unsigned int wait_count;
unsigned int connect_reqs;
unsigned int invitation_reqs;
@@ -105,6 +111,23 @@
u8 go_timeout;
u8 client_timeout;
+
+ /**
+ * go_neg_conf_sent - Number of GO Negotiation Confirmation retries
+ */
+ u8 go_neg_conf_sent;
+
+ /**
+ * freq - Frquency on which the GO Negotiation Confirmation is sent
+ */
+ int go_neg_conf_freq;
+
+ /**
+ * go_neg_conf - GO Negotiation Confirmation frame
+ */
+ struct wpabuf *go_neg_conf;
+
+ int sd_pending_bcast_queries;
};
struct p2p_sd_query {
@@ -205,16 +228,6 @@
* P2P_INVITE_LISTEN - Listen during Invite
*/
P2P_INVITE_LISTEN,
-
- /**
- * P2P_SEARCH_WHEN_READY - Waiting to start Search
- */
- P2P_SEARCH_WHEN_READY,
-
- /**
- * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search
- */
- P2P_CONTINUE_SEARCH_WHEN_READY,
} state;
/**
@@ -237,14 +250,6 @@
*/
struct dl_list devices;
-#ifdef ANDROID_P2P
- /**
- * sd_dev_list - device pointer to be serviced next
- * for service discovery
- */
- struct dl_list *sd_dev_list;
-#endif
-
/**
* go_neg_peer - Pointer to GO Negotiation peer
*/
@@ -257,6 +262,7 @@
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
@@ -268,6 +274,12 @@
*/
struct p2p_sd_query *sd_query;
+ /**
+ * num_p2p_sd_queries - Total number of broadcast SD queries present in
+ * the list
+ */
+ int num_p2p_sd_queries;
+
/* GO Negotiation data */
/**
@@ -324,6 +336,8 @@
*/
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,
@@ -394,6 +408,7 @@
u8 after_scan_peer[ETH_ALEN];
struct p2p_pending_action_tx *after_scan_tx;
unsigned int after_scan_tx_in_progress:1;
+ unsigned int send_action_in_progress:1;
/* Requested device types for find/search */
unsigned int num_req_dev_types;
@@ -401,7 +416,7 @@
u8 *find_dev_id;
u8 find_dev_id_buf[ETH_ALEN];
- struct os_time find_start; /* time of last p2p_find start */
+ struct os_reltime find_start; /* time of last p2p_find start */
struct p2p_group **groups;
size_t num_groups;
@@ -450,6 +465,14 @@
*/
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;
@@ -457,6 +480,10 @@
unsigned int search_delay;
int in_search_delay;
+ u8 pending_reg_class;
+ u8 pending_channel;
+ u8 pending_channel_forced;
+
#ifdef CONFIG_WIFI_DISPLAY
struct wpabuf *wfd_ie_beacon;
struct wpabuf *wfd_ie_probe_req;
@@ -470,6 +497,10 @@
struct wpabuf *wfd_assoc_bssid;
struct wpabuf *wfd_coupled_sink_info;
#endif /* CONFIG_WIFI_DISPLAY */
+
+ u16 authorized_oob_dev_pw_id;
+
+ struct wpabuf **vendor_elem;
};
/**
@@ -511,6 +542,8 @@
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;
@@ -522,6 +555,7 @@
/* 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;
@@ -536,6 +570,8 @@
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;
@@ -570,16 +606,26 @@
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);
-#ifdef ANDROID_P2P
-size_t p2p_copy_reg_class(struct p2p_reg_class *dc, struct p2p_reg_class *sc);
-#endif
+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);
+int p2p_channel_random_social(struct p2p_channels *chans, 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,
@@ -602,7 +648,12 @@
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);
@@ -634,8 +685,11 @@
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_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
- int all_attr);
+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,
@@ -681,7 +735,7 @@
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);
+ 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);
@@ -709,7 +763,7 @@
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_time *rx_time, int level, const u8 *ies,
+ 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,
@@ -727,7 +781,8 @@
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);
+ 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, ...)
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 6b3dafb..ef01a66 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -10,13 +10,15 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.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)
+ const u8 *go_dev_addr,
+ int dev_pw_id)
{
struct wpabuf *buf;
u8 *len;
@@ -44,6 +46,9 @@
extra = wpabuf_len(wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ])
+ extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]);
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -85,6 +90,14 @@
wpabuf_put_buf(buf, wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ])
+ wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]);
+
+ 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;
}
@@ -221,14 +234,18 @@
goto fail;
}
+ p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels);
+ p2p_channels_dump(p2p, "peer channels", &dev->channels);
p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
&intersection);
+ p2p_channels_dump(p2p, "intersection", &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);
+ &go, group_bssid, &op_freq, persistent, &intersection,
+ msg.dev_password_id_present ? msg.dev_password_id : -1);
}
if (op_freq) {
@@ -281,7 +298,9 @@
}
}
- if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+ /* Reselect the channel only for the case of the GO */
+ if (go &&
+ !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);
@@ -296,7 +315,7 @@
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
}
- } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+ } else if (go && !(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);
@@ -352,12 +371,17 @@
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;
+ if (msg.group_id) {
+ 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);
+ } else {
+ p2p->inv_ssid_len = 0;
+ os_memset(p2p->inv_go_dev_addr, 0, ETH_ALEN);
}
- os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
p2p->inv_status = status;
p2p->inv_op_freq = op_freq;
@@ -407,7 +431,7 @@
return;
}
- if (!msg.channel_list) {
+ 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
@@ -416,6 +440,9 @@
#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) {
@@ -428,9 +455,25 @@
channels = &intersection;
}
- if (p2p->cfg->invitation_result)
+ if (p2p->cfg->invitation_result) {
+ int peer_oper_freq = 0;
+ int freq = p2p_channel_to_freq(p2p->op_reg_class,
+ p2p->op_channel);
+ if (freq < 0)
+ freq = 0;
+
+ if (msg.operating_channel) {
+ peer_oper_freq = p2p_channel_to_freq(
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ if (peer_oper_freq < 0)
+ peer_oper_freq = 0;
+ }
+
p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
- msg.group_bssid, channels, sa);
+ msg.group_bssid, channels, sa,
+ freq, peer_oper_freq);
+ }
p2p_parse_free(&msg);
@@ -441,12 +484,14 @@
int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
- const u8 *go_dev_addr)
+ 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",
@@ -454,7 +499,7 @@
return -1;
}
- req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+ req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
if (req == NULL)
return -1;
if (p2p->state != P2P_IDLE)
@@ -466,10 +511,12 @@
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), 200) < 0) {
+ 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->flags |= P2P_DEV_WAIT_INV_REQ_ACK;
}
wpabuf_free(req);
@@ -487,12 +534,15 @@
return;
}
+ if (success)
+ p2p->invite_peer->flags &= ~P2P_DEV_WAIT_INV_REQ_ACK;
+
/*
* 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 ? 350000 : 100000);
+ p2p_set_timeout(p2p, 0, success ? 500000 : 100000);
}
@@ -519,7 +569,7 @@
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 persistent_group, unsigned int pref_freq, int dev_pw_id)
{
struct p2p_device *dev;
@@ -537,15 +587,22 @@
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)) {
+ 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) < 0)
+ 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 &&
@@ -576,5 +633,5 @@
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);
+ return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
}
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 097a31d..d6144a0 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -268,6 +268,19 @@
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);
@@ -340,6 +353,7 @@
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];
@@ -367,6 +381,9 @@
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;
}
@@ -450,6 +467,33 @@
}
+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
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 54aa428..e101367 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "p2p_i.h"
#include "p2p.h"
@@ -53,6 +54,9 @@
extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
+ extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -77,6 +81,9 @@
wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
+ wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
+
return buf;
}
@@ -111,6 +118,9 @@
extra = wpabuf_len(wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
+ extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
+
buf = wpabuf_alloc(100 + extra);
if (buf == NULL)
return NULL;
@@ -125,6 +135,9 @@
wpabuf_put_buf(buf, wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
+ wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
+
return buf;
}
@@ -224,7 +237,8 @@
p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
- }
+ } else
+ p2p->send_action_in_progress = 1;
wpabuf_free(resp);
@@ -437,6 +451,7 @@
}
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;
@@ -472,4 +487,5 @@
p2p->user_initiated_pd = 0;
os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
p2p->pd_retries = 0;
+ p2p->pd_force_freq = 0;
}
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 795ca6b..6235b1d 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -52,6 +52,7 @@
{
struct p2p_sd_query *q;
int wsd = 0;
+ int count = 0;
if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
return NULL; /* peer does not support SD */
@@ -64,8 +65,19 @@
/* 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 the query is a broadcast query */
+ if (q->for_all_peers) {
+ /*
+ * check if there are any broadcast queries pending for
+ * this device
+ */
+ if (dev->sd_pending_bcast_queries <= 0)
+ return NULL;
+ /* query number that needs to be send to the device */
+ if (count == dev->sd_pending_bcast_queries - 1)
+ return q;
+ count++;
+ }
if (!q->for_all_peers &&
os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
0)
@@ -76,14 +88,37 @@
}
+static void p2p_decrease_sd_bc_queries(struct p2p_data *p2p, int query_number)
+{
+ struct p2p_device *dev;
+
+ p2p->num_p2p_sd_queries--;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (query_number <= dev->sd_pending_bcast_queries - 1) {
+ /*
+ * Query not yet sent to the device and it is to be
+ * removed, so update the pending count.
+ */
+ dev->sd_pending_bcast_queries--;
+ }
+ }
+}
+
+
static int p2p_unlink_sd_query(struct p2p_data *p2p,
struct p2p_sd_query *query)
{
struct p2p_sd_query *q, *prev;
+ int query_number = 0;
+
q = p2p->sd_queries;
prev = NULL;
while (q) {
if (q == query) {
+ /* If the query is a broadcast query, decrease one from
+ * all the devices */
+ if (query->for_all_peers)
+ p2p_decrease_sd_bc_queries(p2p, query_number);
if (prev)
prev->next = q->next;
else
@@ -92,6 +127,8 @@
p2p->sd_query = NULL;
return 1;
}
+ if (q->for_all_peers)
+ query_number++;
prev = q;
q = q->next;
}
@@ -118,6 +155,7 @@
q = q->next;
p2p_free_sd_query(prev);
}
+ p2p->num_p2p_sd_queries = 0;
}
@@ -133,8 +171,7 @@
/* 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_be32(buf, P2P_IE_VENDOR_TYPE);
wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
wpabuf_put_buf(buf, tlvs);
gas_anqp_set_element_len(buf, len_pos);
@@ -180,8 +217,7 @@
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);
+ wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
/* Service Update Indicator */
wpabuf_put_le16(buf, update_indic);
wpabuf_put_buf(buf, tlvs);
@@ -212,8 +248,7 @@
/* 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);
+ wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
/* Service Update Indicator */
wpabuf_put_le16(buf, update_indic);
}
@@ -231,6 +266,7 @@
int ret = 0;
struct p2p_sd_query *query;
int freq;
+ unsigned int wait_time;
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
if (freq <= 0) {
@@ -255,13 +291,26 @@
p2p->sd_query = query;
p2p->pending_action_state = P2P_PENDING_SD;
+ wait_time = 5000;
+ if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
+ wait_time = p2p->cfg->max_listen;
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) {
+ wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
ret = -1;
}
+ /* Update the pending broadcast SD query count for this device */
+ dev->sd_pending_bcast_queries--;
+
+ /*
+ * If there are no pending broadcast queries for this device, mark it as
+ * done (-1).
+ */
+ if (dev->sd_pending_bcast_queries == 0)
+ dev->sd_pending_bcast_queries = -1;
+
wpabuf_free(req);
return ret;
@@ -345,17 +394,12 @@
return;
}
- if (WPA_GET_BE24(pos) != OUI_WFA) {
- p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+ p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+ WPA_GET_BE32(pos));
return;
}
- pos += 3;
-
- if (*pos != P2P_OUI_TYPE) {
- p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
- return;
- }
- pos++;
+ pos += 4;
if (pos + 2 > end)
return;
@@ -430,15 +474,7 @@
u16 slen;
u16 update_indic;
-#ifdef ANDROID_P2P
- if (p2p->state != P2P_SD_DURING_FIND) {
- p2p_dbg(p2p, "P2P: #### Not ignoring unexpected GAS Initial Response from "
- MACSTR " state %d", MAC2STR(sa), p2p->state);
- }
- if (p2p->sd_peer == NULL ||
-#else
if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
-#endif
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));
@@ -531,17 +567,12 @@
return;
}
- if (WPA_GET_BE24(pos) != OUI_WFA) {
- p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+ p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+ WPA_GET_BE32(pos));
return;
}
- pos += 3;
-
- if (*pos != P2P_OUI_TYPE) {
- p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
- return;
- }
- pos++;
+ pos += 4;
if (pos + 2 > end)
return;
@@ -549,8 +580,6 @@
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) {
@@ -653,15 +682,7 @@
wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
-#ifdef ANDROID_P2P
- if (p2p->state != P2P_SD_DURING_FIND) {
- p2p_dbg(p2p, "P2P: #### Not ignoring unexpected GAS Comeback Response from "
- MACSTR " state %d", MAC2STR(sa), p2p->state);
- }
- if (p2p->sd_peer == NULL ||
-#else
if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
-#endif
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));
@@ -765,17 +786,12 @@
if (pos + 4 > end)
return;
- if (WPA_GET_BE24(pos) != OUI_WFA) {
- p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+ p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+ WPA_GET_BE32(pos));
return;
}
- pos += 3;
-
- if (*pos != P2P_OUI_TYPE) {
- p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
- return;
- }
- pos++;
+ pos += 4;
if (pos + 2 > end)
return;
@@ -803,8 +819,6 @@
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) {
@@ -835,19 +849,6 @@
const struct wpabuf *tlvs)
{
struct p2p_sd_query *q;
-#ifdef ANDROID_P2P
- /* Currently, supplicant doesn't support more than one pending broadcast SD request.
- * So reject if application is registering another one before cancelling the existing one.
- */
- for (q = p2p->sd_queries; q; q = q->next) {
- if( (q->for_all_peers == 1) && (!dst)) {
- wpa_printf(MSG_ERROR, "P2P: Already one pending"
- " Broadcast request. Please cancel the current one"
- " before adding a new one");
- return NULL;
- }
- }
-#endif
q = os_zalloc(sizeof(*q));
if (q == NULL)
@@ -870,8 +871,16 @@
if (dst == NULL) {
struct p2p_device *dev;
- dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
- dev->flags &= ~P2P_DEV_SD_INFO;
+
+ p2p->num_p2p_sd_queries++;
+
+ /* Update all the devices for the newly added broadcast query */
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (dev->sd_pending_bcast_queries <= 0)
+ dev->sd_pending_bcast_queries = 1;
+ else
+ dev->sd_pending_bcast_queries++;
+ }
}
return q;
@@ -901,9 +910,6 @@
{
if (p2p_unlink_sd_query(p2p, req)) {
p2p_dbg(p2p, "Cancel pending SD query %p", req);
-#ifdef ANDROID_P2P
- p2p->sd_dev_list = NULL;
-#endif
p2p_free_sd_query(req);
return 0;
}
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index a4c48f6..189300a 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -94,6 +94,14 @@
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;
+ case 180: /* 60 GHz band, channels 1..4 */
+ if (channel < 1 || channel > 4)
+ return -1;
+ return 56160 + 2160 * channel;
}
return -1;
}
@@ -204,6 +212,105 @@
}
+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
@@ -244,39 +351,9 @@
}
-#ifdef ANDROID_P2P
-static int p2p_block_op_freq(unsigned int freq)
-{
- return (freq >= 5170 && freq < 5745);
-}
-
-
-size_t p2p_copy_reg_class(struct p2p_reg_class *dc, struct p2p_reg_class *sc)
-{
- unsigned int i;
-
- dc->reg_class = sc->reg_class;
- dc->channels = 0;
- for (i=0; i < sc->channels; i++) {
- if (!p2p_block_op_freq(p2p_channel_to_freq(sc->reg_class,
- sc->channel[i]))) {
- dc->channel[dc->channels] = sc->channel[i];
- dc->channels++;
- }
- }
- return dc->channels;
-}
-#endif
-
-
int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq)
{
u8 op_reg_class, op_channel;
-
-#ifdef ANDROID_P2P
- if (p2p_block_op_freq(freq))
- return 0;
-#endif
if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0)
return 0;
return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
@@ -284,29 +361,142 @@
}
+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;
+ const struct p2p_channels *tmpc = channels ?
+ channels : &p2p->cfg->channels;
- 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;
- }
+ if (tmpc == NULL)
+ return 0;
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))
+ if (p2p_channels_includes_freq(tmpc, 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);
+}
+
+
+static u8 p2p_channel_pick_random(const u8 *channels, unsigned int num_channels)
+{
+ unsigned int r;
+ os_get_random((u8 *) &r, sizeof(r));
+ r %= num_channels;
+ return channels[r];
+}
+
+
+int p2p_channel_select(struct p2p_channels *chans, const int *classes,
+ u8 *op_class, u8 *op_channel)
+{
+ unsigned int i, j;
+
+ for (j = 0; classes == NULL || 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 (classes == NULL || c->reg_class == classes[j]) {
+ /*
+ * Pick one of the available channels in the
+ * operating class at random.
+ */
+ *op_class = c->reg_class;
+ *op_channel = p2p_channel_pick_random(
+ c->channel, c->channels);
+ return 0;
+ }
+ }
+ if (classes == NULL)
+ break;
+ }
+
+ return -1;
+}
+
+
+int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
+ u8 *op_channel)
+{
+ u8 chan[3];
+ unsigned int num_channels = 0;
+
+ /* Try to find available social channels from 2.4 GHz */
+ if (p2p_channels_includes(chans, 81, 1))
+ chan[num_channels++] = 1;
+ if (p2p_channels_includes(chans, 81, 6))
+ chan[num_channels++] = 6;
+ if (p2p_channels_includes(chans, 81, 11))
+ chan[num_channels++] = 11;
+
+ if (num_channels == 0)
+ return -1;
+
+ *op_class = 81;
+ *op_channel = p2p_channel_pick_random(chan, num_channels);
return 0;
}
diff --git a/src/pae/Makefile b/src/pae/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/pae/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
new file mode 100644
index 0000000..cf43c59
--- /dev/null
+++ b/src/pae/ieee802_1x_cp.c
@@ -0,0 +1,744 @@
+/*
+ * IEEE 802.1X-2010 Controlled Port of PAE state machine - CP state machine
+ * Copyright (c) 2013-2014, 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/defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "utils/state_machine.h"
+#include "ieee802_1x_kay.h"
+#include "ieee802_1x_secy_ops.h"
+#include "pae/ieee802_1x_cp.h"
+
+#define STATE_MACHINE_DATA struct ieee802_1x_cp_sm
+#define STATE_MACHINE_DEBUG_PREFIX "CP"
+
+static u8 default_cs_id[] = CS_ID_GCM_AES_128;
+
+/* The variable defined in clause 12 in IEEE Std 802.1X-2010 */
+enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE };
+
+struct ieee802_1x_cp_sm {
+ enum cp_states {
+ CP_BEGIN, CP_INIT, CP_CHANGE, CP_ALLOWED, CP_AUTHENTICATED,
+ CP_SECURED, CP_RECEIVE, CP_RECEIVING, CP_READY, CP_TRANSMIT,
+ CP_TRANSMITTING, CP_ABANDON, CP_RETIRE
+ } CP_state;
+ Boolean changed;
+
+ /* CP -> Client */
+ Boolean port_valid;
+
+ /* Logon -> CP */
+ enum connect_type connect;
+ u8 *authorization_data;
+
+ /* KaY -> CP */
+ Boolean chgd_server; /* clear by CP */
+ Boolean elected_self;
+ u8 *authorization_data1;
+ enum confidentiality_offset cipher_offset;
+ u8 *cipher_suite;
+ Boolean new_sak; /* clear by CP */
+ struct ieee802_1x_mka_ki distributed_ki;
+ u8 distributed_an;
+ Boolean using_receive_sas;
+ Boolean all_receiving;
+ Boolean server_transmitting;
+ Boolean using_transmit_sa;
+
+ /* CP -> KaY */
+ struct ieee802_1x_mka_ki *lki;
+ u8 lan;
+ Boolean ltx;
+ Boolean lrx;
+ struct ieee802_1x_mka_ki *oki;
+ u8 oan;
+ Boolean otx;
+ Boolean orx;
+
+ /* CP -> SecY */
+ Boolean protect_frames;
+ enum validate_frames validate_frames;
+
+ Boolean replay_protect;
+ u32 replay_window;
+
+ u8 *current_cipher_suite;
+ enum confidentiality_offset confidentiality_offset;
+ Boolean controlled_port_enabled;
+
+ /* SecY -> CP */
+ Boolean port_enabled; /* SecY->CP */
+
+ /* private */
+ u32 transmit_when;
+ u32 transmit_delay;
+ u32 retire_when;
+ u32 retire_delay;
+
+ /* not defined IEEE Std 802.1X-2010 */
+ struct ieee802_1x_kay *kay;
+};
+
+static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+
+
+static int changed_cipher(struct ieee802_1x_cp_sm *sm)
+{
+ return sm->confidentiality_offset != sm->cipher_offset ||
+ os_memcmp(sm->current_cipher_suite, sm->cipher_suite,
+ CS_ID_LEN) != 0;
+}
+
+
+static int changed_connect(struct ieee802_1x_cp_sm *sm)
+{
+ return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm);
+}
+
+
+SM_STATE(CP, INIT)
+{
+ SM_ENTRY(CP, INIT);
+
+ sm->controlled_port_enabled = FALSE;
+ secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+
+ sm->port_valid = FALSE;
+
+ os_free(sm->lki);
+ sm->lki = NULL;
+ sm->ltx = FALSE;
+ sm->lrx = FALSE;
+
+ os_free(sm->oki);
+ sm->oki = NULL;
+ sm->otx = FALSE;
+ sm->orx = FALSE;
+
+ sm->port_enabled = TRUE;
+ sm->chgd_server = FALSE;
+}
+
+
+SM_STATE(CP, CHANGE)
+{
+ SM_ENTRY(CP, CHANGE);
+
+ sm->port_valid = FALSE;
+ sm->controlled_port_enabled = FALSE;
+ secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+
+ if (sm->lki)
+ ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
+ if (sm->oki)
+ ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
+}
+
+
+SM_STATE(CP, ALLOWED)
+{
+ SM_ENTRY(CP, ALLOWED);
+
+ sm->protect_frames = FALSE;
+ sm->replay_protect = FALSE;
+ sm->validate_frames = Checked;
+
+ sm->port_valid = FALSE;
+ sm->controlled_port_enabled = TRUE;
+
+ secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+ secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+ secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+ secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, AUTHENTICATED)
+{
+ SM_ENTRY(CP, AUTHENTICATED);
+
+ sm->protect_frames = FALSE;
+ sm->replay_protect = FALSE;
+ sm->validate_frames = Checked;
+
+ sm->port_valid = FALSE;
+ sm->controlled_port_enabled = TRUE;
+
+ secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+ secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+ secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+ secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, SECURED)
+{
+ struct ieee802_1x_cp_conf conf;
+
+ SM_ENTRY(CP, SECURED);
+
+ sm->chgd_server = FALSE;
+
+ ieee802_1x_kay_cp_conf(sm->kay, &conf);
+ sm->protect_frames = conf.protect;
+ sm->replay_protect = conf.replay_protect;
+ sm->validate_frames = conf.validate;
+
+ /* NOTE: now no other than default cipher suiter(AES-GCM-128) */
+ os_memcpy(sm->current_cipher_suite, sm->cipher_suite, CS_ID_LEN);
+ secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite,
+ CS_ID_LEN);
+
+ sm->confidentiality_offset = sm->cipher_offset;
+
+ sm->port_valid = TRUE;
+
+ secy_cp_control_confidentiality_offset(sm->kay,
+ sm->confidentiality_offset);
+ secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+ secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+ secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, RECEIVE)
+{
+ SM_ENTRY(CP, RECEIVE);
+ /* RECEIVE state machine not keep with Figure 12-2 in
+ * IEEE Std 802.1X-2010 */
+ sm->oki = sm->lki;
+ sm->oan = sm->lan;
+ sm->otx = sm->ltx;
+ sm->orx = sm->lrx;
+ ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+ sm->otx, sm->orx);
+
+ sm->lki = os_malloc(sizeof(*sm->lki));
+ if (!sm->lki) {
+ wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__);
+ return;
+ }
+ os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki));
+ sm->lan = sm->distributed_an;
+ sm->ltx = FALSE;
+ sm->lrx = FALSE;
+ ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+ sm->ltx, sm->lrx);
+ ieee802_1x_kay_create_sas(sm->kay, sm->lki);
+ ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki);
+ sm->new_sak = FALSE;
+ sm->all_receiving = FALSE;
+}
+
+
+SM_STATE(CP, RECEIVING)
+{
+ SM_ENTRY(CP, RECEIVING);
+
+ sm->lrx = TRUE;
+ ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+ sm->ltx, sm->lrx);
+ sm->transmit_when = sm->transmit_delay;
+ eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+ eloop_register_timeout(sm->transmit_when / 1000, 0,
+ ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+ /* the electedSelf have been set before CP entering to RECEIVING
+ * but the CP will transmit from RECEIVING to READY under
+ * the !electedSelf when KaY is not key server */
+ ieee802_1x_cp_sm_step(sm);
+ sm->using_receive_sas = FALSE;
+ sm->server_transmitting = FALSE;
+}
+
+
+SM_STATE(CP, READY)
+{
+ SM_ENTRY(CP, READY);
+
+ ieee802_1x_kay_enable_new_info(sm->kay);
+}
+
+
+SM_STATE(CP, TRANSMIT)
+{
+ SM_ENTRY(CP, TRANSMIT);
+
+ sm->controlled_port_enabled = TRUE;
+ secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+ sm->ltx = TRUE;
+ ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+ sm->ltx, sm->lrx);
+ ieee802_1x_kay_enable_tx_sas(sm->kay, sm->lki);
+ sm->all_receiving = FALSE;
+ sm->server_transmitting = FALSE;
+}
+
+
+SM_STATE(CP, TRANSMITTING)
+{
+ SM_ENTRY(CP, TRANSMITTING);
+ sm->retire_when = sm->orx ? sm->retire_delay : 0;
+ sm->otx = FALSE;
+ ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+ sm->otx, sm->orx);
+ ieee802_1x_kay_enable_new_info(sm->kay);
+ eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
+ eloop_register_timeout(sm->retire_when / 1000, 0,
+ ieee802_1x_cp_retire_when_timeout, sm, NULL);
+ sm->using_transmit_sa = FALSE;
+}
+
+
+SM_STATE(CP, ABANDON)
+{
+ SM_ENTRY(CP, ABANDON);
+ sm->lrx = FALSE;
+ ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+ sm->ltx, sm->lrx);
+ ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
+
+ os_free(sm->lki);
+ sm->lki = NULL;
+ ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+ sm->ltx, sm->lrx);
+ sm->new_sak = FALSE;
+}
+
+
+SM_STATE(CP, RETIRE)
+{
+ SM_ENTRY(CP, RETIRE);
+ /* RETIRE state machine not keep with Figure 12-2 in
+ * IEEE Std 802.1X-2010 */
+ os_free(sm->oki);
+ sm->oki = NULL;
+ sm->orx = FALSE;
+ sm->otx = FALSE;
+ ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+ sm->otx, sm->orx);
+}
+
+
+/**
+ * CP state machine handler entry
+ */
+SM_STEP(CP)
+{
+ if (!sm->port_enabled)
+ SM_ENTER(CP, INIT);
+
+ switch (sm->CP_state) {
+ case CP_BEGIN:
+ SM_ENTER(CP, INIT);
+ break;
+
+ case CP_INIT:
+ SM_ENTER(CP, CHANGE);
+ break;
+
+ case CP_CHANGE:
+ if (sm->connect == UNAUTHENTICATED)
+ SM_ENTER(CP, ALLOWED);
+ else if (sm->connect == AUTHENTICATED)
+ SM_ENTER(CP, AUTHENTICATED);
+ else if (sm->connect == SECURE)
+ SM_ENTER(CP, SECURED);
+ break;
+
+ case CP_ALLOWED:
+ if (sm->connect != UNAUTHENTICATED)
+ SM_ENTER(CP, CHANGE);
+ break;
+
+ case CP_AUTHENTICATED:
+ if (sm->connect != AUTHENTICATED)
+ SM_ENTER(CP, CHANGE);
+ break;
+
+ case CP_SECURED:
+ if (changed_connect(sm))
+ SM_ENTER(CP, CHANGE);
+ else if (sm->new_sak)
+ SM_ENTER(CP, RECEIVE);
+ break;
+
+ case CP_RECEIVE:
+ if (sm->using_receive_sas)
+ SM_ENTER(CP, RECEIVING);
+ break;
+
+ case CP_RECEIVING:
+ if (sm->new_sak || changed_connect(sm))
+ SM_ENTER(CP, ABANDON);
+ if (!sm->elected_self)
+ SM_ENTER(CP, READY);
+ if (sm->elected_self &&
+ (sm->all_receiving || !sm->transmit_when))
+ SM_ENTER(CP, TRANSMIT);
+ break;
+
+ case CP_TRANSMIT:
+ if (sm->using_transmit_sa)
+ SM_ENTER(CP, TRANSMITTING);
+ break;
+
+ case CP_TRANSMITTING:
+ if (!sm->retire_when || changed_connect(sm))
+ SM_ENTER(CP, RETIRE);
+ break;
+
+ case CP_RETIRE:
+ if (changed_connect(sm))
+ SM_ENTER(CP, CHANGE);
+ else if (sm->new_sak)
+ SM_ENTER(CP, RECEIVE);
+ break;
+
+ case CP_READY:
+ if (sm->new_sak || changed_connect(sm))
+ SM_ENTER(CP, RECEIVE);
+ if (sm->server_transmitting)
+ SM_ENTER(CP, TRANSMIT);
+ break;
+ case CP_ABANDON:
+ if (changed_connect(sm))
+ SM_ENTER(CP, RETIRE);
+ else if (sm->new_sak)
+ SM_ENTER(CP, RECEIVE);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "CP: the state machine is not defined");
+ break;
+ }
+}
+
+
+/**
+ * ieee802_1x_cp_sm_init -
+ */
+struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(
+ struct ieee802_1x_kay *kay,
+ struct ieee802_1x_cp_conf *pcp_conf)
+{
+ struct ieee802_1x_cp_sm *sm;
+
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL) {
+ wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
+ return NULL;
+ }
+
+ sm->kay = kay;
+
+ sm->port_valid = FALSE;
+
+ sm->chgd_server = FALSE;
+
+ sm->protect_frames = pcp_conf->protect;
+ sm->validate_frames = pcp_conf->validate;
+ sm->replay_protect = pcp_conf->replay_protect;
+ sm->replay_window = pcp_conf->replay_window;
+
+ sm->controlled_port_enabled = FALSE;
+
+ sm->lki = NULL;
+ sm->lrx = FALSE;
+ sm->ltx = FALSE;
+ sm->oki = NULL;
+ sm->orx = FALSE;
+ sm->otx = FALSE;
+
+ sm->cipher_suite = os_zalloc(CS_ID_LEN);
+ sm->current_cipher_suite = os_zalloc(CS_ID_LEN);
+ if (!sm->cipher_suite || !sm->current_cipher_suite) {
+ wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
+ os_free(sm->cipher_suite);
+ os_free(sm->current_cipher_suite);
+ os_free(sm);
+ return NULL;
+ }
+ os_memcpy(sm->current_cipher_suite, default_cs_id, CS_ID_LEN);
+ os_memcpy(sm->cipher_suite, default_cs_id, CS_ID_LEN);
+ sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
+ sm->confidentiality_offset = sm->cipher_offset;
+ sm->transmit_delay = MKA_LIFE_TIME;
+ sm->retire_delay = MKA_SAK_RETIRE_TIME;
+ sm->CP_state = CP_BEGIN;
+ sm->changed = FALSE;
+ sm->authorization_data = NULL;
+
+ wpa_printf(MSG_DEBUG, "CP: state machine created");
+
+ secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+ secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+ secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+ secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+ secy_cp_control_confidentiality_offset(sm->kay,
+ sm->confidentiality_offset);
+
+ SM_ENTER(CP, INIT);
+ SM_STEP_RUN(CP);
+
+ return sm;
+}
+
+
+static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm)
+{
+ enum cp_states prev_state;
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ prev_state = sm->CP_state;
+ SM_STEP_RUN(CP);
+ if (prev_state == sm->CP_state)
+ break;
+ }
+}
+
+
+static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = eloop_ctx;
+ ieee802_1x_cp_step_run(sm);
+}
+
+
+/**
+ * ieee802_1x_cp_sm_deinit -
+ */
+void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "CP: state machine removed");
+ if (!sm)
+ return;
+
+ eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
+ eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+ eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
+ os_free(sm->lki);
+ os_free(sm->oki);
+ os_free(sm->cipher_suite);
+ os_free(sm->current_cipher_suite);
+ os_free(sm->authorization_data);
+ os_free(sm);
+}
+
+
+/**
+ * ieee802_1x_cp_connect_pending
+ */
+void ieee802_1x_cp_connect_pending(void *cp_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+ sm->connect = PENDING;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_unauthenticated
+ */
+void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx;
+
+ sm->connect = UNAUTHENTICATED;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_authenticated
+ */
+void ieee802_1x_cp_connect_authenticated(void *cp_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+ sm->connect = AUTHENTICATED;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_secure
+ */
+void ieee802_1x_cp_connect_secure(void *cp_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+ sm->connect = SECURE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_chgdserver -
+ */
+void ieee802_1x_cp_signal_chgdserver(void *cp_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+ sm->chgd_server = TRUE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_electedself -
+ */
+void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ sm->elected_self = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_authorizationdata -
+ */
+void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ os_free(sm->authorization_data);
+ sm->authorization_data = os_zalloc(len);
+ if (sm->authorization_data)
+ os_memcpy(sm->authorization_data, pdata, len);
+}
+
+
+/**
+ * ieee802_1x_cp_set_ciphersuite -
+ */
+void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ os_memcpy(sm->cipher_suite, pid, CS_ID_LEN);
+}
+
+
+/**
+ * ieee802_1x_cp_set_offset -
+ */
+void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ sm->cipher_offset = offset;
+}
+
+
+/**
+ * ieee802_1x_cp_signal_newsak -
+ */
+void ieee802_1x_cp_signal_newsak(void *cp_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ sm->new_sak = TRUE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_distributedki -
+ */
+void ieee802_1x_cp_set_distributedki(void *cp_ctx,
+ const struct ieee802_1x_mka_ki *dki)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki));
+}
+
+
+/**
+ * ieee802_1x_cp_set_distributedan -
+ */
+void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ sm->distributed_an = an;
+}
+
+
+/**
+ * ieee802_1x_cp_set_usingreceivesas -
+ */
+void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ sm->using_receive_sas = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_allreceiving -
+ */
+void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ sm->all_receiving = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_servertransmitting -
+ */
+void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ sm->server_transmitting = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_usingtransmitsas -
+ */
+void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status)
+{
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ sm->using_transmit_sa = status;
+}
+
+
+/**
+ * ieee802_1x_cp_sm_step - Advance EAPOL state machines
+ * @sm: EAPOL state machine
+ *
+ * This function is called to advance CP state machines after any change
+ * that could affect their state.
+ */
+void ieee802_1x_cp_sm_step(void *cp_ctx)
+{
+ /*
+ * Run ieee802_1x_cp_step_run from a registered timeout
+ * to make sure that other possible timeouts/events are processed
+ * and to avoid long function call chains.
+ */
+ struct ieee802_1x_cp_sm *sm = cp_ctx;
+ eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
+ eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL);
+}
+
+
+static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = eloop_ctx;
+ sm->retire_when = 0;
+ ieee802_1x_cp_step_run(sm);
+}
+
+
+static void
+ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct ieee802_1x_cp_sm *sm = eloop_ctx;
+ sm->transmit_when = 0;
+ ieee802_1x_cp_step_run(sm);
+}
diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h
new file mode 100644
index 0000000..773c930
--- /dev/null
+++ b/src/pae/ieee802_1x_cp.h
@@ -0,0 +1,50 @@
+/*
+ * IEEE Std 802.1X-2010 Controlled Port of PAE state machine - CP state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_CP_H
+#define IEEE802_1X_CP_H
+
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct ieee802_1x_cp_sm;
+struct ieee802_1x_kay;
+struct ieee802_1x_mka_ki;
+
+struct ieee802_1x_cp_conf {
+ Boolean protect;
+ Boolean replay_protect;
+ enum validate_frames validate;
+ u32 replay_window;
+};
+
+
+struct ieee802_1x_cp_sm *
+ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_cp_conf *pcp_conf);
+void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm);
+void ieee802_1x_cp_sm_step(void *cp_ctx);
+void ieee802_1x_cp_connect_pending(void *cp_ctx);
+void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx);
+void ieee802_1x_cp_connect_authenticated(void *cp_ctx);
+void ieee802_1x_cp_connect_secure(void *cp_ctx);
+void ieee802_1x_cp_signal_chgdserver(void *cp_ctx);
+void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len);
+void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid);
+void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset);
+void ieee802_1x_cp_signal_newsak(void *cp_ctx);
+void ieee802_1x_cp_set_distributedki(void *cp_ctx,
+ const struct ieee802_1x_mka_ki *dki);
+void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an);
+void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status);
+
+#endif /* IEEE802_1X_CP_H */
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
new file mode 100644
index 0000000..56c195a
--- /dev/null
+++ b/src/pae/ieee802_1x_kay.c
@@ -0,0 +1,3527 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <time.h>
+#include "includes.h"
+#include "common.h"
+#include "list.h"
+#include "eloop.h"
+#include "wpabuf.h"
+#include "state_machine.h"
+#include "l2_packet/l2_packet.h"
+#include "common/eapol_common.h"
+#include "crypto/aes_wrap.h"
+#include "ieee802_1x_cp.h"
+#include "ieee802_1x_key.h"
+#include "ieee802_1x_kay.h"
+#include "ieee802_1x_kay_i.h"
+#include "ieee802_1x_secy_ops.h"
+
+
+#define DEFAULT_SA_KEY_LEN 16
+#define DEFAULT_ICV_LEN 16
+#define MAX_ICV_LEN 32 /* 32 bytes, 256 bits */
+
+#define PENDING_PN_EXHAUSTION 0xC0000000
+
+/* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */
+#define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 }
+static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009;
+
+/* IEEE802.1AE-2006 Table 14-1 MACsec Cipher Suites */
+static struct macsec_ciphersuite cipher_suite_tbl[] = {
+ /* GCM-AES-128 */
+ {
+ CS_ID_GCM_AES_128,
+ CS_NAME_GCM_AES_128,
+ MACSEC_CAP_INTEG_AND_CONF_0_30_50,
+ 16,
+
+ 0 /* index */
+ },
+};
+#define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
+#define DEFAULT_CS_INDEX 0
+
+static struct mka_alg mka_alg_tbl[] = {
+ {
+ MKA_ALGO_AGILITY_2009,
+ /* 128-bit CAK, KEK, ICK, ICV */
+ 16, 16, 16, 16,
+ ieee802_1x_cak_128bits_aes_cmac,
+ ieee802_1x_ckn_128bits_aes_cmac,
+ ieee802_1x_kek_128bits_aes_cmac,
+ ieee802_1x_ick_128bits_aes_cmac,
+ ieee802_1x_icv_128bits_aes_cmac,
+
+ 1, /* index */
+ },
+};
+#define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl))
+
+
+static int is_ki_equal(struct ieee802_1x_mka_ki *ki1,
+ struct ieee802_1x_mka_ki *ki2)
+{
+ return os_memcmp(ki1->mi, ki2->mi, MI_LEN) == 0 &&
+ ki1->kn == ki2->kn;
+}
+
+
+struct mka_param_body_handler {
+ int (*body_tx)(struct ieee802_1x_mka_participant *participant,
+ struct wpabuf *buf);
+ int (*body_rx)(struct ieee802_1x_mka_participant *participant,
+ const u8 *mka_msg, size_t msg_len);
+ int (*body_length)(struct ieee802_1x_mka_participant *participant);
+ Boolean (*body_present)(struct ieee802_1x_mka_participant *participant);
+};
+
+
+static void set_mka_param_body_len(void *body, unsigned int len)
+{
+ struct ieee802_1x_mka_hdr *hdr = body;
+ hdr->length = (len >> 8) & 0x0f;
+ hdr->length1 = len & 0xff;
+}
+
+
+static unsigned int get_mka_param_body_len(const void *body)
+{
+ const struct ieee802_1x_mka_hdr *hdr = body;
+ return (hdr->length << 8) | hdr->length1;
+}
+
+
+static int get_mka_param_body_type(const void *body)
+{
+ const struct ieee802_1x_mka_hdr *hdr = body;
+ return hdr->type;
+}
+
+
+/**
+ * ieee802_1x_mka_dump_basic_body -
+ */
+static void
+ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body)
+{
+ size_t body_len;
+
+ if (!body)
+ return;
+
+ body_len = get_mka_param_body_len(body);
+ wpa_printf(MSG_DEBUG, "*** MKA Basic Parameter set ***");
+ wpa_printf(MSG_DEBUG, "\tVersion.......: %d", body->version);
+ wpa_printf(MSG_DEBUG, "\tPriority......: %d", body->priority);
+ wpa_printf(MSG_DEBUG, "\tKeySvr........: %d", body->key_server);
+ wpa_printf(MSG_DEBUG, "\tMACSecDesired.: %d", body->macsec_desired);
+ wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capbility);
+ wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+ wpa_printf(MSG_DEBUG, "\tSCI MAC.......: " MACSTR,
+ MAC2STR(body->actor_sci.addr));
+ wpa_printf(MSG_DEBUG, "\tSCI Port .....: %d",
+ be_to_host16(body->actor_sci.port));
+ wpa_hexdump(MSG_DEBUG, "\tMember Id.....:",
+ body->actor_mi, sizeof(body->actor_mi));
+ wpa_printf(MSG_DEBUG, "\tMessage Number: %d",
+ be_to_host32(body->actor_mn));
+ wpa_hexdump(MSG_DEBUG, "\tAlgo Agility..:",
+ body->algo_agility, sizeof(body->algo_agility));
+ wpa_hexdump_ascii(MSG_DEBUG, "\tCAK Name......:", body->ckn,
+ body_len + MKA_HDR_LEN - sizeof(*body));
+}
+
+
+/**
+ * ieee802_1x_mka_dump_peer_body -
+ */
+static void
+ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body)
+{
+ size_t body_len;
+ size_t i;
+ u8 *mi;
+ u32 mn;
+
+ if (body == NULL)
+ return;
+
+ body_len = get_mka_param_body_len(body);
+ if (body->type == MKA_LIVE_PEER_LIST) {
+ wpa_printf(MSG_DEBUG, "*** Live Peer List ***");
+ wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+ } else if (body->type == MKA_POTENTIAL_PEER_LIST) {
+ wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***");
+ wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+ }
+
+ for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) {
+ mi = body->peer + i;
+ os_memcpy(&mn, mi + MI_LEN, sizeof(mn));
+ wpa_hexdump_ascii(MSG_DEBUG, "\tMember Id.....:", mi, MI_LEN);
+ wpa_printf(MSG_DEBUG, "\tMessage Number: %d", be_to_host32(mn));
+ }
+}
+
+
+/**
+ * ieee802_1x_mka_dump_dist_sak_body -
+ */
+static void
+ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body)
+{
+ size_t body_len;
+
+ if (body == NULL)
+ return;
+
+ body_len = get_mka_param_body_len(body);
+ wpa_printf(MSG_INFO, "*** Distributed SAK ***");
+ wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan);
+ wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d",
+ body->confid_offset);
+ wpa_printf(MSG_INFO, "\tBody Length...........: %d", (int) body_len);
+ if (!body_len)
+ return;
+
+ wpa_printf(MSG_INFO, "\tKey Number............: %d",
+ be_to_host32(body->kn));
+ wpa_hexdump(MSG_INFO, "\tAES Key Wrap of SAK...:", body->sak, 24);
+}
+
+
+static const char * yes_no(int val)
+{
+ return val ? "Yes" : "No";
+}
+
+
+/**
+ * ieee802_1x_mka_dump_sak_use_body -
+ */
+static void
+ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body)
+{
+ int body_len;
+
+ if (body == NULL)
+ return;
+
+ body_len = get_mka_param_body_len(body);
+ wpa_printf(MSG_DEBUG, "*** MACsec SAK Use ***");
+ wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan);
+ wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx));
+ wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx));
+ wpa_printf(MSG_DEBUG, "\tOld Key AN....: %d", body->oan);
+ wpa_printf(MSG_DEBUG, "\tOld Key Tx....: %s", yes_no(body->otx));
+ wpa_printf(MSG_DEBUG, "\tOld Key Rx....: %s", yes_no(body->orx));
+ wpa_printf(MSG_DEBUG, "\tPlain Key Tx....: %s", yes_no(body->ptx));
+ wpa_printf(MSG_DEBUG, "\tPlain Key Rx....: %s", yes_no(body->prx));
+ wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s",
+ yes_no(body->delay_protect));
+ wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len);
+ if (!body_len)
+ return;
+
+ wpa_hexdump(MSG_DEBUG, "\tKey Server MI....:",
+ body->lsrv_mi, sizeof(body->lsrv_mi));
+ wpa_printf(MSG_DEBUG, "\tKey Number.......: %u",
+ be_to_host32(body->lkn));
+ wpa_printf(MSG_DEBUG, "\tLowest PN........: %u",
+ be_to_host32(body->llpn));
+ wpa_hexdump_ascii(MSG_DEBUG, "\tOld Key Server MI....:",
+ body->osrv_mi, sizeof(body->osrv_mi));
+ wpa_printf(MSG_DEBUG, "\tOld Key Number.......: %u",
+ be_to_host32(body->okn));
+ wpa_printf(MSG_DEBUG, "\tOld Lowest PN........: %u",
+ be_to_host32(body->olpn));
+}
+
+
+/**
+ * ieee802_1x_kay_get_participant -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn)
+{
+ struct ieee802_1x_mka_participant *participant;
+
+ dl_list_for_each(participant, &kay->participant_list,
+ struct ieee802_1x_mka_participant, list) {
+ if (os_memcmp(participant->ckn.name, ckn,
+ participant->ckn.len) == 0)
+ return participant;
+ }
+
+ wpa_printf(MSG_DEBUG, "KaY: participant is not found");
+
+ return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_get_principal_participant -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay)
+{
+ struct ieee802_1x_mka_participant *participant;
+
+ dl_list_for_each(participant, &kay->participant_list,
+ struct ieee802_1x_mka_participant, list) {
+ if (participant->principal)
+ return participant;
+ }
+
+ wpa_printf(MSG_DEBUG, "KaY: principal participant is not founded");
+ return NULL;
+}
+
+
+static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers,
+ const u8 *mi)
+{
+ struct ieee802_1x_kay_peer *peer;
+
+ dl_list_for_each(peer, peers, struct ieee802_1x_kay_peer, list) {
+ if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
+ return peer;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_potential_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_potential_peer(
+ struct ieee802_1x_mka_participant *participant, const u8 *mi)
+{
+ return get_peer_mi(&participant->potential_peers, mi) != NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_live_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_live_peer(
+ struct ieee802_1x_mka_participant *participant, const u8 *mi)
+{
+ return get_peer_mi(&participant->live_peers, mi) != NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_peer(struct ieee802_1x_mka_participant *participant,
+ const u8 *mi)
+{
+ return ieee802_1x_kay_is_in_live_peer(participant, mi) ||
+ ieee802_1x_kay_is_in_potential_peer(participant, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
+ const u8 *mi)
+{
+ struct ieee802_1x_kay_peer *peer;
+
+ peer = get_peer_mi(&participant->live_peers, mi);
+ if (peer)
+ return peer;
+
+ return get_peer_mi(&participant->potential_peers, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant,
+ const u8 *mi)
+{
+ return get_peer_mi(&participant->live_peers, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_cipher_suite
+ */
+static struct macsec_ciphersuite *
+ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
+ u8 *cs_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CS_TABLE_SIZE; i++) {
+ if (os_memcmp(cipher_suite_tbl[i].id, cs_id, CS_ID_LEN) == 0)
+ break;
+ }
+ if (i >= CS_TABLE_SIZE)
+ return NULL;
+
+ return &cipher_suite_tbl[i];
+}
+
+
+/**
+ * ieee802_1x_kay_get_peer_sci
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant,
+ const struct ieee802_1x_mka_sci *sci)
+{
+ struct ieee802_1x_kay_peer *peer;
+
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list) {
+ if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0)
+ return peer;
+ }
+
+ dl_list_for_each(peer, &participant->potential_peers,
+ struct ieee802_1x_kay_peer, list) {
+ if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0)
+ return peer;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_init_receive_sa -
+ */
+static struct receive_sa *
+ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
+ struct data_key *key)
+{
+ struct receive_sa *psa;
+
+ if (!psc || !key)
+ return NULL;
+
+ psa = os_zalloc(sizeof(*psa));
+ if (!psa) {
+ wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+ return NULL;
+ }
+
+ psa->pkey = key;
+ psa->lowest_pn = lowest_pn;
+ psa->next_pn = lowest_pn;
+ psa->an = an;
+ psa->sc = psc;
+
+ os_get_time(&psa->created_time);
+ psa->in_use = FALSE;
+
+ dl_list_add(&psc->sa_list, &psa->list);
+ wpa_printf(MSG_DEBUG,
+ "KaY: Create receive SA(AN: %d lowest_pn: %u of SC(channel: %d)",
+ (int) an, lowest_pn, psc->channel);
+
+ return psa;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_receive_sa -
+ */
+static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
+{
+ psa->pkey = NULL;
+ wpa_printf(MSG_DEBUG,
+ "KaY: Delete receive SA(an: %d) of SC(channel: %d)",
+ psa->an, psa->sc->channel);
+ dl_list_del(&psa->list);
+ os_free(psa);
+}
+
+
+/**
+ * ieee802_1x_kay_init_receive_sc -
+ */
+static struct receive_sc *
+ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci,
+ int channel)
+{
+ struct receive_sc *psc;
+
+ if (!psci)
+ return NULL;
+
+ psc = os_zalloc(sizeof(*psc));
+ if (!psc) {
+ wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+ return NULL;
+ }
+
+ os_memcpy(&psc->sci, psci, sizeof(psc->sci));
+ psc->channel = channel;
+
+ os_get_time(&psc->created_time);
+ psc->receiving = FALSE;
+
+ dl_list_init(&psc->sa_list);
+ wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel);
+ wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci));
+
+ return psc;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_receive_sc -
+ **/
+static void
+ieee802_1x_kay_deinit_receive_sc(
+ struct ieee802_1x_mka_participant *participant, struct receive_sc *psc)
+{
+ struct receive_sa *psa, *pre_sa;
+
+ wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)",
+ psc->channel);
+ dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
+ list) {
+ secy_disable_receive_sa(participant->kay, psa);
+ ieee802_1x_kay_deinit_receive_sa(psa);
+ }
+ dl_list_del(&psc->list);
+ os_free(psc);
+}
+
+
+/**
+ * ieee802_1x_kay_create_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
+ u8 *mi, u32 mn)
+{
+ struct ieee802_1x_kay_peer *peer;
+ struct receive_sc *rxsc;
+ u32 sc_ch = 0;
+
+ peer = os_zalloc(sizeof(*peer));
+ if (peer == NULL) {
+ wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+ return NULL;
+ }
+
+ os_memcpy(peer->mi, mi, MI_LEN);
+ peer->mn = mn;
+ peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+ peer->sak_used = FALSE;
+ os_memcpy(&peer->sci, &participant->current_peer_sci,
+ sizeof(peer->sci));
+ dl_list_add(&participant->live_peers, &peer->list);
+
+ secy_get_available_receive_sc(participant->kay, &sc_ch);
+
+ rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
+ if (!rxsc)
+ return NULL;
+
+ dl_list_add(&participant->rxsc_list, &rxsc->list);
+ secy_create_receive_sc(participant->kay, rxsc);
+
+ wpa_printf(MSG_DEBUG, "KaY: Live peer created");
+ wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+ wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+ wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+ return peer;
+}
+
+
+/**
+ * ieee802_1x_kay_create_potential_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_create_potential_peer(
+ struct ieee802_1x_mka_participant *participant, const u8 *mi, u32 mn)
+{
+ struct ieee802_1x_kay_peer *peer;
+
+ peer = os_zalloc(sizeof(*peer));
+ if (peer == NULL) {
+ wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+ return NULL;
+ }
+
+ os_memcpy(peer->mi, mi, MI_LEN);
+ peer->mn = mn;
+ peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+ peer->sak_used = FALSE;
+
+ dl_list_add(&participant->potential_peers, &peer->list);
+
+ wpa_printf(MSG_DEBUG, "KaY: potential peer created");
+ wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+ wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+ wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+ return peer;
+}
+
+
+/**
+ * ieee802_1x_kay_move_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
+ u8 *mi, u32 mn)
+{
+ struct ieee802_1x_kay_peer *peer;
+ struct receive_sc *rxsc;
+ u32 sc_ch = 0;
+
+ dl_list_for_each(peer, &participant->potential_peers,
+ struct ieee802_1x_kay_peer, list) {
+ if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
+ break;
+ }
+
+ os_memcpy(&peer->sci, &participant->current_peer_sci,
+ sizeof(peer->sci));
+ peer->mn = mn;
+ peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+
+ wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer");
+ wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+ wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+ wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+ dl_list_del(&peer->list);
+ dl_list_add_tail(&participant->live_peers, &peer->list);
+
+ secy_get_available_receive_sc(participant->kay, &sc_ch);
+
+ rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
+ if (!rxsc)
+ return NULL;
+
+ dl_list_add(&participant->rxsc_list, &rxsc->list);
+ secy_create_receive_sc(participant->kay, rxsc);
+
+ return peer;
+}
+
+
+
+/**
+ * ieee802_1x_mka_basic_body_present -
+ */
+static Boolean
+ieee802_1x_mka_basic_body_present(
+ struct ieee802_1x_mka_participant *participant)
+{
+ return TRUE;
+}
+
+
+/**
+ * ieee802_1x_mka_basic_body_length -
+ */
+static int
+ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant)
+{
+ int length;
+
+ length = sizeof(struct ieee802_1x_mka_basic_body);
+ length += participant->ckn.len;
+ return (length + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_basic_body
+ */
+static int
+ieee802_1x_mka_encode_basic_body(
+ struct ieee802_1x_mka_participant *participant,
+ struct wpabuf *buf)
+{
+ struct ieee802_1x_mka_basic_body *body;
+ struct ieee802_1x_kay *kay = participant->kay;
+ unsigned int length = ieee802_1x_mka_basic_body_length(participant);
+
+ body = wpabuf_put(buf, length);
+
+ body->version = kay->mka_version;
+ body->priority = kay->actor_priority;
+ if (participant->is_elected)
+ body->key_server = participant->is_key_server;
+ else
+ body->key_server = participant->can_be_key_server;
+
+ body->macsec_desired = kay->macsec_desired;
+ body->macsec_capbility = kay->macsec_capable;
+ set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+ os_memcpy(body->actor_sci.addr, kay->actor_sci.addr,
+ sizeof(kay->actor_sci.addr));
+ body->actor_sci.port = host_to_be16(kay->actor_sci.port);
+
+ os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi));
+ participant->mn = participant->mn + 1;
+ body->actor_mn = host_to_be32(participant->mn);
+ os_memcpy(body->algo_agility, participant->kay->algo_agility,
+ sizeof(body->algo_agility));
+
+ os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len);
+
+ ieee802_1x_mka_dump_basic_body(body);
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_basic_body -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
+ size_t msg_len)
+{
+ struct ieee802_1x_mka_participant *participant;
+ const struct ieee802_1x_mka_basic_body *body;
+ struct ieee802_1x_kay_peer *peer;
+
+ body = (const struct ieee802_1x_mka_basic_body *) mka_msg;
+
+ if (body->version > MKA_VERSION_ID) {
+ wpa_printf(MSG_DEBUG,
+ "KaY: peer's version(%d) greater than mka current version(%d)",
+ body->version, MKA_VERSION_ID);
+ }
+ if (kay->is_obliged_key_server && body->key_server) {
+ wpa_printf(MSG_DEBUG, "I must be as key server");
+ return NULL;
+ }
+
+ participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+ if (!participant) {
+ wpa_printf(MSG_DEBUG, "Peer is not included in my CA");
+ return NULL;
+ }
+
+ /* If the peer's MI is my MI, I will choose new MI */
+ if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
+ os_get_random(participant->mi, sizeof(participant->mi));
+ participant->mn = 0;
+ }
+
+ os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN);
+ participant->current_peer_id.mn = be_to_host32(body->actor_mn);
+ os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr,
+ sizeof(participant->current_peer_sci.addr));
+ participant->current_peer_sci.port = be_to_host16(body->actor_sci.port);
+
+ /* handler peer */
+ peer = ieee802_1x_kay_get_peer(participant, body->actor_mi);
+ if (!peer) {
+ /* Check duplicated SCI */
+ /* TODO: What policy should be applied to detect duplicated SCI
+ * is active attacker or a valid peer whose MI is be changed?
+ */
+ peer = ieee802_1x_kay_get_peer_sci(participant,
+ &body->actor_sci);
+ if (peer) {
+ wpa_printf(MSG_WARNING,
+ "KaY: duplicated SCI detected, Maybe active attacker");
+ dl_list_del(&peer->list);
+ os_free(peer);
+ }
+
+ peer = ieee802_1x_kay_create_potential_peer(
+ participant, body->actor_mi,
+ be_to_host32(body->actor_mn));
+ if (!peer)
+ return NULL;
+
+ peer->macsec_desired = body->macsec_desired;
+ peer->macsec_capbility = body->macsec_capbility;
+ peer->is_key_server = (Boolean) body->key_server;
+ peer->key_server_priority = body->priority;
+ } else if (peer->mn < be_to_host32(body->actor_mn)) {
+ peer->mn = be_to_host32(body->actor_mn);
+ peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+ peer->macsec_desired = body->macsec_desired;
+ peer->macsec_capbility = body->macsec_capbility;
+ peer->is_key_server = (Boolean) body->key_server;
+ peer->key_server_priority = body->priority;
+ } else {
+ wpa_printf(MSG_WARNING, "KaY: The peer MN have received");
+ return NULL;
+ }
+
+ return participant;
+}
+
+
+/**
+ * ieee802_1x_mka_live_peer_body_present
+ */
+static Boolean
+ieee802_1x_mka_live_peer_body_present(
+ struct ieee802_1x_mka_participant *participant)
+{
+ return !dl_list_empty(&participant->live_peers);
+}
+
+
+/**
+ * ieee802_1x_kay_get_live_peer_length
+ */
+static int
+ieee802_1x_mka_get_live_peer_length(
+ struct ieee802_1x_mka_participant *participant)
+{
+ int len = MKA_HDR_LEN;
+ struct ieee802_1x_kay_peer *peer;
+
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list)
+ len += sizeof(struct ieee802_1x_mka_peer_id);
+
+ return (len + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_live_peer_body -
+ */
+static int
+ieee802_1x_mka_encode_live_peer_body(
+ struct ieee802_1x_mka_participant *participant,
+ struct wpabuf *buf)
+{
+ struct ieee802_1x_mka_peer_body *body;
+ struct ieee802_1x_kay_peer *peer;
+ unsigned int length;
+ struct ieee802_1x_mka_peer_id *body_peer;
+
+ length = ieee802_1x_mka_get_live_peer_length(participant);
+ body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
+
+ body->type = MKA_LIVE_PEER_LIST;
+ set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list) {
+ body_peer = wpabuf_put(buf,
+ sizeof(struct ieee802_1x_mka_peer_id));
+ os_memcpy(body_peer->mi, peer->mi, MI_LEN);
+ body_peer->mn = host_to_be32(peer->mn);
+ body_peer++;
+ }
+
+ ieee802_1x_mka_dump_peer_body(body);
+ return 0;
+}
+
+/**
+ * ieee802_1x_mka_potential_peer_body_present
+ */
+static Boolean
+ieee802_1x_mka_potential_peer_body_present(
+ struct ieee802_1x_mka_participant *participant)
+{
+ return !dl_list_empty(&participant->potential_peers);
+}
+
+
+/**
+ * ieee802_1x_kay_get_potential_peer_length
+ */
+static int
+ieee802_1x_mka_get_potential_peer_length(
+ struct ieee802_1x_mka_participant *participant)
+{
+ int len = MKA_HDR_LEN;
+ struct ieee802_1x_kay_peer *peer;
+
+ dl_list_for_each(peer, &participant->potential_peers,
+ struct ieee802_1x_kay_peer, list)
+ len += sizeof(struct ieee802_1x_mka_peer_id);
+
+ return (len + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_potential_peer_body -
+ */
+static int
+ieee802_1x_mka_encode_potential_peer_body(
+ struct ieee802_1x_mka_participant *participant,
+ struct wpabuf *buf)
+{
+ struct ieee802_1x_mka_peer_body *body;
+ struct ieee802_1x_kay_peer *peer;
+ unsigned int length;
+ struct ieee802_1x_mka_peer_id *body_peer;
+
+ length = ieee802_1x_mka_get_potential_peer_length(participant);
+ body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
+
+ body->type = MKA_POTENTIAL_PEER_LIST;
+ set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+ dl_list_for_each(peer, &participant->potential_peers,
+ struct ieee802_1x_kay_peer, list) {
+ body_peer = wpabuf_put(buf,
+ sizeof(struct ieee802_1x_mka_peer_id));
+ os_memcpy(body_peer->mi, peer->mi, MI_LEN);
+ body_peer->mn = host_to_be32(peer->mn);
+ body_peer++;
+ }
+
+ ieee802_1x_mka_dump_peer_body(body);
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_i_in_peerlist -
+ */
+static Boolean
+ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
+ const u8 *mka_msg, size_t msg_len)
+{
+ Boolean included = FALSE;
+ struct ieee802_1x_mka_hdr *hdr;
+ size_t body_len;
+ size_t left_len;
+ int body_type;
+ u32 peer_mn;
+ const u8 *peer_mi;
+ const u8 *pos;
+ size_t i;
+
+ pos = mka_msg;
+ left_len = msg_len;
+ while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) {
+ hdr = (struct ieee802_1x_mka_hdr *) pos;
+ body_len = get_mka_param_body_len(hdr);
+ body_type = get_mka_param_body_type(hdr);
+
+ if (body_type != MKA_LIVE_PEER_LIST &&
+ body_type != MKA_POTENTIAL_PEER_LIST)
+ goto SKIP_PEER;
+
+ ieee802_1x_mka_dump_peer_body(
+ (struct ieee802_1x_mka_peer_body *)pos);
+
+ if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+ wpa_printf(MSG_ERROR,
+ "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+ (int) left_len, (int) MKA_HDR_LEN,
+ (int) body_len, DEFAULT_ICV_LEN);
+ goto SKIP_PEER;
+ }
+
+ if ((body_len % 16) != 0) {
+ wpa_printf(MSG_ERROR,
+ "KaY: MKA Peer Packet Body Length (%d bytes) should multiple of 16 octets",
+ (int) body_len);
+ goto SKIP_PEER;
+ }
+
+ for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+ peer_mi = MKA_HDR_LEN + pos + i;
+ os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+ peer_mn = be_to_host32(peer_mn);
+ if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0 &&
+ peer_mn == participant->mn) {
+ included = TRUE;
+ break;
+ }
+ }
+
+ if (included)
+ return TRUE;
+
+SKIP_PEER:
+ left_len -= body_len + MKA_HDR_LEN;
+ pos += body_len + MKA_HDR_LEN;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_live_peer_body -
+ */
+static int ieee802_1x_mka_decode_live_peer_body(
+ struct ieee802_1x_mka_participant *participant,
+ const u8 *peer_msg, size_t msg_len)
+{
+ const struct ieee802_1x_mka_hdr *hdr;
+ struct ieee802_1x_kay_peer *peer;
+ size_t body_len;
+ u32 peer_mn;
+ const u8 *peer_mi;
+ size_t i;
+ Boolean is_included;
+
+ is_included = ieee802_1x_kay_is_in_live_peer(
+ participant, participant->current_peer_id.mi);
+
+ hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
+ body_len = get_mka_param_body_len(hdr);
+
+ for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+ peer_mi = MKA_HDR_LEN + peer_msg + i;
+ os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+ peer_mn = be_to_host32(peer_mn);
+
+ /* it is myself */
+ if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
+ /* My message id is used by other participant */
+ if (peer_mn > participant->mn) {
+ os_get_random(participant->mi,
+ sizeof(participant->mi));
+ participant->mn = 0;
+ }
+ continue;
+ }
+ if (!is_included)
+ continue;
+
+ peer = ieee802_1x_kay_get_peer(participant, peer_mi);
+ if (NULL != peer) {
+ peer->mn = peer_mn;
+ peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+ } else {
+ if (!ieee802_1x_kay_create_potential_peer(
+ participant, peer_mi, peer_mn)) {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_potential_peer_body -
+ */
+static int
+ieee802_1x_mka_decode_potential_peer_body(
+ struct ieee802_1x_mka_participant *participant,
+ const u8 *peer_msg, size_t msg_len)
+{
+ struct ieee802_1x_mka_hdr *hdr;
+ size_t body_len;
+ u32 peer_mn;
+ const u8 *peer_mi;
+ size_t i;
+
+ hdr = (struct ieee802_1x_mka_hdr *) peer_msg;
+ body_len = get_mka_param_body_len(hdr);
+
+ for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+ peer_mi = MKA_HDR_LEN + peer_msg + i;
+ os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+ peer_mn = be_to_host32(peer_mn);
+
+ /* it is myself */
+ if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
+ /* My message id is used by other participant */
+ if (peer_mn > participant->mn) {
+ os_get_random(participant->mi,
+ sizeof(participant->mi));
+ participant->mn = 0;
+ }
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_sak_use_body_present
+ */
+static Boolean
+ieee802_1x_mka_sak_use_body_present(
+ struct ieee802_1x_mka_participant *participant)
+{
+ if (participant->to_use_sak)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+/**
+ * ieee802_1x_mka_get_sak_use_length
+ */
+static int
+ieee802_1x_mka_get_sak_use_length(
+ struct ieee802_1x_mka_participant *participant)
+{
+ int length = MKA_HDR_LEN;
+
+ if (participant->kay->macsec_desired && participant->advised_desired)
+ length = sizeof(struct ieee802_1x_mka_sak_use_body);
+ else
+ length = MKA_HDR_LEN;
+
+ length = (length + 0x3) & ~0x3;
+
+ return length;
+}
+
+
+/**
+ *
+ */
+static u32
+ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
+ struct ieee802_1x_mka_ki *ki)
+{
+ struct receive_sa *rxsa;
+ struct receive_sc *rxsc;
+ u32 lpn = 0;
+
+ dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+ dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
+ {
+ if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
+ secy_get_receive_lowest_pn(principal->kay,
+ rxsa);
+
+ lpn = lpn > rxsa->lowest_pn ?
+ lpn : rxsa->lowest_pn;
+ break;
+ }
+ }
+ }
+
+ if (lpn == 0)
+ lpn = 1;
+
+ return lpn;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_sak_use_body -
+ */
+static int
+ieee802_1x_mka_encode_sak_use_body(
+ struct ieee802_1x_mka_participant *participant,
+ struct wpabuf *buf)
+{
+ struct ieee802_1x_mka_sak_use_body *body;
+ unsigned int length;
+ u32 pn = 1;
+
+ length = ieee802_1x_mka_get_sak_use_length(participant);
+ body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_sak_use_body));
+
+ body->type = MKA_SAK_USE;
+ set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+ if (length == MKA_HDR_LEN) {
+ body->ptx = TRUE;
+ body->prx = TRUE;
+ body->lan = 0;
+ body->lrx = FALSE;
+ body->ltx = FALSE;
+ body->delay_protect = FALSE;
+ return 0;
+ }
+
+ /* data protect, lowest accept packet number */
+ body->delay_protect = participant->kay->macsec_replay_protect;
+ pn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
+ if (pn > participant->kay->pn_exhaustion) {
+ wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion");
+ if (participant->is_key_server)
+ participant->new_sak = TRUE;
+ }
+
+ body->llpn = host_to_be32(pn);
+ pn = ieee802_1x_mka_get_lpn(participant, &participant->oki);
+ body->olpn = host_to_be32(pn);
+
+ /* plain tx, plain rx */
+ if (participant->kay->macsec_protect)
+ body->ptx = FALSE;
+ else
+ body->ptx = TRUE;
+
+ if (participant->kay->macsec_validate == Strict)
+ body->prx = FALSE;
+ else
+ body->prx = TRUE;
+
+ /* latest key: rx, tx, key server member identifier key number */
+ body->lan = participant->lan;
+ os_memcpy(body->lsrv_mi, participant->lki.mi,
+ sizeof(body->lsrv_mi));
+ body->lkn = host_to_be32(participant->lki.kn);
+ body->lrx = participant->lrx;
+ body->ltx = participant->ltx;
+
+ /* old key: rx, tx, key server member identifier key number */
+ body->oan = participant->oan;
+ if (participant->oki.kn != participant->lki.kn &&
+ participant->oki.kn != 0) {
+ body->otx = TRUE;
+ body->orx = TRUE;
+ os_memcpy(body->osrv_mi, participant->oki.mi,
+ sizeof(body->osrv_mi));
+ body->okn = host_to_be32(participant->oki.kn);
+ } else {
+ body->otx = FALSE;
+ body->orx = FALSE;
+ }
+
+ /* set CP's variable */
+ if (body->ltx) {
+ if (!participant->kay->tx_enable)
+ participant->kay->tx_enable = TRUE;
+
+ if (!participant->kay->port_enable)
+ participant->kay->port_enable = TRUE;
+ }
+ if (body->lrx) {
+ if (!participant->kay->rx_enable)
+ participant->kay->rx_enable = TRUE;
+ }
+
+ ieee802_1x_mka_dump_sak_use_body(body);
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_sak_use_body -
+ */
+static int
+ieee802_1x_mka_decode_sak_use_body(
+ struct ieee802_1x_mka_participant *participant,
+ const u8 *mka_msg, size_t msg_len)
+{
+ struct ieee802_1x_mka_hdr *hdr;
+ struct ieee802_1x_mka_sak_use_body *body;
+ struct ieee802_1x_kay_peer *peer;
+ struct transmit_sa *txsa;
+ struct data_key *sa_key = NULL;
+ size_t body_len;
+ struct ieee802_1x_mka_ki ki;
+ u32 lpn;
+ Boolean all_receiving;
+ Boolean founded;
+
+ if (!participant->principal) {
+ wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
+ return -1;
+ }
+ peer = ieee802_1x_kay_get_live_peer(participant,
+ participant->current_peer_id.mi);
+ if (!peer) {
+ wpa_printf(MSG_WARNING, "KaY: the peer is not my live peer");
+ return -1;
+ }
+
+ hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+ body_len = get_mka_param_body_len(hdr);
+ body = (struct ieee802_1x_mka_sak_use_body *) mka_msg;
+ ieee802_1x_mka_dump_sak_use_body(body);
+
+ if ((body_len != 0) && (body_len < 40)) {
+ wpa_printf(MSG_ERROR,
+ "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 40, or more octets",
+ (int) body_len);
+ return -1;
+ }
+
+ /* TODO: what action should I take when peer does not support MACsec */
+ if (body_len == 0) {
+ wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec");
+ return 0;
+ }
+
+ /* TODO: when the plain tx or rx of peer is true, should I change
+ * the attribute of controlled port
+ */
+ if (body->prx)
+ wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE");
+
+ if (body->ptx)
+ wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE");
+
+ /* check latest key is valid */
+ if (body->ltx || body->lrx) {
+ founded = FALSE;
+ os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
+ ki.kn = ntohl(body->lkn);
+ dl_list_for_each(sa_key, &participant->sak_list,
+ struct data_key, list) {
+ if (is_ki_equal(&sa_key->key_identifier, &ki)) {
+ founded = TRUE;
+ break;
+ }
+ }
+ if (!founded) {
+ wpa_printf(MSG_WARNING, "KaY: Latest key is invalid");
+ return -1;
+ }
+ if (os_memcmp(participant->lki.mi, body->lsrv_mi,
+ sizeof(participant->lki.mi)) == 0 &&
+ ntohl(body->lkn) == participant->lki.kn &&
+ body->lan == participant->lan) {
+ peer->sak_used = TRUE;
+ }
+ if (body->ltx && peer->is_key_server) {
+ ieee802_1x_cp_set_servertransmitting(
+ participant->kay->cp, TRUE);
+ ieee802_1x_cp_sm_step(participant->kay->cp);
+ }
+ }
+
+ /* check old key is valid */
+ if (body->otx || body->orx) {
+ if (os_memcmp(participant->oki.mi, body->osrv_mi,
+ sizeof(participant->oki.mi)) != 0 ||
+ ntohl(body->okn) != participant->oki.kn ||
+ body->oan != participant->oan) {
+ wpa_printf(MSG_WARNING, "KaY: Old key is invalid");
+ return -1;
+ }
+ }
+
+ /* TODO: how to set the MACsec hardware when delay_protect is true */
+ if (body->delay_protect && (!ntohl(body->llpn) || !ntohl(body->olpn))) {
+ wpa_printf(MSG_WARNING,
+ "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE");
+ return -1;
+ }
+
+ /* check all live peer have used the sak for receiving sa */
+ all_receiving = TRUE;
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list) {
+ if (!peer->sak_used) {
+ all_receiving = FALSE;
+ break;
+ }
+ }
+ if (all_receiving) {
+ participant->to_dist_sak = FALSE;
+ ieee802_1x_cp_set_allreceiving(participant->kay->cp, TRUE);
+ ieee802_1x_cp_sm_step(participant->kay->cp);
+ }
+
+ /* if i'm key server, and detects peer member pn exhaustion, rekey.*/
+ lpn = ntohl(body->llpn);
+ if (lpn > participant->kay->pn_exhaustion) {
+ if (participant->is_key_server) {
+ participant->new_sak = TRUE;
+ wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
+ }
+ }
+
+ founded = FALSE;
+ dl_list_for_each(txsa, &participant->txsc->sa_list,
+ struct transmit_sa, list) {
+ if (sa_key != NULL && txsa->pkey == sa_key) {
+ founded = TRUE;
+ break;
+ }
+ }
+ if (!founded) {
+ wpa_printf(MSG_WARNING, "KaY: Can't find txsa");
+ return -1;
+ }
+
+ /* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key
+ * npn is larger than txsa's npn, set it to txsa.
+ */
+ secy_get_transmit_next_pn(participant->kay, txsa);
+ if (lpn > txsa->next_pn) {
+ secy_set_transmit_next_pn(participant->kay, txsa);
+ wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn);
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_dist_sak_body_present
+ */
+static Boolean
+ieee802_1x_mka_dist_sak_body_present(
+ struct ieee802_1x_mka_participant *participant)
+{
+ if (!participant->to_dist_sak || !participant->new_key)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/**
+ * ieee802_1x_kay_get_dist_sak_length
+ */
+static int
+ieee802_1x_mka_get_dist_sak_length(
+ struct ieee802_1x_mka_participant *participant)
+{
+ int length;
+ int cs_index = participant->kay->macsec_csindex;
+
+ if (participant->advised_desired) {
+ length = sizeof(struct ieee802_1x_mka_dist_sak_body);
+ if (cs_index != DEFAULT_CS_INDEX)
+ length += CS_ID_LEN;
+
+ length += cipher_suite_tbl[cs_index].sak_len + 8;
+ } else {
+ length = MKA_HDR_LEN;
+ }
+ length = (length + 0x3) & ~0x3;
+
+ return length;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_dist_sak_body -
+ */
+static int
+ieee802_1x_mka_encode_dist_sak_body(
+ struct ieee802_1x_mka_participant *participant,
+ struct wpabuf *buf)
+{
+ struct ieee802_1x_mka_dist_sak_body *body;
+ struct data_key *sak;
+ unsigned int length;
+ int cs_index;
+ int sak_pos;
+
+ length = ieee802_1x_mka_get_dist_sak_length(participant);
+ body = wpabuf_put(buf, length);
+ body->type = MKA_DISTRIBUTED_SAK;
+ set_mka_param_body_len(body, length - MKA_HDR_LEN);
+ if (length == MKA_HDR_LEN) {
+ body->confid_offset = 0;
+ body->dan = 0;
+ return 0;
+ }
+
+ sak = participant->new_key;
+ body->confid_offset = sak->confidentiality_offset;
+ body->dan = sak->an;
+ body->kn = host_to_be32(sak->key_identifier.kn);
+ cs_index = participant->kay->macsec_csindex;
+ sak_pos = 0;
+ if (cs_index != DEFAULT_CS_INDEX) {
+ os_memcpy(body->sak, cipher_suite_tbl[cs_index].id, CS_ID_LEN);
+ sak_pos = CS_ID_LEN;
+ }
+ if (aes_wrap(participant->kek.key,
+ cipher_suite_tbl[cs_index].sak_len / 8,
+ sak->key, body->sak + sak_pos)) {
+ wpa_printf(MSG_ERROR, "KaY: AES wrap failed");
+ return -1;
+ }
+
+ ieee802_1x_mka_dump_dist_sak_body(body);
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_init_data_key -
+ */
+static struct data_key *
+ieee802_1x_kay_init_data_key(const struct key_conf *conf)
+{
+ struct data_key *pkey;
+
+ if (!conf)
+ return NULL;
+
+ pkey = os_zalloc(sizeof(*pkey));
+ if (pkey == NULL) {
+ wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+ return NULL;
+ }
+
+ pkey->key = os_zalloc(conf->key_len);
+ if (pkey->key == NULL) {
+ wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+ os_free(pkey);
+ return NULL;
+ }
+
+ os_memcpy(pkey->key, conf->key, conf->key_len);
+ os_memcpy(&pkey->key_identifier, &conf->ki,
+ sizeof(pkey->key_identifier));
+ pkey->confidentiality_offset = conf->offset;
+ pkey->an = conf->an;
+ pkey->transmits = conf->tx;
+ pkey->receives = conf->rx;
+ os_get_time(&pkey->created_time);
+
+ pkey->user = 1;
+
+ return pkey;
+}
+
+
+/**
+ * ieee802_1x_kay_decode_dist_sak_body -
+ */
+static int
+ieee802_1x_mka_decode_dist_sak_body(
+ struct ieee802_1x_mka_participant *participant,
+ const u8 *mka_msg, size_t msg_len)
+{
+ struct ieee802_1x_mka_hdr *hdr;
+ struct ieee802_1x_mka_dist_sak_body *body;
+ struct ieee802_1x_kay_peer *peer;
+ struct macsec_ciphersuite *cs;
+ size_t body_len;
+ struct key_conf *conf;
+ struct data_key *sa_key = NULL;
+ struct ieee802_1x_mka_ki sak_ki;
+ int sak_len;
+ u8 *wrap_sak;
+ u8 *unwrap_sak;
+
+ hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+ body_len = get_mka_param_body_len(hdr);
+ if ((body_len != 0) && (body_len != 28) && (body_len < 36)) {
+ wpa_printf(MSG_ERROR,
+ "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 28, 36, or more octets",
+ (int) body_len);
+ return -1;
+ }
+
+ if (!participant->principal) {
+ wpa_printf(MSG_ERROR,
+ "KaY: I can't accept the distributed SAK as I am not principal");
+ return -1;
+ }
+ if (participant->is_key_server) {
+ wpa_printf(MSG_ERROR,
+ "KaY: I can't accept the distributed SAK as myself is key server ");
+ return -1;
+ }
+ if (!participant->kay->macsec_desired ||
+ participant->kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
+ wpa_printf(MSG_ERROR,
+ "KaY: I am not MACsec-desired or without MACsec capable");
+ return -1;
+ }
+
+ peer = ieee802_1x_kay_get_live_peer(participant,
+ participant->current_peer_id.mi);
+ if (!peer) {
+ wpa_printf(MSG_ERROR,
+ "KaY: The key server is not in my live peers list");
+ return -1;
+ }
+ if (os_memcmp(&participant->kay->key_server_sci,
+ &peer->sci, sizeof(struct ieee802_1x_mka_sci)) != 0) {
+ wpa_printf(MSG_ERROR, "KaY: The key server is not elected");
+ return -1;
+ }
+ if (body_len == 0) {
+ participant->kay->authenticated = TRUE;
+ participant->kay->secured = FALSE;
+ participant->kay->failed = FALSE;
+ participant->advised_desired = FALSE;
+ ieee802_1x_cp_connect_authenticated(participant->kay->cp);
+ ieee802_1x_cp_sm_step(participant->kay->cp);
+ wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec");
+ participant->to_use_sak = TRUE;
+ return 0;
+ }
+ participant->advised_desired = TRUE;
+ participant->kay->authenticated = FALSE;
+ participant->kay->secured = TRUE;
+ participant->kay->failed = FALSE;
+ ieee802_1x_cp_connect_secure(participant->kay->cp);
+ ieee802_1x_cp_sm_step(participant->kay->cp);
+
+ body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg;
+ ieee802_1x_mka_dump_dist_sak_body(body);
+ dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list)
+ {
+ if (os_memcmp(sa_key->key_identifier.mi,
+ participant->current_peer_id.mi, MI_LEN) == 0 &&
+ sa_key->key_identifier.kn == be_to_host32(body->kn)) {
+ wpa_printf(MSG_WARNING, "KaY:The Key has installed");
+ return 0;
+ }
+ }
+ if (body_len == 28) {
+ sak_len = DEFAULT_SA_KEY_LEN;
+ wrap_sak = body->sak;
+ participant->kay->macsec_csindex = DEFAULT_CS_INDEX;
+ } else {
+ cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak);
+ if (!cs) {
+ wpa_printf(MSG_ERROR,
+ "KaY: I can't support the Cipher Suite advised by key server");
+ return -1;
+ }
+ sak_len = cs->sak_len;
+ wrap_sak = body->sak + CS_ID_LEN;
+ participant->kay->macsec_csindex = cs->index;
+ }
+
+ unwrap_sak = os_zalloc(sak_len);
+ if (!unwrap_sak) {
+ wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+ return -1;
+ }
+ if (aes_unwrap(participant->kek.key, sak_len >> 3, wrap_sak,
+ unwrap_sak)) {
+ wpa_printf(MSG_ERROR, "KaY: AES unwrap failed");
+ os_free(unwrap_sak);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len);
+
+ conf = os_zalloc(sizeof(*conf));
+ if (!conf) {
+ wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+ os_free(unwrap_sak);
+ return -1;
+ }
+ conf->key_len = sak_len;
+
+ conf->key = os_zalloc(conf->key_len);
+ if (!conf->key) {
+ wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+ os_free(unwrap_sak);
+ os_free(conf);
+ return -1;
+ }
+
+ os_memcpy(conf->key, unwrap_sak, conf->key_len);
+
+ os_memcpy(&sak_ki.mi, &participant->current_peer_id.mi,
+ sizeof(sak_ki.mi));
+ sak_ki.kn = be_to_host32(body->kn);
+
+ os_memcpy(conf->ki.mi, sak_ki.mi, MI_LEN);
+ conf->ki.kn = sak_ki.kn;
+ conf->an = body->dan;
+ conf->offset = body->confid_offset;
+ conf->rx = TRUE;
+ conf->tx = TRUE;
+
+ sa_key = ieee802_1x_kay_init_data_key(conf);
+ if (!sa_key) {
+ os_free(unwrap_sak);
+ os_free(conf->key);
+ os_free(conf);
+ return -1;
+ }
+
+ dl_list_add(&participant->sak_list, &sa_key->list);
+
+ ieee802_1x_cp_set_ciphersuite(
+ participant->kay->cp,
+ cipher_suite_tbl[participant->kay->macsec_csindex].id);
+ ieee802_1x_cp_sm_step(participant->kay->cp);
+ ieee802_1x_cp_set_offset(participant->kay->cp, body->confid_offset);
+ ieee802_1x_cp_sm_step(participant->kay->cp);
+ ieee802_1x_cp_set_distributedki(participant->kay->cp, &sak_ki);
+ ieee802_1x_cp_set_distributedan(participant->kay->cp, body->dan);
+ ieee802_1x_cp_signal_newsak(participant->kay->cp);
+ ieee802_1x_cp_sm_step(participant->kay->cp);
+
+ participant->to_use_sak = TRUE;
+
+ os_free(unwrap_sak);
+ os_free(conf->key);
+ os_free(conf);
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_icv_body_present
+ */
+static Boolean
+ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant)
+{
+ return TRUE;
+}
+
+
+/**
+ * ieee802_1x_kay_get_icv_length
+ */
+static int
+ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant)
+{
+ int length;
+
+ length = sizeof(struct ieee802_1x_mka_icv_body);
+ length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
+
+ return (length + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_icv_body -
+ */
+static int
+ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
+ struct wpabuf *buf)
+{
+ struct ieee802_1x_mka_icv_body *body;
+ unsigned int length;
+ u8 cmac[MAX_ICV_LEN];
+
+ length = ieee802_1x_mka_get_icv_length(participant);
+ if (length != DEFAULT_ICV_LEN) {
+ body = wpabuf_put(buf, MKA_HDR_LEN);
+ body->type = MKA_ICV_INDICATOR;
+ set_mka_param_body_len(body, length - MKA_HDR_LEN);
+ }
+
+ if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash(
+ participant->ick.key, wpabuf_head(buf), buf->used, cmac)) {
+ wpa_printf(MSG_ERROR, "KaY, omac1_aes_128 failed");
+ return -1;
+ }
+
+ if (length != DEFAULT_ICV_LEN) {
+ os_memcpy(wpabuf_put(buf, length - MKA_HDR_LEN), cmac,
+ length - MKA_HDR_LEN);
+ } else {
+ os_memcpy(wpabuf_put(buf, length), cmac, length);
+ }
+
+ return 0;
+}
+
+/**
+ * ieee802_1x_mka_decode_icv_body -
+ */
+static u8 *
+ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
+ const u8 *mka_msg, size_t msg_len)
+{
+ struct ieee802_1x_mka_hdr *hdr;
+ struct ieee802_1x_mka_icv_body *body;
+ size_t body_len;
+ size_t left_len;
+ int body_type;
+ const u8 *pos;
+
+ pos = mka_msg;
+ left_len = msg_len;
+ while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) {
+ hdr = (struct ieee802_1x_mka_hdr *) pos;
+ body_len = get_mka_param_body_len(hdr);
+ body_type = get_mka_param_body_type(hdr);
+
+ if (left_len < (body_len + MKA_HDR_LEN))
+ break;
+
+ if (body_type != MKA_ICV_INDICATOR) {
+ left_len -= MKA_HDR_LEN + body_len;
+ pos += MKA_HDR_LEN + body_len;
+ continue;
+ }
+
+ body = (struct ieee802_1x_mka_icv_body *)pos;
+ if (body_len
+ < mka_alg_tbl[participant->kay->mka_algindex].icv_len) {
+ return NULL;
+ }
+
+ return body->icv;
+ }
+
+ return (u8 *) (mka_msg + msg_len - DEFAULT_ICV_LEN);
+}
+
+
+/**
+ * ieee802_1x_mka_decode_dist_cak_body-
+ */
+static int
+ieee802_1x_mka_decode_dist_cak_body(
+ struct ieee802_1x_mka_participant *participant,
+ const u8 *mka_msg, size_t msg_len)
+{
+ struct ieee802_1x_mka_hdr *hdr;
+ size_t body_len;
+
+ hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+ body_len = get_mka_param_body_len(hdr);
+ if (body_len < 28) {
+ wpa_printf(MSG_ERROR,
+ "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 28 or more octets",
+ (int) body_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_kmd_body -
+ */
+static int
+ieee802_1x_mka_decode_kmd_body(
+ struct ieee802_1x_mka_participant *participant,
+ const u8 *mka_msg, size_t msg_len)
+{
+ struct ieee802_1x_mka_hdr *hdr;
+ size_t body_len;
+
+ hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+ body_len = get_mka_param_body_len(hdr);
+ if (body_len < 5) {
+ wpa_printf(MSG_ERROR,
+ "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 5 or more octets",
+ (int) body_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_announce_body -
+ */
+static int ieee802_1x_mka_decode_announce_body(
+ struct ieee802_1x_mka_participant *participant,
+ const u8 *mka_msg, size_t msg_len)
+{
+ return 0;
+}
+
+
+static struct mka_param_body_handler mak_body_handler[] = {
+ /* basic parameter set */
+ {
+ ieee802_1x_mka_encode_basic_body,
+ NULL,
+ ieee802_1x_mka_basic_body_length,
+ ieee802_1x_mka_basic_body_present
+ },
+
+ /* live peer list parameter set */
+ {
+ ieee802_1x_mka_encode_live_peer_body,
+ ieee802_1x_mka_decode_live_peer_body,
+ ieee802_1x_mka_get_live_peer_length,
+ ieee802_1x_mka_live_peer_body_present
+ },
+
+ /* potential peer list parameter set */
+ {
+ ieee802_1x_mka_encode_potential_peer_body,
+ ieee802_1x_mka_decode_potential_peer_body,
+ ieee802_1x_mka_get_potential_peer_length,
+ ieee802_1x_mka_potential_peer_body_present
+ },
+
+ /* sak use parameter set */
+ {
+ ieee802_1x_mka_encode_sak_use_body,
+ ieee802_1x_mka_decode_sak_use_body,
+ ieee802_1x_mka_get_sak_use_length,
+ ieee802_1x_mka_sak_use_body_present
+ },
+
+ /* distribute sak parameter set */
+ {
+ ieee802_1x_mka_encode_dist_sak_body,
+ ieee802_1x_mka_decode_dist_sak_body,
+ ieee802_1x_mka_get_dist_sak_length,
+ ieee802_1x_mka_dist_sak_body_present
+ },
+
+ /* distribute cak parameter set */
+ {
+ NULL,
+ ieee802_1x_mka_decode_dist_cak_body,
+ NULL,
+ NULL
+ },
+
+ /* kmd parameter set */
+ {
+ NULL,
+ ieee802_1x_mka_decode_kmd_body,
+ NULL,
+ NULL
+ },
+
+ /* announce parameter set */
+ {
+ NULL,
+ ieee802_1x_mka_decode_announce_body,
+ NULL,
+ NULL
+ },
+
+ /* icv parameter set */
+ {
+ ieee802_1x_mka_encode_icv_body,
+ NULL,
+ ieee802_1x_mka_get_icv_length,
+ ieee802_1x_mka_icv_body_present
+ },
+};
+
+
+/**
+ * ieee802_1x_kay_deinit_data_key -
+ */
+void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
+{
+ if (!pkey)
+ return;
+
+ pkey->user--;
+ if (pkey->user > 1)
+ return;
+
+ dl_list_del(&pkey->list);
+ os_free(pkey->key);
+ os_free(pkey);
+}
+
+
+/**
+ * ieee802_1x_kay_generate_new_sak -
+ */
+static int
+ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
+{
+ struct data_key *sa_key = NULL;
+ struct key_conf *conf;
+ struct ieee802_1x_kay_peer *peer;
+ struct ieee802_1x_kay *kay = participant->kay;
+ int ctx_len, ctx_offset;
+ u8 *context;
+
+ /* check condition for generating a fresh SAK:
+ * must have one live peer
+ * and MKA life time elapse since last distribution
+ * or potential peer is empty
+ */
+ if (dl_list_empty(&participant->live_peers)) {
+ wpa_printf(MSG_ERROR,
+ "KaY: Live peers list must not empty when generating fresh SAK");
+ return -1;
+ }
+
+ /* FIXME: A fresh SAK not generated until
+ * the live peer list contains at least one peer and
+ * MKA life time has elapsed since the prior SAK was first distributed,
+ * or the Key server's potential peer is empty
+ * but I can't understand the second item, so
+ * here only check first item and ingore
+ * && (!dl_list_empty(&participant->potential_peers))) {
+ */
+ if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) {
+ wpa_printf(MSG_ERROR,
+ "KaY: Life time have not elapsed since prior SAK distributed");
+ return -1;
+ }
+
+ conf = os_zalloc(sizeof(*conf));
+ if (!conf) {
+ wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+ return -1;
+ }
+ conf->key_len = cipher_suite_tbl[kay->macsec_csindex].sak_len;
+
+ conf->key = os_zalloc(conf->key_len);
+ if (!conf->key) {
+ os_free(conf);
+ wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+ return -1;
+ }
+
+ ctx_len = conf->key_len + sizeof(kay->dist_kn);
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list)
+ ctx_len += sizeof(peer->mi);
+ ctx_len += sizeof(participant->mi);
+
+ context = os_zalloc(ctx_len);
+ if (!context) {
+ os_free(conf->key);
+ os_free(conf);
+ return -1;
+ }
+ ctx_offset = 0;
+ os_get_random(context + ctx_offset, conf->key_len);
+ ctx_offset += conf->key_len;
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list) {
+ os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi));
+ ctx_offset += sizeof(peer->mi);
+ }
+ os_memcpy(context + ctx_offset, participant->mi,
+ sizeof(participant->mi));
+ ctx_offset += sizeof(participant->mi);
+ os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
+
+ if (conf->key_len == 16) {
+ ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
+ context, ctx_len, conf->key);
+ } else if (conf->key_len == 32) {
+ ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
+ context, ctx_len, conf->key);
+ } else {
+ wpa_printf(MSG_ERROR, "KaY: SAK Length not support");
+ os_free(conf->key);
+ os_free(conf);
+ os_free(context);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK",
+ conf->key, conf->key_len);
+
+ os_memcpy(conf->ki.mi, participant->mi, MI_LEN);
+ conf->ki.kn = participant->kay->dist_kn;
+ conf->an = participant->kay->dist_an;
+ conf->offset = kay->macsec_confidentiality;
+ conf->rx = TRUE;
+ conf->tx = TRUE;
+
+ sa_key = ieee802_1x_kay_init_data_key(conf);
+ if (!sa_key) {
+ os_free(conf->key);
+ os_free(conf);
+ os_free(context);
+ return -1;
+ }
+ participant->new_key = sa_key;
+
+ dl_list_add(&participant->sak_list, &sa_key->list);
+ ieee802_1x_cp_set_ciphersuite(participant->kay->cp,
+ cipher_suite_tbl[kay->macsec_csindex].id);
+ ieee802_1x_cp_sm_step(kay->cp);
+ ieee802_1x_cp_set_offset(kay->cp, conf->offset);
+ ieee802_1x_cp_sm_step(kay->cp);
+ ieee802_1x_cp_set_distributedki(kay->cp, &conf->ki);
+ ieee802_1x_cp_set_distributedan(kay->cp, conf->an);
+ ieee802_1x_cp_signal_newsak(kay->cp);
+ ieee802_1x_cp_sm_step(kay->cp);
+
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list)
+ peer->sak_used = FALSE;
+
+ participant->kay->dist_kn++;
+ participant->kay->dist_an++;
+ if (participant->kay->dist_an > 3)
+ participant->kay->dist_an = 0;
+
+ participant->kay->dist_time = time(NULL);
+
+ os_free(conf->key);
+ os_free(conf);
+ os_free(context);
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_elect_key_server - elect the key server
+ * when to elect: whenever the live peers list changes
+ */
+static int
+ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
+{
+ struct ieee802_1x_kay_peer *peer;
+ struct ieee802_1x_kay_peer *key_server = NULL;
+ struct ieee802_1x_kay *kay = participant->kay;
+ Boolean i_is_key_server;
+ int i;
+
+ if (participant->is_obliged_key_server) {
+ participant->new_sak = TRUE;
+ participant->to_dist_sak = FALSE;
+ ieee802_1x_cp_set_electedself(kay->cp, TRUE);
+ return 0;
+ }
+
+ /* elect the key server among the peers */
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list) {
+ if (!peer->is_key_server)
+ continue;
+
+ if (!key_server) {
+ key_server = peer;
+ continue;
+ }
+
+ if (peer->key_server_priority <
+ key_server->key_server_priority) {
+ key_server = peer;
+ } else if (peer->key_server_priority ==
+ key_server->key_server_priority) {
+ for (i = 0; i < 6; i++) {
+ if (peer->sci.addr[i] <
+ key_server->sci.addr[i])
+ key_server = peer;
+ }
+ }
+ }
+
+ /* elect the key server between me and the above elected peer */
+ i_is_key_server = FALSE;
+ if (key_server && participant->can_be_key_server) {
+ if (kay->actor_priority
+ < key_server->key_server_priority) {
+ i_is_key_server = TRUE;
+ } else if (kay->actor_priority
+ == key_server->key_server_priority) {
+ for (i = 0; i < 6; i++) {
+ if (kay->actor_sci.addr[i]
+ < key_server->sci.addr[i]) {
+ i_is_key_server = TRUE;
+ }
+ }
+ }
+ }
+
+ if (!key_server && !i_is_key_server) {
+ participant->principal = FALSE;
+ participant->is_key_server = FALSE;
+ participant->is_elected = FALSE;
+ return 0;
+ }
+
+ if (i_is_key_server) {
+ ieee802_1x_cp_set_electedself(kay->cp, TRUE);
+ if (os_memcmp(&kay->key_server_sci, &kay->actor_sci,
+ sizeof(kay->key_server_sci))) {
+ ieee802_1x_cp_signal_chgdserver(kay->cp);
+ ieee802_1x_cp_sm_step(kay->cp);
+ }
+
+ participant->is_key_server = TRUE;
+ participant->principal = TRUE;
+ participant->new_sak = TRUE;
+ wpa_printf(MSG_DEBUG, "KaY: I is elected as key server");
+ participant->to_dist_sak = FALSE;
+ participant->is_elected = TRUE;
+
+ os_memcpy(&kay->key_server_sci, &kay->actor_sci,
+ sizeof(kay->key_server_sci));
+ kay->key_server_priority = kay->actor_priority;
+ }
+
+ if (key_server) {
+ ieee802_1x_cp_set_electedself(kay->cp, FALSE);
+ if (os_memcmp(&kay->key_server_sci, &key_server->sci,
+ sizeof(kay->key_server_sci))) {
+ ieee802_1x_cp_signal_chgdserver(kay->cp);
+ ieee802_1x_cp_sm_step(kay->cp);
+ }
+
+ participant->is_key_server = FALSE;
+ participant->principal = TRUE;
+ participant->is_elected = TRUE;
+
+ os_memcpy(&kay->key_server_sci, &key_server->sci,
+ sizeof(kay->key_server_sci));
+ kay->key_server_priority = key_server->key_server_priority;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_decide_macsec_use - the key server determinate
+ * how to use MACsec: whether use MACsec and its capability
+ * protectFrames will be advised if the key server and one of its live peers are
+ * MACsec capable and one of those request MACsec protection
+ */
+static int
+ieee802_1x_kay_decide_macsec_use(
+ struct ieee802_1x_mka_participant *participant)
+{
+ struct ieee802_1x_kay *kay = participant->kay;
+ struct ieee802_1x_kay_peer *peer;
+ enum macsec_cap less_capability;
+ Boolean has_peer;
+
+ if (!participant->is_key_server)
+ return -1;
+
+ /* key server self is MACsec-desired and requesting MACsec */
+ if (!kay->macsec_desired) {
+ participant->advised_desired = FALSE;
+ return -1;
+ }
+ if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
+ participant->advised_desired = FALSE;
+ return -1;
+ }
+ less_capability = kay->macsec_capable;
+
+ /* at least one of peers is MACsec-desired and requesting MACsec */
+ has_peer = FALSE;
+ dl_list_for_each(peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list) {
+ if (!peer->macsec_desired)
+ continue;
+
+ if (peer->macsec_capbility == MACSEC_CAP_NOT_IMPLEMENTED)
+ continue;
+
+ less_capability = (less_capability < peer->macsec_capbility) ?
+ less_capability : peer->macsec_capbility;
+ has_peer = TRUE;
+ }
+
+ if (has_peer) {
+ participant->advised_desired = TRUE;
+ participant->advised_capability = less_capability;
+ kay->authenticated = FALSE;
+ kay->secured = TRUE;
+ kay->failed = FALSE;
+ ieee802_1x_cp_connect_secure(kay->cp);
+ ieee802_1x_cp_sm_step(kay->cp);
+ } else {
+ participant->advised_desired = FALSE;
+ participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED;
+ participant->to_use_sak = FALSE;
+ kay->authenticated = TRUE;
+ kay->secured = FALSE;
+ kay->failed = FALSE;
+ kay->ltx_kn = 0;
+ kay->ltx_an = 0;
+ kay->lrx_kn = 0;
+ kay->lrx_an = 0;
+ kay->otx_kn = 0;
+ kay->otx_an = 0;
+ kay->orx_kn = 0;
+ kay->orx_an = 0;
+ ieee802_1x_cp_connect_authenticated(kay->cp);
+ ieee802_1x_cp_sm_step(kay->cp);
+ }
+
+ return 0;
+}
+
+static const u8 pae_group_addr[ETH_ALEN] = {
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
+};
+
+
+/**
+ * ieee802_1x_kay_encode_mkpdu -
+ */
+static int
+ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
+ struct wpabuf *pbuf)
+{
+ unsigned int i;
+ struct ieee8023_hdr *ether_hdr;
+ struct ieee802_1x_hdr *eapol_hdr;
+
+ ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr));
+ os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest));
+ os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
+ sizeof(ether_hdr->dest));
+ ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
+
+ eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr));
+ eapol_hdr->version = EAPOL_VERSION;
+ eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
+ eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used);
+
+ for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) {
+ if (mak_body_handler[i].body_present &&
+ mak_body_handler[i].body_present(participant)) {
+ if (mak_body_handler[i].body_tx(participant, pbuf))
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ieee802_1x_participant_send_mkpdu -
+ */
+static int
+ieee802_1x_participant_send_mkpdu(
+ struct ieee802_1x_mka_participant *participant)
+{
+ struct wpabuf *buf;
+ struct ieee802_1x_kay *kay = participant->kay;
+ size_t length = 0;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU");
+ length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
+ for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) {
+ if (mak_body_handler[i].body_present &&
+ mak_body_handler[i].body_present(participant))
+ length += mak_body_handler[i].body_length(participant);
+ }
+
+ buf = wpabuf_alloc(length);
+ if (!buf) {
+ wpa_printf(MSG_ERROR, "KaY: out of memory");
+ return -1;
+ }
+
+ if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
+ wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail!");
+ return -1;
+ }
+
+ l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+
+ kay->active = TRUE;
+ participant->active = TRUE;
+
+ return 0;
+}
+
+
+static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
+/**
+ * ieee802_1x_participant_timer -
+ */
+static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct ieee802_1x_mka_participant *participant;
+ struct ieee802_1x_kay *kay;
+ struct ieee802_1x_kay_peer *peer, *pre_peer;
+ time_t now = time(NULL);
+ Boolean lp_changed;
+ struct receive_sc *rxsc, *pre_rxsc;
+ struct transmit_sa *txsa, *pre_txsa;
+
+ participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
+ kay = participant->kay;
+ if (participant->cak_life) {
+ if (now > participant->cak_life) {
+ kay->authenticated = FALSE;
+ kay->secured = FALSE;
+ kay->failed = TRUE;
+ ieee802_1x_kay_delete_mka(kay, &participant->ckn);
+ return;
+ }
+ }
+
+ /* should delete MKA instance if there are not live peers
+ * when the MKA life elapsed since its creating */
+ if (participant->mka_life) {
+ if (dl_list_empty(&participant->live_peers)) {
+ if (now > participant->mka_life) {
+ kay->authenticated = FALSE;
+ kay->secured = FALSE;
+ kay->failed = TRUE;
+ ieee802_1x_kay_delete_mka(kay,
+ &participant->ckn);
+ return;
+ }
+ } else {
+ participant->mka_life = 0;
+ }
+ }
+
+ lp_changed = FALSE;
+ dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
+ struct ieee802_1x_kay_peer, list) {
+ if (now > peer->expire) {
+ wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
+ wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
+ sizeof(peer->mi));
+ wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+ dl_list_for_each_safe(rxsc, pre_rxsc,
+ &participant->rxsc_list,
+ struct receive_sc, list) {
+ if (os_memcmp(&rxsc->sci, &peer->sci,
+ sizeof(rxsc->sci)) == 0) {
+ secy_delete_receive_sc(kay, rxsc);
+ ieee802_1x_kay_deinit_receive_sc(
+ participant, rxsc);
+ }
+ }
+ dl_list_del(&peer->list);
+ os_free(peer);
+ lp_changed = TRUE;
+ }
+ }
+
+ if (lp_changed) {
+ if (dl_list_empty(&participant->live_peers)) {
+ participant->advised_desired = FALSE;
+ participant->advised_capability =
+ MACSEC_CAP_NOT_IMPLEMENTED;
+ participant->to_use_sak = FALSE;
+ kay->authenticated = TRUE;
+ kay->secured = FALSE;
+ kay->failed = FALSE;
+ kay->ltx_kn = 0;
+ kay->ltx_an = 0;
+ kay->lrx_kn = 0;
+ kay->lrx_an = 0;
+ kay->otx_kn = 0;
+ kay->otx_an = 0;
+ kay->orx_kn = 0;
+ kay->orx_an = 0;
+ dl_list_for_each_safe(txsa, pre_txsa,
+ &participant->txsc->sa_list,
+ struct transmit_sa, list) {
+ secy_disable_transmit_sa(kay, txsa);
+ ieee802_1x_kay_deinit_transmit_sa(txsa);
+ }
+
+ ieee802_1x_cp_connect_authenticated(kay->cp);
+ ieee802_1x_cp_sm_step(kay->cp);
+ } else {
+ ieee802_1x_kay_elect_key_server(participant);
+ ieee802_1x_kay_decide_macsec_use(participant);
+ }
+ }
+
+ dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers,
+ struct ieee802_1x_kay_peer, list) {
+ if (now > peer->expire) {
+ wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
+ wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
+ sizeof(peer->mi));
+ wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+ dl_list_del(&peer->list);
+ os_free(peer);
+ }
+ }
+
+ if (participant->new_sak) {
+ if (!ieee802_1x_kay_generate_new_sak(participant))
+ participant->to_dist_sak = TRUE;
+
+ participant->new_sak = FALSE;
+ }
+
+ if (participant->retry_count < MAX_RETRY_CNT) {
+ ieee802_1x_participant_send_mkpdu(participant);
+ participant->retry_count++;
+ }
+
+ eloop_register_timeout(MKA_HELLO_TIME / 1000, 0,
+ ieee802_1x_participant_timer,
+ participant, NULL);
+}
+
+
+/**
+ * ieee802_1x_kay_init_transmit_sa -
+ */
+static struct transmit_sa *
+ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
+ struct data_key *key)
+{
+ struct transmit_sa *psa;
+
+ key->tx_latest = TRUE;
+ key->rx_latest = TRUE;
+
+ psa = os_zalloc(sizeof(*psa));
+ if (!psa) {
+ wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+ return NULL;
+ }
+
+ if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 &&
+ key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50)
+ psa->confidentiality = TRUE;
+ else
+ psa->confidentiality = FALSE;
+
+ psa->an = an;
+ psa->pkey = key;
+ psa->next_pn = next_PN;
+ psa->sc = psc;
+
+ os_get_time(&psa->created_time);
+ psa->in_use = FALSE;
+
+ dl_list_add(&psc->sa_list, &psa->list);
+ wpa_printf(MSG_DEBUG,
+ "KaY: Create transmit SA(an: %d, next_PN: %u) of SC(channel: %d)",
+ (int) an, next_PN, psc->channel);
+
+ return psa;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_transmit_sa -
+ */
+static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
+{
+ psa->pkey = NULL;
+ wpa_printf(MSG_DEBUG,
+ "KaY: Delete transmit SA(an: %d) of SC(channel: %d)",
+ psa->an, psa->sc->channel);
+ dl_list_del(&psa->list);
+ os_free(psa);
+}
+
+
+/**
+ * init_transmit_sc -
+ */
+static struct transmit_sc *
+ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci,
+ int channel)
+{
+ struct transmit_sc *psc;
+
+ psc = os_zalloc(sizeof(*psc));
+ if (!psc) {
+ wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+ return NULL;
+ }
+ os_memcpy(&psc->sci, sci, sizeof(psc->sci));
+ psc->channel = channel;
+
+ os_get_time(&psc->created_time);
+ psc->transmitting = FALSE;
+ psc->encoding_sa = FALSE;
+ psc->enciphering_sa = FALSE;
+
+ dl_list_init(&psc->sa_list);
+ wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel);
+ wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci));
+
+ return psc;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_transmit_sc -
+ */
+static void
+ieee802_1x_kay_deinit_transmit_sc(
+ struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc)
+{
+ struct transmit_sa *psa, *tmp;
+
+ wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)",
+ psc->channel);
+ dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa,
+ list) {
+ secy_disable_transmit_sa(participant->kay, psa);
+ ieee802_1x_kay_deinit_transmit_sa(psa);
+ }
+
+ os_free(psc);
+}
+
+
+/****************** Interface between CP and KAY *********************/
+/**
+ * ieee802_1x_kay_set_latest_sa_attr -
+ */
+int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *lki, u8 lan,
+ Boolean ltx, Boolean lrx)
+{
+ struct ieee802_1x_mka_participant *principal;
+
+ principal = ieee802_1x_kay_get_principal_participant(kay);
+ if (!principal)
+ return -1;
+
+ if (!lki)
+ os_memset(&principal->lki, 0, sizeof(principal->lki));
+ else
+ os_memcpy(&principal->lki, lki, sizeof(principal->lki));
+
+ principal->lan = lan;
+ principal->ltx = ltx;
+ principal->lrx = lrx;
+ if (!lki) {
+ kay->ltx_kn = 0;
+ kay->lrx_kn = 0;
+ } else {
+ kay->ltx_kn = lki->kn;
+ kay->lrx_kn = lki->kn;
+ }
+ kay->ltx_an = lan;
+ kay->lrx_an = lan;
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_set_old_sa_attr -
+ */
+int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *oki,
+ u8 oan, Boolean otx, Boolean orx)
+{
+ struct ieee802_1x_mka_participant *principal;
+
+ principal = ieee802_1x_kay_get_principal_participant(kay);
+ if (!principal)
+ return -1;
+
+ if (!oki)
+ os_memset(&principal->oki, 0, sizeof(principal->oki));
+ else
+ os_memcpy(&principal->oki, oki, sizeof(principal->oki));
+
+ principal->oan = oan;
+ principal->otx = otx;
+ principal->orx = orx;
+
+ if (!oki) {
+ kay->otx_kn = 0;
+ kay->orx_kn = 0;
+ } else {
+ kay->otx_kn = oki->kn;
+ kay->orx_kn = oki->kn;
+ }
+ kay->otx_an = oan;
+ kay->orx_an = oan;
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_create_sas -
+ */
+int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *lki)
+{
+ struct data_key *sa_key, *latest_sak;
+ struct ieee802_1x_mka_participant *principal;
+ struct receive_sc *rxsc;
+ struct receive_sa *rxsa;
+ struct transmit_sa *txsa;
+
+ principal = ieee802_1x_kay_get_principal_participant(kay);
+ if (!principal)
+ return -1;
+
+ latest_sak = NULL;
+ dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) {
+ if (is_ki_equal(&sa_key->key_identifier, lki)) {
+ sa_key->rx_latest = TRUE;
+ sa_key->tx_latest = TRUE;
+ latest_sak = sa_key;
+ principal->to_use_sak = TRUE;
+ } else {
+ sa_key->rx_latest = FALSE;
+ sa_key->tx_latest = FALSE;
+ }
+ }
+ if (!latest_sak) {
+ wpa_printf(MSG_ERROR, "lki related sak not found");
+ return -1;
+ }
+
+ dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+ rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
+ latest_sak);
+ if (!rxsa)
+ return -1;
+
+ secy_create_receive_sa(kay, rxsa);
+ }
+
+ txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
+ 1, latest_sak);
+ if (!txsa)
+ return -1;
+
+ secy_create_transmit_sa(kay, txsa);
+
+
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_delete_sas -
+ */
+int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *ki)
+{
+ struct data_key *sa_key, *pre_key;
+ struct transmit_sa *txsa, *pre_txsa;
+ struct receive_sa *rxsa, *pre_rxsa;
+ struct receive_sc *rxsc;
+ struct ieee802_1x_mka_participant *principal;
+
+ wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__);
+ principal = ieee802_1x_kay_get_principal_participant(kay);
+ if (!principal)
+ return -1;
+
+ /* remove the transmit sa */
+ dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
+ struct transmit_sa, list) {
+ if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
+ secy_disable_transmit_sa(kay, txsa);
+ ieee802_1x_kay_deinit_transmit_sa(txsa);
+ }
+ }
+
+ /* remove the receive sa */
+ dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+ dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
+ struct receive_sa, list) {
+ if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
+ secy_disable_receive_sa(kay, rxsa);
+ ieee802_1x_kay_deinit_receive_sa(rxsa);
+ }
+ }
+ }
+
+ /* remove the sak */
+ dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
+ struct data_key, list) {
+ if (is_ki_equal(&sa_key->key_identifier, ki)) {
+ ieee802_1x_kay_deinit_data_key(sa_key);
+ break;
+ }
+ if (principal->new_key == sa_key)
+ principal->new_key = NULL;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_tx_sas -
+ */
+int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *lki)
+{
+ struct ieee802_1x_mka_participant *principal;
+ struct transmit_sa *txsa;
+
+ principal = ieee802_1x_kay_get_principal_participant(kay);
+ if (!principal)
+ return -1;
+
+ dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa,
+ list) {
+ if (is_ki_equal(&txsa->pkey->key_identifier, lki)) {
+ txsa->in_use = TRUE;
+ secy_enable_transmit_sa(kay, txsa);
+ ieee802_1x_cp_set_usingtransmitas(
+ principal->kay->cp, TRUE);
+ ieee802_1x_cp_sm_step(principal->kay->cp);
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_rx_sas -
+ */
+int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *lki)
+{
+ struct ieee802_1x_mka_participant *principal;
+ struct receive_sa *rxsa;
+ struct receive_sc *rxsc;
+
+ principal = ieee802_1x_kay_get_principal_participant(kay);
+ if (!principal)
+ return -1;
+
+ dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+ dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
+ {
+ if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) {
+ rxsa->in_use = TRUE;
+ secy_enable_receive_sa(kay, rxsa);
+ ieee802_1x_cp_set_usingreceivesas(
+ principal->kay->cp, TRUE);
+ ieee802_1x_cp_sm_step(principal->kay->cp);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_new_info -
+ */
+int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
+{
+ struct ieee802_1x_mka_participant *principal;
+
+ principal = ieee802_1x_kay_get_principal_participant(kay);
+ if (!principal)
+ return -1;
+
+ if (principal->retry_count < MAX_RETRY_CNT) {
+ ieee802_1x_participant_send_mkpdu(principal);
+ principal->retry_count++;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_cp_conf -
+ */
+int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_cp_conf *pconf)
+{
+ pconf->protect = kay->macsec_protect;
+ pconf->replay_protect = kay->macsec_replay_protect;
+ pconf->validate = kay->macsec_validate;
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_alloc_cp_sm -
+ */
+static struct ieee802_1x_cp_sm *
+ieee802_1x_kay_alloc_cp_sm(struct ieee802_1x_kay *kay)
+{
+ struct ieee802_1x_cp_conf conf;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.protect = kay->macsec_protect;
+ conf.replay_protect = kay->macsec_replay_protect;
+ conf.validate = kay->macsec_validate;
+ conf.replay_window = kay->macsec_replay_window;
+
+ return ieee802_1x_cp_sm_init(kay, &conf);
+}
+
+
+/**
+ * ieee802_1x_kay_mkpdu_sanity_check -
+ * sanity check specified in clause 11.11.2 of IEEE802.1X-2010
+ */
+static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
+ const u8 *buf, size_t len)
+{
+ struct ieee8023_hdr *eth_hdr;
+ struct ieee802_1x_hdr *eapol_hdr;
+ struct ieee802_1x_mka_hdr *mka_hdr;
+ struct ieee802_1x_mka_basic_body *body;
+ size_t mka_msg_len;
+ struct ieee802_1x_mka_participant *participant;
+ size_t body_len;
+ u8 icv[MAX_ICV_LEN];
+ u8 *msg_icv;
+
+ eth_hdr = (struct ieee8023_hdr *) buf;
+ eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
+ mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1);
+
+ /* destination address should be not individual address */
+ if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_MSGDUMP,
+ "KaY: ethernet destination address is not PAE group address");
+ return -1;
+ }
+
+ /* MKPDU should not less than 32 octets */
+ mka_msg_len = be_to_host16(eapol_hdr->length);
+ if (mka_msg_len < 32) {
+ wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is less than 32 octets");
+ return -1;
+ }
+ /* MKPDU should multiple 4 octets */
+ if ((mka_msg_len % 4) != 0) {
+ wpa_printf(MSG_MSGDUMP,
+ "KaY: MKPDU is not multiple of 4 octets");
+ return -1;
+ }
+
+ body = (struct ieee802_1x_mka_basic_body *) mka_hdr;
+ ieee802_1x_mka_dump_basic_body(body);
+ body_len = get_mka_param_body_len(body);
+ /* EAPOL-MKA body should comprise basic parameter set and ICV */
+ if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
+ wpa_printf(MSG_ERROR,
+ "KaY: Received EAPOL-MKA Packet Body Length (%d bytes) is less than the Basic Parameter Set Header Length (%d bytes) + the Basic Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+ (int) mka_msg_len, (int) MKA_HDR_LEN,
+ (int) body_len, DEFAULT_ICV_LEN);
+ return -1;
+ }
+
+ /* CKN should be owned by I */
+ participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+ if (!participant) {
+ wpa_printf(MSG_DEBUG, "CKN is not included in my CA");
+ return -1;
+ }
+
+ /* algorithm agility check */
+ if (os_memcmp(body->algo_agility, mka_algo_agility,
+ sizeof(body->algo_agility)) != 0) {
+ wpa_printf(MSG_ERROR,
+ "KaY: peer's algorithm agility not supported for me");
+ return -1;
+ }
+
+ /* ICV check */
+ /*
+ * The ICV will comprise the final octets of the packet body, whatever
+ * its size, not the fixed length 16 octets, indicated by the EAPOL
+ * packet body length.
+ */
+ if (mka_alg_tbl[kay->mka_algindex].icv_hash(
+ participant->ick.key,
+ buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) {
+ wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed");
+ return -1;
+ }
+ msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr,
+ mka_msg_len);
+
+ if (msg_icv) {
+ if (os_memcmp_const(msg_icv, icv,
+ mka_alg_tbl[kay->mka_algindex].icv_len) !=
+ 0) {
+ wpa_printf(MSG_ERROR,
+ "KaY: Computed ICV is not equal to Received ICV");
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "KaY: No ICV");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_decode_mkpdu -
+ */
+static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
+ const u8 *buf, size_t len)
+{
+ struct ieee802_1x_mka_participant *participant;
+ struct ieee802_1x_mka_hdr *hdr;
+ size_t body_len;
+ size_t left_len;
+ int body_type;
+ int i;
+ const u8 *pos;
+ Boolean my_included;
+ Boolean handled[256];
+
+ if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len))
+ return -1;
+
+ /* handle basic parameter set */
+ pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr);
+ left_len = len - sizeof(struct ieee8023_hdr) -
+ sizeof(struct ieee802_1x_hdr);
+ participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len);
+ if (!participant)
+ return -1;
+
+ /* to skip basic parameter set */
+ hdr = (struct ieee802_1x_mka_hdr *) pos;
+ body_len = get_mka_param_body_len(hdr);
+ pos += body_len + MKA_HDR_LEN;
+ left_len -= body_len + MKA_HDR_LEN;
+
+ /* check i am in the peer's peer list */
+ my_included = ieee802_1x_mka_i_in_peerlist(participant, pos, left_len);
+ if (my_included) {
+ /* accept the peer as live peer */
+ if (!ieee802_1x_kay_is_in_peer(
+ participant,
+ participant->current_peer_id.mi)) {
+ if (!ieee802_1x_kay_create_live_peer(
+ participant,
+ participant->current_peer_id.mi,
+ participant->current_peer_id.mn))
+ return -1;
+ ieee802_1x_kay_elect_key_server(participant);
+ ieee802_1x_kay_decide_macsec_use(participant);
+ }
+ if (ieee802_1x_kay_is_in_potential_peer(
+ participant, participant->current_peer_id.mi)) {
+ ieee802_1x_kay_move_live_peer(
+ participant, participant->current_peer_id.mi,
+ participant->current_peer_id.mn);
+ ieee802_1x_kay_elect_key_server(participant);
+ ieee802_1x_kay_decide_macsec_use(participant);
+ }
+ }
+
+ /*
+ * Handle other parameter set than basic parameter set.
+ * Each parameter set should be present only once.
+ */
+ for (i = 0; i < 256; i++)
+ handled[i] = FALSE;
+
+ handled[0] = TRUE;
+ while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
+ hdr = (struct ieee802_1x_mka_hdr *) pos;
+ body_len = get_mka_param_body_len(hdr);
+ body_type = get_mka_param_body_type(hdr);
+
+ if (body_type == MKA_ICV_INDICATOR)
+ return 0;
+
+ if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+ wpa_printf(MSG_ERROR,
+ "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+ (int) left_len, (int) MKA_HDR_LEN,
+ (int) body_len, DEFAULT_ICV_LEN);
+ goto next_para_set;
+ }
+
+ if (handled[body_type])
+ goto next_para_set;
+
+ handled[body_type] = TRUE;
+ if (mak_body_handler[body_type].body_rx) {
+ mak_body_handler[body_type].body_rx
+ (participant, pos, left_len);
+ } else {
+ wpa_printf(MSG_ERROR,
+ "The type %d not supported in this MKA version %d",
+ body_type, MKA_VERSION_ID);
+ }
+
+next_para_set:
+ pos += body_len + MKA_HDR_LEN;
+ left_len -= body_len + MKA_HDR_LEN;
+ }
+
+ kay->active = TRUE;
+ participant->retry_count = 0;
+ participant->active = TRUE;
+
+ return 0;
+}
+
+
+
+static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct ieee802_1x_kay *kay = ctx;
+ struct ieee8023_hdr *eth_hdr;
+ struct ieee802_1x_hdr *eapol_hdr;
+
+ /* must contain at least ieee8023_hdr + ieee802_1x_hdr */
+ if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) {
+ wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ eth_hdr = (struct ieee8023_hdr *) buf;
+ eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
+ if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
+ ntohs(eapol_hdr->length)) {
+ wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)",
+ (unsigned long) len,
+ (unsigned long) ntohs(eapol_hdr->length));
+ return;
+ }
+
+ if (eapol_hdr->version < EAPOL_VERSION) {
+ wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA",
+ eapol_hdr->version);
+ return;
+ }
+ if (ntohs(eth_hdr->ethertype) != ETH_P_PAE ||
+ eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
+ return;
+
+ wpa_hexdump(MSG_DEBUG, "RX EAPOL-MKA: ", buf, len);
+ if (dl_list_empty(&kay->participant_list)) {
+ wpa_printf(MSG_ERROR, "KaY: no MKA participant instance");
+ return;
+ }
+
+ ieee802_1x_kay_decode_mkpdu(kay, buf, len);
+}
+
+
+/**
+ * ieee802_1x_kay_init -
+ */
+struct ieee802_1x_kay *
+ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
+ const char *ifname, const u8 *addr)
+{
+ struct ieee802_1x_kay *kay;
+
+ kay = os_zalloc(sizeof(*kay));
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+ return NULL;
+ }
+
+ kay->ctx = ctx;
+
+ kay->enable = TRUE;
+ kay->active = FALSE;
+
+ kay->authenticated = FALSE;
+ kay->secured = FALSE;
+ kay->failed = FALSE;
+ kay->policy = policy;
+
+ os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
+ os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
+ kay->actor_sci.port = 0x0001;
+ kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+
+ /* While actor acts as a key server, shall distribute sakey */
+ kay->dist_kn = 1;
+ kay->dist_an = 0;
+ kay->dist_time = 0;
+
+ kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
+ kay->macsec_csindex = DEFAULT_CS_INDEX;
+ kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
+ kay->mka_version = MKA_VERSION_ID;
+
+ os_memcpy(kay->algo_agility, mka_algo_agility,
+ sizeof(kay->algo_agility));
+
+ dl_list_init(&kay->participant_list);
+
+ if (policy == DO_NOT_SECURE) {
+ kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
+ kay->macsec_desired = FALSE;
+ kay->macsec_protect = FALSE;
+ kay->macsec_validate = FALSE;
+ kay->macsec_replay_protect = FALSE;
+ kay->macsec_replay_window = 0;
+ kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
+ } else {
+ kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+ kay->macsec_desired = TRUE;
+ kay->macsec_protect = TRUE;
+ kay->macsec_validate = TRUE;
+ kay->macsec_replay_protect = FALSE;
+ kay->macsec_replay_window = 0;
+ kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+ }
+
+ wpa_printf(MSG_DEBUG, "KaY: state machine created");
+
+ /* Initialize the SecY must be prio to CP, as CP will control SecY */
+ secy_init_macsec(kay);
+ secy_get_available_transmit_sc(kay, &kay->sc_ch);
+
+ wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
+
+ /* init CP */
+ kay->cp = ieee802_1x_kay_alloc_cp_sm(kay);
+ if (kay->cp == NULL) {
+ ieee802_1x_kay_deinit(kay);
+ return NULL;
+ }
+
+ if (policy == DO_NOT_SECURE) {
+ ieee802_1x_cp_connect_authenticated(kay->cp);
+ ieee802_1x_cp_sm_step(kay->cp);
+ } else {
+ kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE,
+ kay_l2_receive, kay, 1);
+ if (kay->l2_mka == NULL) {
+ wpa_printf(MSG_WARNING,
+ "KaY: Failed to initialize L2 packet processing for MKA packet");
+ ieee802_1x_kay_deinit(kay);
+ return NULL;
+ }
+ }
+
+ return kay;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit -
+ */
+void
+ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
+{
+ struct ieee802_1x_mka_participant *participant;
+
+ if (!kay)
+ return;
+
+ wpa_printf(MSG_DEBUG, "KaY: state machine removed");
+
+ while (!dl_list_empty(&kay->participant_list)) {
+ participant = dl_list_entry(kay->participant_list.next,
+ struct ieee802_1x_mka_participant,
+ list);
+ ieee802_1x_kay_delete_mka(kay, &participant->ckn);
+ }
+
+ ieee802_1x_cp_sm_deinit(kay->cp);
+ secy_deinit_macsec(kay);
+
+ if (kay->l2_mka) {
+ l2_packet_deinit(kay->l2_mka);
+ kay->l2_mka = NULL;
+ }
+
+ os_free(kay->ctx);
+ os_free(kay);
+}
+
+
+/**
+ * ieee802_1x_kay_create_mka -
+ */
+struct ieee802_1x_mka_participant *
+ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn,
+ struct mka_key *cak, u32 life,
+ enum mka_created_mode mode, Boolean is_authenticator)
+{
+ struct ieee802_1x_mka_participant *participant;
+ unsigned int usecs;
+
+ if (!kay || !ckn || !cak) {
+ wpa_printf(MSG_ERROR, "KaY: ckn or cak is null");
+ return NULL;
+ }
+
+ if (cak->len != mka_alg_tbl[kay->mka_algindex].cak_len) {
+ wpa_printf(MSG_ERROR, "KaY: CAK length not follow key schema");
+ return NULL;
+ }
+ if (ckn->len > MAX_CKN_LEN) {
+ wpa_printf(MSG_ERROR, "KaY: CKN is out of range(<=32 bytes)");
+ return NULL;
+ }
+ if (!kay->enable) {
+ wpa_printf(MSG_ERROR, "KaY: Now is at disable state");
+ return NULL;
+ }
+
+ participant = os_zalloc(sizeof(*participant));
+ if (!participant) {
+ wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+ return NULL;
+ }
+
+ participant->ckn.len = ckn->len;
+ os_memcpy(participant->ckn.name, ckn->name, ckn->len);
+ participant->cak.len = cak->len;
+ os_memcpy(participant->cak.key, cak->key, cak->len);
+ if (life)
+ participant->cak_life = life + time(NULL);
+
+ switch (mode) {
+ case EAP_EXCHANGE:
+ if (is_authenticator) {
+ participant->is_obliged_key_server = TRUE;
+ participant->can_be_key_server = TRUE;
+ participant->is_key_server = TRUE;
+ participant->principal = TRUE;
+
+ os_memcpy(&kay->key_server_sci, &kay->actor_sci,
+ sizeof(kay->key_server_sci));
+ kay->key_server_priority = kay->actor_priority;
+ participant->is_elected = TRUE;
+ } else {
+ participant->is_obliged_key_server = FALSE;
+ participant->can_be_key_server = FALSE;
+ participant->is_key_server = FALSE;
+ participant->is_elected = TRUE;
+ }
+ break;
+
+ default:
+ participant->is_obliged_key_server = FALSE;
+ participant->can_be_key_server = TRUE;
+ participant->is_key_server = FALSE;
+ participant->is_elected = FALSE;
+ break;
+ }
+
+ participant->cached = FALSE;
+
+ participant->active = FALSE;
+ participant->participant = FALSE;
+ participant->retain = FALSE;
+ participant->activate = DEFAULT;
+
+ if (participant->is_key_server)
+ participant->principal = TRUE;
+
+ dl_list_init(&participant->live_peers);
+ dl_list_init(&participant->potential_peers);
+
+ participant->retry_count = 0;
+ participant->kay = kay;
+
+ os_get_random(participant->mi, sizeof(participant->mi));
+ participant->mn = 0;
+
+ participant->lrx = FALSE;
+ participant->ltx = FALSE;
+ participant->orx = FALSE;
+ participant->otx = FALSE;
+ participant->to_dist_sak = FALSE;
+ participant->to_use_sak = FALSE;
+ participant->new_sak = FALSE;
+ dl_list_init(&participant->sak_list);
+ participant->new_key = NULL;
+ dl_list_init(&participant->rxsc_list);
+ participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci,
+ kay->sc_ch);
+ secy_create_transmit_sc(kay, participant->txsc);
+
+ /* to derive KEK from CAK and CKN */
+ participant->kek.len = mka_alg_tbl[kay->mka_algindex].kek_len;
+ if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key,
+ participant->ckn.name,
+ participant->ckn.len,
+ participant->kek.key)) {
+ wpa_printf(MSG_ERROR, "KaY: Derived KEK failed");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK",
+ participant->kek.key, participant->kek.len);
+
+ /* to derive ICK from CAK and CKN */
+ participant->ick.len = mka_alg_tbl[kay->mka_algindex].ick_len;
+ if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key,
+ participant->ckn.name,
+ participant->ckn.len,
+ participant->ick.key)) {
+ wpa_printf(MSG_ERROR, "KaY: Derived ICK failed");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK",
+ participant->ick.key, participant->ick.len);
+
+ dl_list_add(&kay->participant_list, &participant->list);
+ wpa_hexdump(MSG_DEBUG, "KaY: Participant created:",
+ ckn->name, ckn->len);
+
+ usecs = os_random() % (MKA_HELLO_TIME * 1000);
+ eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
+ participant, NULL);
+ participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
+ usecs / 1000000;
+
+ return participant;
+
+fail:
+ os_free(participant);
+ return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_delete_mka -
+ */
+void
+ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
+{
+ struct ieee802_1x_mka_participant *participant;
+ struct ieee802_1x_kay_peer *peer;
+ struct data_key *sak;
+ struct receive_sc *rxsc;
+
+ if (!kay || !ckn)
+ return;
+
+ wpa_printf(MSG_DEBUG, "KaY: participant removed");
+
+ /* get the participant */
+ participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+ if (!participant) {
+ wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
+ ckn->name, ckn->len);
+ return;
+ }
+
+ dl_list_del(&participant->list);
+
+ /* remove live peer */
+ while (!dl_list_empty(&participant->live_peers)) {
+ peer = dl_list_entry(participant->live_peers.next,
+ struct ieee802_1x_kay_peer, list);
+ dl_list_del(&peer->list);
+ os_free(peer);
+ }
+
+ /* remove potential peer */
+ while (!dl_list_empty(&participant->potential_peers)) {
+ peer = dl_list_entry(participant->potential_peers.next,
+ struct ieee802_1x_kay_peer, list);
+ dl_list_del(&peer->list);
+ os_free(peer);
+ }
+
+ /* remove sak */
+ while (!dl_list_empty(&participant->sak_list)) {
+ sak = dl_list_entry(participant->sak_list.next,
+ struct data_key, list);
+ dl_list_del(&sak->list);
+ os_free(sak->key);
+ os_free(sak);
+ }
+ while (!dl_list_empty(&participant->rxsc_list)) {
+ rxsc = dl_list_entry(participant->rxsc_list.next,
+ struct receive_sc, list);
+ secy_delete_receive_sc(kay, rxsc);
+ ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
+ }
+ secy_delete_transmit_sc(kay, participant->txsc);
+ ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
+
+ os_memset(&participant->cak, 0, sizeof(participant->cak));
+ os_memset(&participant->kek, 0, sizeof(participant->kek));
+ os_memset(&participant->ick, 0, sizeof(participant->ick));
+ os_free(participant);
+}
+
+
+/**
+ * ieee802_1x_kay_mka_participate -
+ */
+void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
+ struct mka_key_name *ckn,
+ Boolean status)
+{
+ struct ieee802_1x_mka_participant *participant;
+
+ if (!kay || !ckn)
+ return;
+
+ participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+ if (!participant)
+ return;
+
+ participant->active = status;
+}
+
+
+/**
+ * ieee802_1x_kay_new_sak -
+ */
+int
+ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay)
+{
+ struct ieee802_1x_mka_participant *participant;
+
+ if (!kay)
+ return -1;
+
+ participant = ieee802_1x_kay_get_principal_participant(kay);
+ if (!participant)
+ return -1;
+
+ participant->new_sak = TRUE;
+ wpa_printf(MSG_DEBUG, "KaY: new SAK signal");
+
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_change_cipher_suite -
+ */
+int
+ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, int cs_index)
+{
+ struct ieee802_1x_mka_participant *participant;
+
+ if (!kay)
+ return -1;
+
+ if ((unsigned int) cs_index >= CS_TABLE_SIZE) {
+ wpa_printf(MSG_ERROR,
+ "KaY: Configured cipher suite index is out of range");
+ return -1;
+ }
+ if (kay->macsec_csindex == cs_index)
+ return -2;
+
+ if (cs_index == 0)
+ kay->macsec_desired = FALSE;
+
+ kay->macsec_csindex = cs_index;
+ kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
+
+ participant = ieee802_1x_kay_get_principal_participant(kay);
+ if (participant) {
+ wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
+ participant->new_sak = TRUE;
+ }
+
+ return 0;
+}
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
new file mode 100644
index 0000000..064417e
--- /dev/null
+++ b/src/pae/ieee802_1x_kay.h
@@ -0,0 +1,194 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KAY_H
+#define IEEE802_1X_KAY_H
+
+#include "utils/list.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct macsec_init_params;
+struct ieee802_1x_cp_conf;
+
+#define MI_LEN 12
+#define MAX_KEY_LEN 32 /* 32 bytes, 256 bits */
+#define MAX_CKN_LEN 32 /* 32 bytes, 256 bits */
+
+/* MKA timer, unit: millisecond */
+#define MKA_HELLO_TIME 2000
+#define MKA_LIFE_TIME 6000
+#define MKA_SAK_RETIRE_TIME 3000
+
+struct ieee802_1x_mka_ki {
+ u8 mi[MI_LEN];
+ u32 kn;
+};
+
+struct ieee802_1x_mka_sci {
+ u8 addr[ETH_ALEN];
+ u16 port;
+};
+
+struct mka_key {
+ u8 key[MAX_KEY_LEN];
+ size_t len;
+};
+
+struct mka_key_name {
+ u8 name[MAX_CKN_LEN];
+ size_t len;
+};
+
+enum mka_created_mode {
+ PSK,
+ EAP_EXCHANGE,
+ DISTRIBUTED,
+ CACHED,
+};
+
+struct ieee802_1x_kay_ctx {
+ /* pointer to arbitrary upper level context */
+ void *ctx;
+
+ /* abstract wpa driver interface */
+ int (*macsec_init)(void *ctx, struct macsec_init_params *params);
+ int (*macsec_deinit)(void *ctx);
+ int (*enable_protect_frames)(void *ctx, Boolean enabled);
+ int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window);
+ int (*set_current_cipher_suite)(void *ctx, const u8 *cs, size_t cs_len);
+ int (*enable_controlled_port)(void *ctx, Boolean enabled);
+ int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an,
+ u32 *lowest_pn);
+ int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an,
+ u32 *next_pn);
+ int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn);
+ int (*get_available_receive_sc)(void *ctx, u32 *channel);
+ int (*create_receive_sc)(void *ctx, u32 channel,
+ struct ieee802_1x_mka_sci *sci,
+ enum validate_frames vf,
+ enum confidentiality_offset co);
+ int (*delete_receive_sc)(void *ctx, u32 channel);
+ int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn,
+ const u8 *sak);
+ int (*enable_receive_sa)(void *ctx, u32 channel, u8 an);
+ int (*disable_receive_sa)(void *ctx, u32 channel, u8 an);
+ int (*get_available_transmit_sc)(void *ctx, u32 *channel);
+ int (*create_transmit_sc)(void *ctx, u32 channel,
+ const struct ieee802_1x_mka_sci *sci,
+ enum confidentiality_offset co);
+ int (*delete_transmit_sc)(void *ctx, u32 channel);
+ int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn,
+ Boolean confidentiality, const u8 *sak);
+ int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an);
+ int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an);
+};
+
+struct ieee802_1x_kay {
+ Boolean enable;
+ Boolean active;
+
+ Boolean authenticated;
+ Boolean secured;
+ Boolean failed;
+
+ struct ieee802_1x_mka_sci actor_sci;
+ u8 actor_priority;
+ struct ieee802_1x_mka_sci key_server_sci;
+ u8 key_server_priority;
+
+ enum macsec_cap macsec_capable;
+ Boolean macsec_desired;
+ Boolean macsec_protect;
+ Boolean macsec_replay_protect;
+ u32 macsec_replay_window;
+ enum validate_frames macsec_validate;
+ enum confidentiality_offset macsec_confidentiality;
+
+ u32 ltx_kn;
+ u8 ltx_an;
+ u32 lrx_kn;
+ u8 lrx_an;
+
+ u32 otx_kn;
+ u8 otx_an;
+ u32 orx_kn;
+ u8 orx_an;
+
+ /* not defined in IEEE802.1X */
+ struct ieee802_1x_kay_ctx *ctx;
+ Boolean is_key_server;
+ Boolean is_obliged_key_server;
+ char if_name[IFNAMSIZ];
+
+ int macsec_csindex; /* MACsec cipher suite table index */
+ int mka_algindex; /* MKA alg table index */
+
+ u32 dist_kn;
+ u8 dist_an;
+ time_t dist_time;
+
+ u8 mka_version;
+ u8 algo_agility[4];
+ u32 sc_ch;
+
+ u32 pn_exhaustion;
+ Boolean port_enable;
+ Boolean rx_enable;
+ Boolean tx_enable;
+
+ struct dl_list participant_list;
+ enum macsec_policy policy;
+
+ struct ieee802_1x_cp_sm *cp;
+
+ struct l2_packet_data *l2_mka;
+
+ enum validate_frames vf;
+ enum confidentiality_offset co;
+};
+
+
+struct ieee802_1x_kay *
+ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
+ const char *ifname, const u8 *addr);
+void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
+
+struct ieee802_1x_mka_participant *
+ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
+ struct mka_key_name *ckn, struct mka_key *cak,
+ u32 life, enum mka_created_mode mode,
+ Boolean is_authenticator);
+void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay,
+ struct mka_key_name *ckn);
+void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
+ struct mka_key_name *ckn,
+ Boolean status);
+int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay);
+int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
+ int cs_index);
+
+int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *lki, u8 lan,
+ Boolean ltx, Boolean lrx);
+int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *oki,
+ u8 oan, Boolean otx, Boolean orx);
+int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *ki);
+int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay);
+int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay,
+ struct ieee802_1x_cp_conf *pconf);
+
+#endif /* IEEE802_1X_KAY_H */
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
new file mode 100644
index 0000000..bdad3a5
--- /dev/null
+++ b/src/pae/ieee802_1x_kay_i.h
@@ -0,0 +1,419 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KAY_I_H
+#define IEEE802_1X_KAY_I_H
+
+#include "utils/list.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+#define MKA_VERSION_ID 1
+
+/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 */
+enum mka_packet_type {
+ MKA_BASIC_PARAMETER_SET = MKA_VERSION_ID,
+ MKA_LIVE_PEER_LIST = 1,
+ MKA_POTENTIAL_PEER_LIST = 2,
+ MKA_SAK_USE = 3,
+ MKA_DISTRIBUTED_SAK = 4,
+ MKA_DISTRIBUTED_CAK = 5,
+ MKA_KMD = 6,
+ MKA_ANNOUNCEMENT = 7,
+ MKA_ICV_INDICATOR = 255
+};
+
+#define ICV_LEN 16 /* 16 bytes */
+#define SAK_WRAPPED_LEN 24
+/* KN + Wrapper SAK */
+#define DEFAULT_DIS_SAK_BODY_LENGTH (SAK_WRAPPED_LEN + 4)
+#define MAX_RETRY_CNT 5
+
+struct ieee802_1x_kay;
+
+struct ieee802_1x_mka_peer_id {
+ u8 mi[MI_LEN];
+ u32 mn;
+};
+
+struct ieee802_1x_kay_peer {
+ struct ieee802_1x_mka_sci sci;
+ u8 mi[MI_LEN];
+ u32 mn;
+ time_t expire;
+ Boolean is_key_server;
+ u8 key_server_priority;
+ Boolean macsec_desired;
+ enum macsec_cap macsec_capbility;
+ Boolean sak_used;
+ struct dl_list list;
+};
+
+struct key_conf {
+ u8 *key;
+ struct ieee802_1x_mka_ki ki;
+ enum confidentiality_offset offset;
+ u8 an;
+ Boolean tx;
+ Boolean rx;
+ int key_len; /* unit: byte */
+};
+
+struct data_key {
+ u8 *key;
+ int key_len;
+ struct ieee802_1x_mka_ki key_identifier;
+ enum confidentiality_offset confidentiality_offset;
+ u8 an;
+ Boolean transmits;
+ Boolean receives;
+ struct os_time created_time;
+ u32 next_pn;
+
+ /* not defined data */
+ Boolean rx_latest;
+ Boolean tx_latest;
+
+ int user; /* FIXME: to indicate if it can be delete safely */
+
+ struct dl_list list;
+};
+
+/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sc {
+ struct ieee802_1x_mka_sci sci; /* const SCI sci */
+ Boolean transmitting; /* bool transmitting (read only) */
+
+ struct os_time created_time; /* Time createdTime */
+
+ u8 encoding_sa; /* AN encodingSA (read only) */
+ u8 enciphering_sa; /* AN encipheringSA (read only) */
+
+ /* not defined data */
+ unsigned int channel;
+
+ struct dl_list list;
+ struct dl_list sa_list;
+};
+
+/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sa {
+ Boolean in_use; /* bool inUse (read only) */
+ u32 next_pn; /* PN nextPN (read only) */
+ struct os_time created_time; /* Time createdTime */
+
+ Boolean enable_transmit; /* bool EnableTransmit */
+
+ u8 an;
+ Boolean confidentiality;
+ struct data_key *pkey;
+
+ struct transmit_sc *sc;
+ struct dl_list list; /* list entry in struct transmit_sc::sa_list */
+};
+
+/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sc {
+ struct ieee802_1x_mka_sci sci; /* const SCI sci */
+ Boolean receiving; /* bool receiving (read only) */
+
+ struct os_time created_time; /* Time createdTime */
+
+ unsigned int channel;
+
+ struct dl_list list;
+ struct dl_list sa_list;
+};
+
+/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sa {
+ Boolean enable_receive; /* bool enableReceive */
+ Boolean in_use; /* bool inUse (read only) */
+
+ u32 next_pn; /* PN nextPN (read only) */
+ u32 lowest_pn; /* PN lowestPN (read only) */
+ u8 an;
+ struct os_time created_time;
+
+ struct data_key *pkey;
+ struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
+
+ struct dl_list list;
+};
+
+struct macsec_ciphersuite {
+ u8 id[CS_ID_LEN];
+ char name[32];
+ enum macsec_cap capable;
+ int sak_len; /* unit: byte */
+
+ u32 index;
+};
+
+struct mka_alg {
+ u8 parameter[4];
+ size_t cak_len;
+ size_t kek_len;
+ size_t ick_len;
+ size_t icv_len;
+
+ int (*cak_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2, u8 *cak);
+ int (*ckn_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2,
+ const u8 *sid, size_t sid_len, u8 *ckn);
+ int (*kek_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *kek);
+ int (*ick_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *ick);
+ int (*icv_hash)(const u8 *ick, const u8 *msg, size_t msg_len, u8 *icv);
+
+ int index; /* index for configuring */
+};
+
+#define DEFAULT_MKA_ALG_INDEX 0
+
+/* See IEEE Std 802.1X-2010, 9.16 MKA management */
+struct ieee802_1x_mka_participant {
+ /* used for active and potential participant */
+ struct mka_key_name ckn;
+ struct mka_key cak;
+ Boolean cached;
+
+ /* used by management to monitor and control activation */
+ Boolean active;
+ Boolean participant;
+ Boolean retain;
+
+ enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate;
+
+ /* used for active participant */
+ Boolean principal;
+ struct dl_list live_peers;
+ struct dl_list potential_peers;
+
+ /* not defined in IEEE 802.1X */
+ struct dl_list list;
+
+ struct mka_key kek;
+ struct mka_key ick;
+
+ struct ieee802_1x_mka_ki lki;
+ u8 lan;
+ Boolean ltx;
+ Boolean lrx;
+
+ struct ieee802_1x_mka_ki oki;
+ u8 oan;
+ Boolean otx;
+ Boolean orx;
+
+ Boolean is_key_server;
+ Boolean is_obliged_key_server;
+ Boolean can_be_key_server;
+ Boolean is_elected;
+
+ struct dl_list sak_list;
+ struct dl_list rxsc_list;
+
+ struct transmit_sc *txsc;
+
+ u8 mi[MI_LEN];
+ u32 mn;
+
+ struct ieee802_1x_mka_peer_id current_peer_id;
+ struct ieee802_1x_mka_sci current_peer_sci;
+ time_t cak_life;
+ time_t mka_life;
+ Boolean to_dist_sak;
+ Boolean to_use_sak;
+ Boolean new_sak;
+
+ Boolean advised_desired;
+ enum macsec_cap advised_capability;
+
+ struct data_key *new_key;
+ u32 retry_count;
+
+ struct ieee802_1x_kay *kay;
+};
+
+struct ieee802_1x_mka_hdr {
+ /* octet 1 */
+ u32 type:8;
+ /* octet 2 */
+ u32 reserve:8;
+ /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u32 length:4;
+ u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u32 reserve1:4;
+ u32 length:4;
+#else
+#error "Please fix <bits/endian.h>"
+#endif
+ /* octet 4 */
+ u32 length1:8;
+};
+
+#define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr)
+
+struct ieee802_1x_mka_basic_body {
+ /* octet 1 */
+ u32 version:8;
+ /* octet 2 */
+ u32 priority:8;
+ /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u32 length:4;
+ u32 macsec_capbility:2;
+ u32 macsec_desired:1;
+ u32 key_server:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u32 key_server:1;
+ u32 macsec_desired:1;
+ u32 macsec_capbility:2;
+ u32 length:4;
+#endif
+ /* octet 4 */
+ u32 length1:8;
+
+ struct ieee802_1x_mka_sci actor_sci;
+ u8 actor_mi[MI_LEN];
+ u32 actor_mn;
+ u8 algo_agility[4];
+
+ /* followed by CAK Name*/
+ u8 ckn[0];
+};
+
+struct ieee802_1x_mka_peer_body {
+ /* octet 1 */
+ u32 type:8;
+ /* octet 2 */
+ u32 reserve:8;
+ /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u32 length:4;
+ u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u32 reserve1:4;
+ u32 length:4;
+#endif
+ /* octet 4 */
+ u32 length1:8;
+
+ u8 peer[0];
+ /* followed by Peers */
+};
+
+struct ieee802_1x_mka_sak_use_body {
+ /* octet 1 */
+ u32 type:8;
+ /* octet 2 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u32 orx:1;
+ u32 otx:1;
+ u32 oan:2;
+ u32 lrx:1;
+ u32 ltx:1;
+ u32 lan:2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u32 lan:2;
+ u32 ltx:1;
+ u32 lrx:1;
+ u32 oan:2;
+ u32 otx:1;
+ u32 orx:1;
+#endif
+
+ /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u32 length:4;
+ u32 delay_protect:1;
+ u32 reserve:1;
+ u32 prx:1;
+ u32 ptx:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u32 ptx:1;
+ u32 prx:1;
+ u32 reserve:1;
+ u32 delay_protect:1;
+ u32 length:4;
+#endif
+
+ /* octet 4 */
+ u32 length1:8;
+
+ /* octet 5 - 16 */
+ u8 lsrv_mi[MI_LEN];
+ /* octet 17 - 20 */
+ u32 lkn;
+ /* octet 21 - 24 */
+ u32 llpn;
+
+ /* octet 25 - 36 */
+ u8 osrv_mi[MI_LEN];
+ /* octet 37 - 40 */
+ u32 okn;
+ /* octet 41 - 44 */
+ u32 olpn;
+};
+
+
+struct ieee802_1x_mka_dist_sak_body {
+ /* octet 1 */
+ u32 type:8;
+ /* octet 2 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u32 reserve:4;
+ u32 confid_offset:2;
+ u32 dan:2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u32 dan:2;
+ u32 confid_offset:2;
+ u32 reserve:4;
+#endif
+ /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u32 length:4;
+ u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u32 reserve1:4;
+ u32 length:4;
+#endif
+ /* octet 4 */
+ u32 length1:8;
+ /* octet 5 - 8 */
+ u32 kn;
+
+ /* for GCM-AES-128: octet 9-32: SAK
+ * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK
+ */
+ u8 sak[0];
+};
+
+
+struct ieee802_1x_mka_icv_body {
+ /* octet 1 */
+ u32 type:8;
+ /* octet 2 */
+ u32 reserve:8;
+ /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u32 length:4;
+ u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u32 reserve1:4;
+ u32 length:4;
+#endif
+ /* octet 4 */
+ u32 length1:8;
+
+ /* octet 5 - */
+ u8 icv[0];
+};
+
+#endif /* IEEE802_1X_KAY_I_H */
diff --git a/src/pae/ieee802_1x_key.c b/src/pae/ieee802_1x_key.c
new file mode 100644
index 0000000..9a8d923
--- /dev/null
+++ b/src/pae/ieee802_1x_key.c
@@ -0,0 +1,189 @@
+/*
+ * IEEE 802.1X-2010 Key Hierarchy
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * SAK derivation specified in IEEE Std 802.1X-2010, Clause 6.2
+*/
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "ieee802_1x_key.h"
+
+
+static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out)
+{
+ if (os_memcmp(mac1, mac2, ETH_ALEN) < 0) {
+ os_memcpy(out, mac1, ETH_ALEN);
+ os_memcpy(out + ETH_ALEN, mac2, ETH_ALEN);
+ } else {
+ os_memcpy(out, mac2, ETH_ALEN);
+ os_memcpy(out + ETH_ALEN, mac1, ETH_ALEN);
+ }
+}
+
+
+/* IEEE Std 802.1X-2010, 6.2.1 KDF */
+static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context,
+ int ctx_bits, int ret_bits, u8 *ret)
+{
+ const int h = 128;
+ const int r = 8;
+ int i, n;
+ int lab_len, ctx_len, ret_len, buf_len;
+ u8 *buf;
+
+ lab_len = os_strlen(label);
+ ctx_len = (ctx_bits + 7) / 8;
+ ret_len = ((ret_bits & 0xffff) + 7) / 8;
+ buf_len = lab_len + ctx_len + 4;
+
+ os_memset(ret, 0, ret_len);
+
+ n = (ret_bits + h - 1) / h;
+ if (n > ((0x1 << r) - 1))
+ return -1;
+
+ buf = os_zalloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ os_memcpy(buf + 1, label, lab_len);
+ os_memcpy(buf + lab_len + 2, context, ctx_len);
+ WPA_PUT_BE16(&buf[buf_len - 2], ret_bits);
+
+ for (i = 0; i < n; i++) {
+ buf[0] = (u8) (i + 1);
+ if (omac1_aes_128(kdk, buf, buf_len, ret)) {
+ os_free(buf);
+ return -1;
+ }
+ ret = ret + h / 8;
+ }
+ os_free(buf);
+ return 0;
+}
+
+
+/********** AES-CMAC-128 **********/
+/**
+ * ieee802_1x_cak_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 6.2.2
+ * CAK = KDF(Key, Label, mac1 | mac2, CAKlength)
+ */
+int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+ const u8 *mac2, u8 *cak)
+{
+ u8 context[2 * ETH_ALEN];
+
+ joint_two_mac(mac1, mac2, context);
+ return aes_kdf_128(msk, "IEEE8021 EAP CAK",
+ context, sizeof(context) * 8, 128, cak);
+}
+
+
+/**
+ * ieee802_1x_ckn_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 6.2.2
+ * CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength)
+ */
+int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+ const u8 *mac2, const u8 *sid,
+ size_t sid_bytes, u8 *ckn)
+{
+ int res;
+ u8 *context;
+ size_t ctx_len = sid_bytes + ETH_ALEN * 2;
+
+ context = os_zalloc(ctx_len);
+ if (!context) {
+ wpa_printf(MSG_ERROR, "MKA-%s: out of memory", __func__);
+ return -1;
+ }
+ os_memcpy(context, sid, sid_bytes);
+ joint_two_mac(mac1, mac2, context + sid_bytes);
+
+ res = aes_kdf_128(msk, "IEEE8021 EAP CKN", context, ctx_len * 8,
+ 128, ckn);
+ os_free(context);
+ return res;
+}
+
+
+/**
+ * ieee802_1x_kek_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.3.3
+ * KEK = KDF(Key, Label, Keyid, KEKLength)
+ */
+int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+ size_t ckn_bytes, u8 *kek)
+{
+ u8 context[16];
+
+ /* First 16 octets of CKN, with null octets appended to pad if needed */
+ os_memset(context, 0, sizeof(context));
+ os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
+
+ return aes_kdf_128(cak, "IEEE8021 KEK", context, sizeof(context) * 8,
+ 128, kek);
+}
+
+
+/**
+ * ieee802_1x_ick_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.3.3
+ * ICK = KDF(Key, Label, Keyid, ICKLength)
+ */
+int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+ size_t ckn_bytes, u8 *ick)
+{
+ u8 context[16];
+
+ /* First 16 octets of CKN, with null octets appended to pad if needed */
+ os_memset(context, 0, sizeof(context));
+ os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
+
+ return aes_kdf_128(cak, "IEEE8021 ICK", context, sizeof(context) * 8,
+ 128, ick);
+}
+
+
+/**
+ * ieee802_1x_icv_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.4.1
+ * ICV = AES-CMAC(ICK, M, 128)
+ */
+int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
+ size_t msg_bytes, u8 *icv)
+{
+ if (omac1_aes_128(ick, msg, msg_bytes, icv)) {
+ wpa_printf(MSG_ERROR, "MKA: omac1_aes_128 failed");
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * ieee802_1x_sak_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.8.1
+ * SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength)
+ */
+int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx,
+ size_t ctx_bytes, u8 *sak)
+{
+ return aes_kdf_128(cak, "IEEE8021 SAK", ctx, ctx_bytes * 8, 128, sak);
+}
diff --git a/src/pae/ieee802_1x_key.h b/src/pae/ieee802_1x_key.h
new file mode 100644
index 0000000..ea318ea
--- /dev/null
+++ b/src/pae/ieee802_1x_key.h
@@ -0,0 +1,26 @@
+/*
+ * IEEE 802.1X-2010 Key Hierarchy
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KEY_H
+#define IEEE802_1X_KEY_H
+
+int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+ const u8 *mac2, u8 *cak);
+int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+ const u8 *mac2, const u8 *sid,
+ size_t sid_bytes, u8 *ckn);
+int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+ size_t ckn_bytes, u8 *kek);
+int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+ size_t ckn_bytes, u8 *ick);
+int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
+ size_t msg_bytes, u8 *icv);
+int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx,
+ size_t ctx_bytes, u8 *sak);
+
+#endif /* IEEE802_1X_KEY_H */
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
new file mode 100644
index 0000000..fbe05dc
--- /dev/null
+++ b/src/pae/ieee802_1x_secy_ops.c
@@ -0,0 +1,492 @@
+ /*
+ * SecY Operations
+ * 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 "utils/eloop.h"
+#include "common/defs.h"
+#include "drivers/driver.h"
+#include "pae/ieee802_1x_kay.h"
+#include "pae/ieee802_1x_kay_i.h"
+#include "pae/ieee802_1x_secy_ops.h"
+
+
+int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
+ enum validate_frames vf)
+{
+ kay->vf = vf;
+ return 0;
+}
+
+
+int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean enabled)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->enable_protect_frames) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy enable_protect_frames operation not supported");
+ return -1;
+ }
+
+ return ops->enable_protect_frames(ops->ctx, enabled);
+}
+
+
+int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->set_replay_protect) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy set_replay_protect operation not supported");
+ return -1;
+ }
+
+ return ops->set_replay_protect(ops->ctx, enabled, win);
+}
+
+
+int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay,
+ const u8 *cs, size_t cs_len)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->set_current_cipher_suite) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy set_current_cipher_suite operation not supported");
+ return -1;
+ }
+
+ return ops->set_current_cipher_suite(ops->ctx, cs, cs_len);
+}
+
+
+int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
+ enum confidentiality_offset co)
+{
+ kay->co = co;
+ return 0;
+}
+
+
+int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean enabled)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->enable_controlled_port) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy enable_controlled_port operation not supported");
+ return -1;
+ }
+
+ return ops->enable_controlled_port(ops->ctx, enabled);
+}
+
+
+int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
+ struct receive_sa *rxsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !rxsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->get_receive_lowest_pn) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy get_receive_lowest_pn operation not supported");
+ return -1;
+ }
+
+ return ops->get_receive_lowest_pn(ops->ctx,
+ rxsa->sc->channel,
+ rxsa->an,
+ &rxsa->lowest_pn);
+}
+
+
+int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !txsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->get_transmit_next_pn) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy get_receive_lowest_pn operation not supported");
+ return -1;
+ }
+
+ return ops->get_transmit_next_pn(ops->ctx,
+ txsa->sc->channel,
+ txsa->an,
+ &txsa->next_pn);
+}
+
+
+int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !txsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->set_transmit_next_pn) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy get_receive_lowest_pn operation not supported");
+ return -1;
+ }
+
+ return ops->set_transmit_next_pn(ops->ctx,
+ txsa->sc->channel,
+ txsa->an,
+ txsa->next_pn);
+}
+
+
+int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->get_available_receive_sc) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy get_available_receive_sc operation not supported");
+ return -1;
+ }
+
+ return ops->get_available_receive_sc(ops->ctx, channel);
+}
+
+
+int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !rxsc) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->create_receive_sc) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy create_receive_sc operation not supported");
+ return -1;
+ }
+
+ return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci,
+ kay->vf, kay->co);
+}
+
+
+int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !rxsc) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->delete_receive_sc) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy delete_receive_sc operation not supported");
+ return -1;
+ }
+
+ return ops->delete_receive_sc(ops->ctx, rxsc->channel);
+}
+
+
+int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !rxsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->create_receive_sa) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy create_receive_sa operation not supported");
+ return -1;
+ }
+
+ return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an,
+ rxsa->lowest_pn, rxsa->pkey->key);
+}
+
+
+int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !rxsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->enable_receive_sa) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy enable_receive_sa operation not supported");
+ return -1;
+ }
+
+ rxsa->enable_receive = TRUE;
+
+ return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+}
+
+
+int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !rxsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->disable_receive_sa) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy disable_receive_sa operation not supported");
+ return -1;
+ }
+
+ rxsa->enable_receive = FALSE;
+
+ return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+}
+
+
+int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->get_available_transmit_sc) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy get_available_transmit_sc operation not supported");
+ return -1;
+ }
+
+ return ops->get_available_transmit_sc(ops->ctx, channel);
+}
+
+
+int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
+ struct transmit_sc *txsc)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !txsc) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->create_transmit_sc) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy create_transmit_sc operation not supported");
+ return -1;
+ }
+
+ return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci,
+ kay->co);
+}
+
+
+int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
+ struct transmit_sc *txsc)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !txsc) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->delete_transmit_sc) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy delete_transmit_sc operation not supported");
+ return -1;
+ }
+
+ return ops->delete_transmit_sc(ops->ctx, txsc->channel);
+}
+
+
+int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !txsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->create_transmit_sa) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy create_transmit_sa operation not supported");
+ return -1;
+ }
+
+ return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an,
+ txsa->next_pn, txsa->confidentiality,
+ txsa->pkey->key);
+}
+
+
+int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !txsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->enable_transmit_sa) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy enable_transmit_sa operation not supported");
+ return -1;
+ }
+
+ txsa->enable_transmit = TRUE;
+
+ return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+}
+
+
+int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !txsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->disable_transmit_sa) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy disable_transmit_sa operation not supported");
+ return -1;
+ }
+
+ txsa->enable_transmit = FALSE;
+
+ return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+}
+
+
+int secy_init_macsec(struct ieee802_1x_kay *kay)
+{
+ int ret;
+ struct ieee802_1x_kay_ctx *ops;
+ struct macsec_init_params params;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->macsec_init) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy macsec_init operation not supported");
+ return -1;
+ }
+
+ params.use_es = FALSE;
+ params.use_scb = FALSE;
+ params.always_include_sci = TRUE;
+
+ ret = ops->macsec_init(ops->ctx, ¶ms);
+
+ return ret;
+}
+
+
+int secy_deinit_macsec(struct ieee802_1x_kay *kay)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->macsec_deinit) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy macsec_deinit operation not supported");
+ return -1;
+ }
+
+ return ops->macsec_deinit(ops->ctx);
+}
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
new file mode 100644
index 0000000..295b823
--- /dev/null
+++ b/src/pae/ieee802_1x_secy_ops.h
@@ -0,0 +1,62 @@
+ /*
+ * SecY Operations
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_SECY_OPS_H
+#define IEEE802_1X_SECY_OPS_H
+
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct ieee802_1x_kay_conf;
+struct receive_sa;
+struct transmit_sa;
+struct receive_sc;
+struct transmit_sc;
+
+int secy_init_macsec(struct ieee802_1x_kay *kay);
+int secy_deinit_macsec(struct ieee802_1x_kay *kay);
+
+/****** CP -> SecY ******/
+int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
+ enum validate_frames vf);
+int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag);
+int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win);
+int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay,
+ const u8 *cs, size_t cs_len);
+int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
+ enum confidentiality_offset co);
+int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag);
+
+/****** KaY -> SecY *******/
+int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
+ struct receive_sa *rxsa);
+int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa);
+int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa);
+int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel);
+int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
+int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
+int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_disable_receive_sa(struct ieee802_1x_kay *kay,
+ struct receive_sa *rxsa);
+
+int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel);
+int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
+ struct transmit_sc *txsc);
+int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
+ struct transmit_sc *txsc);
+int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa);
+int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa);
+int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa);
+
+#endif /* IEEE802_1X_SECY_OPS_H */
diff --git a/src/radius/Makefile b/src/radius/Makefile
index b199be8..b5d063d 100644
--- a/src/radius/Makefile
+++ b/src/radius/Makefile
@@ -1,7 +1,7 @@
all: libradius.a
clean:
- rm -f *~ *.o *.d libradius.a
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a
install:
@echo Nothing to be made.
diff --git a/src/radius/radius.c b/src/radius/radius.c
index d1feec9..f3b645d 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
/*
* RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -231,9 +231,21 @@
{ 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 }
+ { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP },
+ { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher",
+ RADIUS_ATTR_HEXDUMP },
+ { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher",
+ RADIUS_ATTR_HEXDUMP },
+ { RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite",
+ RADIUS_ATTR_HEXDUMP },
+ { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher",
+ RADIUS_ATTR_HEXDUMP },
};
-#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
+#define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
static struct radius_attr_type *radius_get_attr_type(u8 type)
@@ -249,25 +261,17 @@
}
-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;
+ int len;
unsigned char *pos;
+ char buf[1000];
attr = radius_get_attr_type(hdr->type);
- printf(" Attribute %d (%s) length=%d\n",
- hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+ wpa_printf(MSG_INFO, " Attribute %d (%s) length=%d",
+ hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
return;
@@ -277,47 +281,50 @@
switch (attr->data_type) {
case RADIUS_ATTR_TEXT:
- printf(" Value: '");
- for (i = 0; i < len; i++)
- print_char(pos[i]);
- printf("'\n");
+ printf_encode(buf, sizeof(buf), pos, len);
+ wpa_printf(MSG_INFO, " Value: '%s'", buf);
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);
+ wpa_printf(MSG_INFO, " Value: %s",
+ inet_ntoa(addr));
+ } else {
+ wpa_printf(MSG_INFO, " Invalid IP address length %d",
+ 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);
+ wpa_printf(MSG_INFO, " Value: %s",
+ atxt ? atxt : "?");
+ } else {
+ wpa_printf(MSG_INFO, " Invalid IPv6 address length %d",
+ 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");
+ wpa_snprintf_hex(buf, sizeof(buf), pos, len);
+ wpa_printf(MSG_INFO, " Value: %s", buf);
break;
case RADIUS_ATTR_INT32:
if (len == 4)
- printf(" Value: %u\n", WPA_GET_BE32(pos));
+ wpa_printf(MSG_INFO, " Value: %u",
+ WPA_GET_BE32(pos));
else
- printf(" Invalid INT32 length %d\n", len);
+ wpa_printf(MSG_INFO, " Invalid INT32 length %d",
+ len);
break;
default:
@@ -330,9 +337,9 @@
{
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));
+ wpa_printf(MSG_INFO, "RADIUS message: code=%d (%s) identifier=%d length=%d",
+ 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);
@@ -384,7 +391,7 @@
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");
+ wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator");
return -1;
}
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
@@ -473,6 +480,27 @@
}
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_authenticator)
+{
+ const u8 *addr[2];
+ size_t len[2];
+
+ msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+ os_memcpy(msg->hdr->authenticator, req_authenticator, 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)
{
@@ -491,7 +519,7 @@
addr[3] = secret;
len[3] = secret_len;
md5_vector(4, addr, len, hash);
- return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
+ return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
}
@@ -518,7 +546,7 @@
addr[3] = secret;
len[3] = secret_len;
md5_vector(4, addr, len, hash);
- if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
+ if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
return 1;
for (i = 0; i < msg->attr_used; i++) {
@@ -551,7 +579,7 @@
os_memcpy(msg->hdr->authenticator, orig_authenticator,
sizeof(orig_authenticator));
- return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
+ return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0;
}
@@ -585,7 +613,7 @@
struct radius_attr_hdr *attr;
if (data_len > RADIUS_MAX_ATTR_LEN) {
- printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
+ wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
(unsigned long) data_len);
return NULL;
}
@@ -756,8 +784,7 @@
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");
+ wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message");
return 1;
}
attr = tmp;
@@ -765,7 +792,7 @@
}
if (attr == NULL) {
- printf("No Message-Authenticator attribute found\n");
+ wpa_printf(MSG_INFO, "No Message-Authenticator attribute found");
return 1;
}
@@ -785,8 +812,8 @@
sizeof(orig_authenticator));
}
- if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
- printf("Invalid Message-Authenticator!\n");
+ if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "Invalid Message-Authenticator!");
return 1;
}
@@ -802,7 +829,7 @@
u8 hash[MD5_MAC_LEN];
if (sent_msg == NULL) {
- printf("No matching Access-Request message found\n");
+ wpa_printf(MSG_INFO, "No matching Access-Request message found");
return 1;
}
@@ -822,8 +849,8 @@
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");
+ if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "Response Authenticator invalid!");
return 1;
}
@@ -962,7 +989,8 @@
pos = key + 2;
left = len - 2;
if (left % 16) {
- printf("Invalid ms key len %lu\n", (unsigned long) left);
+ wpa_printf(MSG_INFO, "Invalid ms key len %lu",
+ (unsigned long) left);
return NULL;
}
@@ -996,7 +1024,7 @@
}
if (plain[0] == 0 || plain[0] > plen - 1) {
- printf("Failed to decrypt MPPE key\n");
+ wpa_printf(MSG_INFO, "Failed to decrypt MPPE key");
os_free(plain);
return NULL;
}
@@ -1204,30 +1232,55 @@
}
-/* 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)
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+ size_t len)
{
- u8 buf[128];
- size_t padlen, i, buf_len, pos;
+ struct radius_attr_hdr *attr;
+ u8 *buf, *pos;
+ size_t alen;
+
+ alen = 4 + 2 + len;
+ buf = os_malloc(alen);
+ if (buf == NULL)
+ return 0;
+ pos = buf;
+ WPA_PUT_BE32(pos, RADIUS_VENDOR_ID_WFA);
+ pos += 4;
+ *pos++ = subtype;
+ *pos++ = 2 + len;
+ os_memcpy(pos, data, len);
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ buf, alen);
+ os_free(buf);
+ if (attr == NULL)
+ return 0;
+
+ return 1;
+}
+
+
+int radius_user_password_hide(struct radius_msg *msg,
+ const u8 *data, size_t data_len,
+ const u8 *secret, size_t secret_len,
+ u8 *buf, size_t buf_len)
+{
+ size_t padlen, i, pos;
const u8 *addr[2];
size_t len[2];
u8 hash[16];
- if (data_len > 128)
- return NULL;
+ if (data_len + 16 > buf_len)
+ return -1;
os_memcpy(buf, data, data_len);
- buf_len = data_len;
padlen = data_len % 16;
- if (padlen && data_len < sizeof(buf)) {
+ if (padlen && data_len < buf_len) {
padlen = 16 - padlen;
os_memset(buf + data_len, 0, padlen);
- buf_len += padlen;
+ buf_len = data_len + padlen;
+ } else {
+ buf_len = data_len;
}
addr[0] = secret;
@@ -1253,8 +1306,27 @@
pos += 16;
}
+ return buf_len;
+}
+
+
+/* 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];
+ int res;
+
+ res = radius_user_password_hide(msg, data, data_len,
+ secret, secret_len, buf, sizeof(buf));
+ if (res < 0)
+ return NULL;
+
return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
- buf, buf_len);
+ buf, res);
}
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 2031054..62faae1 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
/*
* RADIUS message processing
- * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -90,7 +90,14 @@
RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
- RADIUS_ATTR_ERROR_CAUSE = 101
+ RADIUS_ATTR_ERROR_CAUSE = 101,
+ RADIUS_ATTR_EAP_KEY_NAME = 102,
+ RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177,
+ RADIUS_ATTR_WLAN_HESSID = 181,
+ RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186,
+ RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
+ RADIUS_ATTR_WLAN_AKM_SUITE = 188,
+ RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER = 189,
};
@@ -163,6 +170,18 @@
RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
};
+
+/* Hotspot 2.0 - WFA Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_WFA 40808
+
+enum {
+ RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION = 1,
+ RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2,
+ RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3,
+ RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4,
+ RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5,
+};
+
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
@@ -204,6 +223,9 @@
const struct radius_hdr *req_hdr);
void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
size_t secret_len);
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len,
+ const u8 *req_authenticator);
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,
@@ -234,6 +256,12 @@
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);
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+ size_t len);
+int radius_user_password_hide(struct radius_msg *msg,
+ const u8 *data, size_t data_len,
+ const u8 *secret, size_t secret_len,
+ u8 *buf, size_t buf_len);
struct radius_attr_hdr *
radius_msg_add_attr_user_password(struct radius_msg *msg,
const u8 *data, size_t data_len,
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 425ad93..10056a6 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -122,7 +122,7 @@
/**
* last_attempt - Time of the last transmission attempt
*/
- struct os_time last_attempt;
+ struct os_reltime last_attempt;
/**
* shared_secret - Shared secret with the target RADIUS server
@@ -295,26 +295,34 @@
}
-static void radius_client_handle_send_error(struct radius_client_data *radius,
- int s, RadiusType msg_type)
+/*
+ * Returns >0 if message queue was flushed (i.e., the message that triggered
+ * the error is not available anymore)
+ */
+static int radius_client_handle_send_error(struct radius_client_data *radius,
+ int s, RadiusType msg_type)
{
#ifndef CONFIG_NATIVE_WINDOWS
int _errno = errno;
- perror("send[RADIUS]");
+ wpa_printf(MSG_INFO, "send[RADIUS]: %s", strerror(errno));
if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
- _errno == EBADF) {
+ _errno == EBADF || _errno == ENETUNREACH) {
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)
+ if (msg_type == RADIUS_ACCT ||
+ msg_type == RADIUS_ACCT_INTERIM) {
radius_client_init_acct(radius);
- else
+ return 0;
+ } else {
radius_client_init_auth(radius);
+ return 1;
+ }
}
#endif /* CONFIG_NATIVE_WINDOWS */
+
+ return 0;
}
@@ -351,18 +359,20 @@
HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
radius_msg_get_hdr(entry->msg)->identifier);
- os_get_time(&entry->last_attempt);
+ 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);
+ if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+ if (radius_client_handle_send_error(radius, s, entry->msg_type)
+ > 0)
+ return 0;
+ }
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) {
- printf("Removing un-ACKed RADIUS message due to too many "
- "failed retransmit attempts\n");
+ wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
return 1;
}
@@ -374,21 +384,24 @@
{
struct radius_client_data *radius = eloop_ctx;
struct hostapd_radius_servers *conf = radius->conf;
- struct os_time now;
+ 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];
+ size_t prev_num_msgs;
+ int s;
entry = radius->msgs;
if (!entry)
return;
- os_get_time(&now);
+ os_get_reltime(&now);
first = 0;
prev = NULL;
while (entry) {
+ prev_num_msgs = radius->num_msgs;
if (now.sec >= entry->next_try &&
radius_client_retransmit(radius, entry, now.sec)) {
if (prev)
@@ -403,7 +416,18 @@
continue;
}
- if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
+ if (prev_num_msgs != radius->num_msgs) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Message removed from queue - restart from beginning");
+ entry = radius->msgs;
+ prev = NULL;
+ continue;
+ }
+
+ s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
+ radius->acct_sock;
+ if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
+ (s < 0 && entry->attempts > 0)) {
if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM)
acct_failover++;
@@ -482,7 +506,7 @@
static void radius_client_update_timeout(struct radius_client_data *radius)
{
- struct os_time now;
+ struct os_reltime now;
os_time_t first;
struct radius_msg_list *entry;
@@ -498,7 +522,7 @@
first = entry->next_try;
}
- os_get_time(&now);
+ os_get_reltime(&now);
if (first < now.sec)
first = now.sec;
eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
@@ -526,7 +550,7 @@
entry = os_zalloc(sizeof(*entry));
if (entry == NULL) {
- printf("Failed to add RADIUS packet into retransmit list\n");
+ wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
radius_msg_free(msg);
return;
}
@@ -537,7 +561,7 @@
entry->msg_type = msg_type;
entry->shared_secret = shared_secret;
entry->shared_secret_len = shared_secret_len;
- os_get_time(&entry->last_attempt);
+ 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;
@@ -547,8 +571,7 @@
radius_client_update_timeout(radius);
if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
- printf("Removing the oldest un-ACKed RADIUS packet due to "
- "retransmit list limits.\n");
+ wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
prev = NULL;
while (entry->next) {
prev = entry;
@@ -635,7 +658,7 @@
}
if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
- if (conf->acct_server == NULL) {
+ if (conf->acct_server == NULL || radius->acct_sock < 0) {
hostapd_logger(radius->ctx, NULL,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
@@ -649,7 +672,7 @@
s = radius->acct_sock;
conf->acct_server->requests++;
} else {
- if (conf->auth_server == NULL) {
+ if (conf->auth_server == NULL || radius->auth_sock < 0) {
hostapd_logger(radius->ctx, NULL,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
@@ -694,7 +717,7 @@
struct radius_rx_handler *handlers;
size_t num_handlers, i;
struct radius_msg_list *req, *prev_req;
- struct os_time now;
+ struct os_reltime now;
struct hostapd_radius_server *rconf;
int invalid_authenticator = 0;
@@ -710,21 +733,20 @@
len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
if (len < 0) {
- perror("recv[RADIUS]");
+ 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)) {
- printf("Possibly too long UDP frame for our buffer - "
- "dropping it\n");
+ 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) {
- printf("Parsing incoming RADIUS frame failed\n");
+ wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
rconf->malformed_responses++;
return;
}
@@ -775,7 +797,7 @@
goto fail;
}
- os_get_time(&now);
+ 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,
@@ -954,9 +976,10 @@
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) {
+ if (oserv && oserv != nserv &&
+ (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
@@ -974,7 +997,8 @@
}
/* Reset retry counters for the new server */
- for (entry = radius->msgs; entry; entry = entry->next) {
+ for (entry = radius->msgs; oserv && oserv != nserv && entry;
+ entry = entry->next) {
if ((auth && entry->msg_type != RADIUS_AUTH) ||
(!auth && entry->msg_type != RADIUS_ACCT))
continue;
@@ -1041,13 +1065,14 @@
}
if (bind(sel_sock, cl_addr, claddrlen) < 0) {
- perror("bind[radius]");
+ wpa_printf(MSG_INFO, "bind[radius]: %s",
+ strerror(errno));
return -1;
}
}
if (connect(sel_sock, addr, addrlen) < 0) {
- perror("connect[radius]");
+ wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
return -1;
}
@@ -1123,21 +1148,62 @@
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));
+ wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
+ strerror(errno));
#endif
return r;
}
+static void radius_close_auth_sockets(struct radius_client_data *radius)
+{
+ radius->auth_sock = -1;
+
+ if (radius->auth_serv_sock >= 0) {
+ eloop_unregister_read_sock(radius->auth_serv_sock);
+ close(radius->auth_serv_sock);
+ radius->auth_serv_sock = -1;
+ }
+#ifdef CONFIG_IPV6
+ if (radius->auth_serv_sock6 >= 0) {
+ eloop_unregister_read_sock(radius->auth_serv_sock6);
+ close(radius->auth_serv_sock6);
+ radius->auth_serv_sock6 = -1;
+ }
+#endif /* CONFIG_IPV6 */
+}
+
+
+static void radius_close_acct_sockets(struct radius_client_data *radius)
+{
+ radius->acct_sock = -1;
+
+ if (radius->acct_serv_sock >= 0) {
+ eloop_unregister_read_sock(radius->acct_serv_sock);
+ close(radius->acct_serv_sock);
+ radius->acct_serv_sock = -1;
+ }
+#ifdef CONFIG_IPV6
+ if (radius->acct_serv_sock6 >= 0) {
+ eloop_unregister_read_sock(radius->acct_serv_sock6);
+ close(radius->acct_serv_sock6);
+ radius->acct_serv_sock6 = -1;
+ }
+#endif /* CONFIG_IPV6 */
+}
+
+
static int radius_client_init_auth(struct radius_client_data *radius)
{
struct hostapd_radius_servers *conf = radius->conf;
int ok = 0;
+ radius_close_auth_sockets(radius);
+
radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (radius->auth_serv_sock < 0)
- perror("socket[PF_INET,SOCK_DGRAM]");
+ wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+ strerror(errno));
else {
radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
ok++;
@@ -1146,7 +1212,8 @@
#ifdef CONFIG_IPV6
radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
if (radius->auth_serv_sock6 < 0)
- perror("socket[PF_INET6,SOCK_DGRAM]");
+ wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+ strerror(errno));
else
ok++;
#endif /* CONFIG_IPV6 */
@@ -1162,8 +1229,8 @@
eloop_register_read_sock(radius->auth_serv_sock,
radius_client_receive, radius,
(void *) RADIUS_AUTH)) {
- printf("Could not register read socket for authentication "
- "server\n");
+ wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+ radius_close_auth_sockets(radius);
return -1;
}
@@ -1172,8 +1239,8 @@
eloop_register_read_sock(radius->auth_serv_sock6,
radius_client_receive, radius,
(void *) RADIUS_AUTH)) {
- printf("Could not register read socket for authentication "
- "server\n");
+ wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+ radius_close_auth_sockets(radius);
return -1;
}
#endif /* CONFIG_IPV6 */
@@ -1187,9 +1254,12 @@
struct hostapd_radius_servers *conf = radius->conf;
int ok = 0;
+ radius_close_acct_sockets(radius);
+
radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (radius->acct_serv_sock < 0)
- perror("socket[PF_INET,SOCK_DGRAM]");
+ wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+ strerror(errno));
else {
radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
ok++;
@@ -1198,7 +1268,8 @@
#ifdef CONFIG_IPV6
radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
if (radius->acct_serv_sock6 < 0)
- perror("socket[PF_INET6,SOCK_DGRAM]");
+ wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+ strerror(errno));
else
ok++;
#endif /* CONFIG_IPV6 */
@@ -1214,8 +1285,8 @@
eloop_register_read_sock(radius->acct_serv_sock,
radius_client_receive, radius,
(void *) RADIUS_ACCT)) {
- printf("Could not register read socket for accounting "
- "server\n");
+ wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+ radius_close_acct_sockets(radius);
return -1;
}
@@ -1224,8 +1295,8 @@
eloop_register_read_sock(radius->acct_serv_sock6,
radius_client_receive, radius,
(void *) RADIUS_ACCT)) {
- printf("Could not register read socket for accounting "
- "server\n");
+ wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+ radius_close_acct_sockets(radius);
return -1;
}
#endif /* CONFIG_IPV6 */
@@ -1287,16 +1358,8 @@
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 */
+ radius_close_auth_sockets(radius);
+ radius_close_acct_sockets(radius);
eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index bded965..9655f4c 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -1,6 +1,6 @@
/*
* RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
- * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -16,9 +16,6 @@
#include "radius_das.h"
-extern int wpa_debug_level;
-
-
struct radius_das_data {
int sock;
u8 *shared_secret;
@@ -41,11 +38,16 @@
struct radius_msg *reply;
u8 allowed[] = {
RADIUS_ATTR_USER_NAME,
+ RADIUS_ATTR_NAS_IP_ADDRESS,
RADIUS_ATTR_CALLING_STATION_ID,
+ RADIUS_ATTR_NAS_IDENTIFIER,
RADIUS_ATTR_ACCT_SESSION_ID,
RADIUS_ATTR_EVENT_TIMESTAMP,
RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+#ifdef CONFIG_IPV6
+ RADIUS_ATTR_NAS_IPV6_ADDRESS,
+#endif /* CONFIG_IPV6 */
0
};
int error = 405;
@@ -70,6 +72,36 @@
os_memset(&attrs, 0, sizeof(attrs));
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ &buf, &len, NULL) == 0) {
+ if (len != 4) {
+ wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
+ abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.nas_ip_addr = buf;
+ }
+
+#ifdef CONFIG_IPV6
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+ &buf, &len, NULL) == 0) {
+ if (len != 16) {
+ wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
+ abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.nas_ipv6_addr = buf;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ &buf, &len, NULL) == 0) {
+ attrs.nas_identifier = buf;
+ attrs.nas_identifier_len = len;
+ }
+
if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
&buf, &len, NULL) == 0) {
if (len >= sizeof(tmp))
@@ -200,7 +232,8 @@
(u8 *) &val, 4);
if (res == 4) {
u32 timestamp = ntohl(val);
- if (abs(now.sec - timestamp) > das->time_window) {
+ 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",
@@ -284,7 +317,7 @@
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
- perror("socket");
+ wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
return -1;
}
@@ -292,7 +325,7 @@
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- perror("bind");
+ wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
close(s);
return -1;
}
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
index 738b18b..e3ed540 100644
--- a/src/radius/radius_das.h
+++ b/src/radius/radius_das.h
@@ -18,6 +18,13 @@
};
struct radius_das_attrs {
+ /* NAS identification attributes */
+ const u8 *nas_ip_addr;
+ const u8 *nas_identifier;
+ size_t nas_identifier_len;
+ const u8 *nas_ipv6_addr;
+
+ /* Session identification attributes */
const u8 *sta_addr;
const u8 *user_name;
size_t user_name_len;
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index 0144c9f..24348a3 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -1,6 +1,6 @@
/*
* RADIUS authentication server
- * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2009, 2011-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -8,11 +8,16 @@
#include "includes.h"
#include <net/if.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
#include "common.h"
#include "radius.h"
#include "eloop.h"
#include "eap_server/eap.h"
+#include "ap/ap_config.h"
+#include "crypto/tls.h"
#include "radius_server.h"
/**
@@ -49,6 +54,13 @@
u32 bad_authenticators;
u32 packets_dropped;
u32 unknown_types;
+
+ u32 acct_requests;
+ u32 invalid_acct_requests;
+ u32 acct_responses;
+ u32 malformed_acct_requests;
+ u32 acct_bad_authenticators;
+ u32 unknown_acct_types;
};
/**
@@ -61,6 +73,8 @@
unsigned int sess_id;
struct eap_sm *eap;
struct eap_eapol_interface *eap_if;
+ char *username; /* from User-Name attribute */
+ char *nas_ip;
struct radius_msg *last_msg;
char *last_from_addr;
@@ -70,6 +84,11 @@
u8 last_identifier;
struct radius_msg *last_reply;
u8 last_authenticator[16];
+
+ unsigned int remediation:1;
+ unsigned int macacl:1;
+
+ struct hostapd_radius_attr *accept_attr;
};
/**
@@ -99,6 +118,11 @@
int auth_sock;
/**
+ * acct_sock - Socket for RADIUS accounting messages
+ */
+ int acct_sock;
+
+ /**
* clients - List of authorized RADIUS clients
*/
struct radius_client *clients;
@@ -244,7 +268,7 @@
/**
* start_time - Timestamp of server start
*/
- struct os_time start_time;
+ struct os_reltime start_time;
/**
* counters - Statistics counters for server operations
@@ -295,11 +319,16 @@
#ifdef CONFIG_RADIUS_TEST
char *dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */
+
+ char *subscr_remediation_url;
+ u8 subscr_remediation_method;
+
+#ifdef CONFIG_SQLITE
+ sqlite3 *db;
+#endif /* CONFIG_SQLITE */
};
-extern int wpa_debug_level;
-
#define RADIUS_DEBUG(args...) \
wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
#define RADIUS_ERROR(args...) \
@@ -314,6 +343,52 @@
static void radius_server_session_remove_timeout(void *eloop_ctx,
void *timeout_ctx);
+void srv_log(struct radius_session *sess, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+void srv_log(struct radius_session *sess, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ va_start(ap, fmt);
+ vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+
+ RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf);
+
+#ifdef CONFIG_SQLITE
+ if (sess->server->db) {
+ char *sql;
+ sql = sqlite3_mprintf("INSERT INTO authlog"
+ "(timestamp,session,nas_ip,username,note)"
+ " VALUES ("
+ "strftime('%%Y-%%m-%%d %%H:%%M:%%f',"
+ "'now'),%u,%Q,%Q,%Q)",
+ sess->sess_id, sess->nas_ip,
+ sess->username, buf);
+ if (sql) {
+ if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
+ NULL) != SQLITE_OK) {
+ RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s",
+ sqlite3_errmsg(sess->server->db));
+ }
+ sqlite3_free(sql);
+ }
+ }
+#endif /* CONFIG_SQLITE */
+
+ os_free(buf);
+}
+
static struct radius_client *
radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
@@ -379,6 +454,8 @@
radius_msg_free(sess->last_msg);
os_free(sess->last_from_addr);
radius_msg_free(sess->last_reply);
+ os_free(sess->username);
+ os_free(sess->nas_ip);
os_free(sess);
data->num_sess--;
}
@@ -458,47 +535,125 @@
}
+#ifdef CONFIG_TESTING_OPTIONS
+static void radius_server_testing_options_tls(struct radius_session *sess,
+ const char *tls,
+ struct eap_config *eap_conf)
+{
+ int test = atoi(tls);
+
+ switch (test) {
+ case 1:
+ srv_log(sess, "TLS test - break VerifyData");
+ eap_conf->tls_test_flags = TLS_BREAK_VERIFY_DATA;
+ break;
+ case 2:
+ srv_log(sess, "TLS test - break ServerKeyExchange ServerParams hash");
+ eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_HASH;
+ break;
+ case 3:
+ srv_log(sess, "TLS test - break ServerKeyExchange ServerParams Signature");
+ eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_SIGNATURE;
+ break;
+ case 4:
+ srv_log(sess, "TLS test - RSA-DHE using a short 511-bit prime");
+ eap_conf->tls_test_flags = TLS_DHE_PRIME_511B;
+ break;
+ case 5:
+ srv_log(sess, "TLS test - RSA-DHE using a short 767-bit prime");
+ eap_conf->tls_test_flags = TLS_DHE_PRIME_767B;
+ break;
+ case 6:
+ srv_log(sess, "TLS test - RSA-DHE using a bogus 15 \"prime\"");
+ eap_conf->tls_test_flags = TLS_DHE_PRIME_15;
+ break;
+ case 7:
+ srv_log(sess, "TLS test - RSA-DHE using a short 58-bit prime in long container");
+ eap_conf->tls_test_flags = TLS_DHE_PRIME_58B;
+ break;
+ case 8:
+ srv_log(sess, "TLS test - RSA-DHE using a non-prime");
+ eap_conf->tls_test_flags = TLS_DHE_NON_PRIME;
+ break;
+ default:
+ srv_log(sess, "Unrecognized TLS test");
+ break;
+ }
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+static void radius_server_testing_options(struct radius_session *sess,
+ struct eap_config *eap_conf)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ const char *pos;
+
+ pos = os_strstr(sess->username, "@test-");
+ if (pos == NULL)
+ return;
+ pos += 6;
+ if (os_strncmp(pos, "tls-", 4) == 0)
+ radius_server_testing_options_tls(sess, pos + 4, eap_conf);
+ else
+ srv_log(sess, "Unrecognized test: %s", pos);
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
static struct radius_session *
radius_server_get_new_session(struct radius_server_data *data,
struct radius_client *client,
- struct radius_msg *msg)
+ struct radius_msg *msg, const char *from_addr)
{
u8 *user;
size_t user_len;
int res;
struct radius_session *sess;
struct eap_config eap_conf;
+ struct eap_user tmp;
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) {
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user,
+ &user_len, NULL) < 0) {
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);
+ os_memset(&tmp, 0, sizeof(tmp));
+ res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp);
+ bin_clear_free(tmp.password, tmp.password_len);
- 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 {
+ if (res != 0) {
RADIUS_DEBUG("User-Name not found from user database");
return NULL;
}
+ 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;
+ }
+ sess->accept_attr = tmp.accept_attr;
+ sess->macacl = tmp.macacl;
+
+ sess->username = os_malloc(user_len * 4 + 1);
+ if (sess->username == NULL) {
+ radius_server_session_free(data, sess);
+ return NULL;
+ }
+ printf_encode(sess->username, user_len * 4 + 1, user, user_len);
+
+ sess->nas_ip = os_strdup(from_addr);
+ if (sess->nas_ip == NULL) {
+ radius_server_session_free(data, sess);
+ return NULL;
+ }
+
+ srv_log(sess, "New session created");
+
os_memset(&eap_conf, 0, sizeof(eap_conf));
eap_conf.ssl_ctx = data->ssl_ctx;
eap_conf.msg_ctx = data->msg_ctx;
@@ -518,6 +673,7 @@
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);
+ radius_server_testing_options(sess, &eap_conf);
sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
&eap_conf);
if (sess->eap == NULL) {
@@ -612,12 +768,134 @@
}
}
+#ifdef CONFIG_HS20
+ if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation &&
+ data->subscr_remediation_url) {
+ u8 *buf;
+ size_t url_len = os_strlen(data->subscr_remediation_url);
+ buf = os_malloc(1 + url_len);
+ if (buf == NULL) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+ buf[0] = data->subscr_remediation_method;
+ os_memcpy(&buf[1], data->subscr_remediation_url, url_len);
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
+ buf, 1 + url_len)) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
+ }
+ os_free(buf);
+ } else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) {
+ u8 buf[1];
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
+ buf, 0)) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
+ }
+ }
+#endif /* CONFIG_HS20 */
+
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 (code == RADIUS_CODE_ACCESS_ACCEPT) {
+ struct hostapd_radius_attr *attr;
+ for (attr = sess->accept_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");
+ 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 struct radius_msg *
+radius_server_macacl(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_session *sess,
+ struct radius_msg *request)
+{
+ struct radius_msg *msg;
+ int code;
+ struct radius_hdr *hdr = radius_msg_get_hdr(request);
+ u8 *pw;
+ size_t pw_len;
+
+ code = RADIUS_CODE_ACCESS_ACCEPT;
+
+ if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw,
+ &pw_len, NULL) < 0) {
+ RADIUS_DEBUG("Could not get User-Password");
+ code = RADIUS_CODE_ACCESS_REJECT;
+ } else {
+ int res;
+ struct eap_user tmp;
+
+ os_memset(&tmp, 0, sizeof(tmp));
+ res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username,
+ os_strlen(sess->username), 0, &tmp);
+ if (res || !tmp.macacl || tmp.password == NULL) {
+ RADIUS_DEBUG("No MAC ACL user entry");
+ bin_clear_free(tmp.password, tmp.password_len);
+ code = RADIUS_CODE_ACCESS_REJECT;
+ } else {
+ u8 buf[128];
+ res = radius_user_password_hide(
+ request, tmp.password, tmp.password_len,
+ (u8 *) client->shared_secret,
+ client->shared_secret_len,
+ buf, sizeof(buf));
+ bin_clear_free(tmp.password, tmp.password_len);
+
+ if (res < 0 || pw_len != (size_t) res ||
+ os_memcmp_const(pw, buf, res) != 0) {
+ RADIUS_DEBUG("Incorrect User-Password");
+ code = RADIUS_CODE_ACCESS_REJECT;
+ }
+ }
+ }
+
+ msg = radius_msg_new(code, hdr->identifier);
+ if (msg == NULL) {
+ RADIUS_DEBUG("Failed to allocate reply message");
+ return NULL;
+ }
+
+ 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 (code == RADIUS_CODE_ACCESS_ACCEPT) {
+ struct hostapd_radius_attr *attr;
+ for (attr = sess->accept_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");
+ radius_msg_free(msg);
+ return NULL;
+ }
+ }
+ }
+
if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
client->shared_secret_len,
hdr->authenticator) < 0) {
@@ -679,7 +957,7 @@
buf = radius_msg_get_buf(msg);
if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
(struct sockaddr *) from, sizeof(*from)) < 0) {
- perror("sendto[RADIUS SRV]");
+ wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno));
ret = -1;
}
@@ -726,7 +1004,8 @@
from_addr, from_port);
return -1;
} else {
- sess = radius_server_get_new_session(data, client, msg);
+ sess = radius_server_get_new_session(data, client, msg,
+ from_addr);
if (sess == NULL) {
RADIUS_DEBUG("Could not create a new session");
radius_server_reject(data, client, msg, from, fromlen,
@@ -750,7 +1029,8 @@
wpabuf_len(buf), 0,
(struct sockaddr *) from, fromlen);
if (res < 0) {
- perror("sendto[RADIUS SRV]");
+ wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+ strerror(errno));
}
return 0;
}
@@ -761,6 +1041,12 @@
}
eap = radius_msg_get_eap(msg);
+ if (eap == NULL && sess->macacl) {
+ reply = radius_server_macacl(data, client, sess, msg);
+ if (reply == NULL)
+ return -1;
+ goto send_reply;
+ }
if (eap == NULL) {
RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
from_addr);
@@ -811,9 +1097,14 @@
if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
is_complete = 1;
+ if (sess->eap_if->eapFail)
+ srv_log(sess, "EAP authentication failed");
+ else if (sess->eap_if->eapSuccess)
+ srv_log(sess, "EAP authentication succeeded");
reply = radius_server_encapsulate_eap(data, client, sess, msg);
+send_reply:
if (reply) {
struct wpabuf *buf;
struct radius_hdr *hdr;
@@ -825,10 +1116,12 @@
switch (radius_msg_get_hdr(reply)->code) {
case RADIUS_CODE_ACCESS_ACCEPT:
+ srv_log(sess, "Sending Access-Accept");
data->counters.access_accepts++;
client->counters.access_accepts++;
break;
case RADIUS_CODE_ACCESS_REJECT:
+ srv_log(sess, "Sending Access-Reject");
data->counters.access_rejects++;
client->counters.access_rejects++;
break;
@@ -842,7 +1135,8 @@
wpabuf_len(buf), 0,
(struct sockaddr *) from, fromlen);
if (res < 0) {
- perror("sendto[RADIUS SRV]");
+ wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+ strerror(errno));
}
radius_msg_free(sess->last_reply);
sess->last_reply = reply;
@@ -897,7 +1191,8 @@
len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
(struct sockaddr *) &from.ss, &fromlen);
if (len < 0) {
- perror("recvfrom[radius_server]");
+ wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
+ strerror(errno));
goto fail;
}
@@ -978,6 +1273,140 @@
}
+static void radius_server_receive_acct(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, res;
+ struct radius_client *client = NULL;
+ struct radius_msg *msg = NULL, *resp = NULL;
+ char abuf[50];
+ int from_port = 0;
+ struct radius_hdr *hdr;
+ struct wpabuf *rbuf;
+
+ 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_acct_requests++;
+ goto fail;
+ }
+
+ msg = radius_msg_parse(buf, len);
+ if (msg == NULL) {
+ RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
+ data->counters.malformed_acct_requests++;
+ client->counters.malformed_acct_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_ACCOUNTING_REQUEST) {
+ RADIUS_DEBUG("Unexpected RADIUS code %d",
+ radius_msg_get_hdr(msg)->code);
+ data->counters.unknown_acct_types++;
+ client->counters.unknown_acct_types++;
+ goto fail;
+ }
+
+ data->counters.acct_requests++;
+ client->counters.acct_requests++;
+
+ if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len)) {
+ RADIUS_DEBUG("Invalid Authenticator from %s", abuf);
+ data->counters.acct_bad_authenticators++;
+ client->counters.acct_bad_authenticators++;
+ goto fail;
+ }
+
+ /* TODO: Write accounting information to a file or database */
+
+ hdr = radius_msg_get_hdr(msg);
+
+ resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier);
+ if (resp == NULL)
+ goto fail;
+
+ radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
+ client->shared_secret_len,
+ hdr->authenticator);
+
+ RADIUS_DEBUG("Reply to %s:%d", abuf, from_port);
+ if (wpa_debug_level <= MSG_MSGDUMP) {
+ radius_msg_dump(resp);
+ }
+ rbuf = radius_msg_get_buf(resp);
+ data->counters.acct_responses++;
+ client->counters.acct_responses++;
+ res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0,
+ (struct sockaddr *) &from.ss, fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+ strerror(errno));
+ }
+
+fail:
+ radius_msg_free(resp);
+ radius_msg_free(msg);
+ os_free(buf);
+}
+
+
static int radius_server_disable_pmtu_discovery(int s)
{
int r = -1;
@@ -1001,7 +1430,7 @@
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
- perror("socket");
+ wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno));
return -1;
}
@@ -1011,7 +1440,7 @@
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- perror("bind");
+ wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
close(s);
return -1;
}
@@ -1028,7 +1457,8 @@
s = socket(PF_INET6, SOCK_DGRAM, 0);
if (s < 0) {
- perror("socket[IPv6]");
+ wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s",
+ strerror(errno));
return -1;
}
@@ -1037,7 +1467,7 @@
os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr.sin6_port = htons(port);
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- perror("bind");
+ wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
close(s);
return -1;
}
@@ -1190,8 +1620,8 @@
break;
}
entry->shared_secret_len = os_strlen(entry->shared_secret);
- entry->addr.s_addr = addr.s_addr;
if (!ipv6) {
+ entry->addr.s_addr = addr.s_addr;
val = 0;
for (i = 0; i < mask; i++)
val |= 1 << (31 - i);
@@ -1248,8 +1678,7 @@
#ifndef CONFIG_IPV6
if (conf->ipv6) {
- fprintf(stderr, "RADIUS server compiled without IPv6 "
- "support.\n");
+ wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support");
return NULL;
}
#endif /* CONFIG_IPV6 */
@@ -1258,7 +1687,7 @@
if (data == NULL)
return NULL;
- os_get_time(&data->start_time);
+ 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;
@@ -1297,6 +1726,23 @@
}
}
+ if (conf->subscr_remediation_url) {
+ data->subscr_remediation_url =
+ os_strdup(conf->subscr_remediation_url);
+ }
+ data->subscr_remediation_method = conf->subscr_remediation_method;
+
+#ifdef CONFIG_SQLITE
+ if (conf->sqlite_file) {
+ if (sqlite3_open(conf->sqlite_file, &data->db)) {
+ RADIUS_ERROR("Could not open SQLite file '%s'",
+ conf->sqlite_file);
+ radius_server_deinit(data);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_SQLITE */
+
#ifdef CONFIG_RADIUS_TEST
if (conf->dump_msk_file)
data->dump_msk_file = os_strdup(conf->dump_msk_file);
@@ -1305,7 +1751,7 @@
data->clients = radius_server_read_clients(conf->client_file,
conf->ipv6);
if (data->clients == NULL) {
- printf("No RADIUS clients configured.\n");
+ wpa_printf(MSG_ERROR, "No RADIUS clients configured");
radius_server_deinit(data);
return NULL;
}
@@ -1317,8 +1763,7 @@
#endif /* CONFIG_IPV6 */
data->auth_sock = radius_server_open_socket(conf->auth_port);
if (data->auth_sock < 0) {
- printf("Failed to open UDP socket for RADIUS authentication "
- "server\n");
+ wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
radius_server_deinit(data);
return NULL;
}
@@ -1329,6 +1774,29 @@
return NULL;
}
+ if (conf->acct_port) {
+#ifdef CONFIG_IPV6
+ if (conf->ipv6)
+ data->acct_sock = radius_server_open_socket6(
+ conf->acct_port);
+ else
+#endif /* CONFIG_IPV6 */
+ data->acct_sock = radius_server_open_socket(conf->acct_port);
+ if (data->acct_sock < 0) {
+ wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server");
+ radius_server_deinit(data);
+ return NULL;
+ }
+ if (eloop_register_read_sock(data->acct_sock,
+ radius_server_receive_acct,
+ data, NULL)) {
+ radius_server_deinit(data);
+ return NULL;
+ }
+ } else {
+ data->acct_sock = -1;
+ }
+
return data;
}
@@ -1347,6 +1815,11 @@
close(data->auth_sock);
}
+ if (data->acct_sock >= 0) {
+ eloop_unregister_read_sock(data->acct_sock);
+ close(data->acct_sock);
+ }
+
radius_server_free_clients(data, data->clients);
os_free(data->pac_opaque_encr_key);
@@ -1356,6 +1829,13 @@
#ifdef CONFIG_RADIUS_TEST
os_free(data->dump_msk_file);
#endif /* CONFIG_RADIUS_TEST */
+ os_free(data->subscr_remediation_url);
+
+#ifdef CONFIG_SQLITE
+ if (data->db)
+ sqlite3_close(data->db);
+#endif /* CONFIG_SQLITE */
+
os_free(data);
}
@@ -1373,7 +1853,7 @@
int ret, uptime;
unsigned int idx;
char *end, *pos;
- struct os_time now;
+ struct os_reltime now;
struct radius_client *cli;
/* RFC 2619 - RADIUS Authentication Server MIB */
@@ -1384,7 +1864,7 @@
pos = buf;
end = buf + buflen;
- os_get_time(&now);
+ 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,
@@ -1410,7 +1890,13 @@
"radiusAuthServTotalMalformedAccessRequests=%u\n"
"radiusAuthServTotalBadAuthenticators=%u\n"
"radiusAuthServTotalPacketsDropped=%u\n"
- "radiusAuthServTotalUnknownTypes=%u\n",
+ "radiusAuthServTotalUnknownTypes=%u\n"
+ "radiusAccServTotalRequests=%u\n"
+ "radiusAccServTotalInvalidRequests=%u\n"
+ "radiusAccServTotalResponses=%u\n"
+ "radiusAccServTotalMalformedRequests=%u\n"
+ "radiusAccServTotalBadAuthenticators=%u\n"
+ "radiusAccServTotalUnknownTypes=%u\n",
data->counters.access_requests,
data->counters.invalid_requests,
data->counters.dup_access_requests,
@@ -1420,7 +1906,13 @@
data->counters.malformed_access_requests,
data->counters.bad_authenticators,
data->counters.packets_dropped,
- data->counters.unknown_types);
+ data->counters.unknown_types,
+ data->counters.acct_requests,
+ data->counters.invalid_acct_requests,
+ data->counters.acct_responses,
+ data->counters.malformed_acct_requests,
+ data->counters.acct_bad_authenticators,
+ data->counters.unknown_acct_types);
if (ret < 0 || ret >= end - pos) {
*pos = '\0';
return pos - buf;
@@ -1455,7 +1947,13 @@
"radiusAuthServMalformedAccessRequests=%u\n"
"radiusAuthServBadAuthenticators=%u\n"
"radiusAuthServPacketsDropped=%u\n"
- "radiusAuthServUnknownTypes=%u\n",
+ "radiusAuthServUnknownTypes=%u\n"
+ "radiusAccServTotalRequests=%u\n"
+ "radiusAccServTotalInvalidRequests=%u\n"
+ "radiusAccServTotalResponses=%u\n"
+ "radiusAccServTotalMalformedRequests=%u\n"
+ "radiusAccServTotalBadAuthenticators=%u\n"
+ "radiusAccServTotalUnknownTypes=%u\n",
idx,
abuf, mbuf,
cli->counters.access_requests,
@@ -1466,7 +1964,13 @@
cli->counters.malformed_access_requests,
cli->counters.bad_authenticators,
cli->counters.packets_dropped,
- cli->counters.unknown_types);
+ cli->counters.unknown_types,
+ cli->counters.acct_requests,
+ cli->counters.invalid_acct_requests,
+ cli->counters.acct_responses,
+ cli->counters.malformed_acct_requests,
+ cli->counters.acct_bad_authenticators,
+ cli->counters.unknown_acct_types);
if (ret < 0 || ret >= end - pos) {
*pos = '\0';
return pos - buf;
@@ -1484,9 +1988,16 @@
{
struct radius_session *sess = ctx;
struct radius_server_data *data = sess->server;
+ int ret;
- return data->get_eap_user(data->conf_ctx, identity, identity_len,
- phase2, user);
+ ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
+ phase2, user);
+ if (ret == 0 && user) {
+ sess->accept_attr = user->accept_attr;
+ sess->remediation = user->remediation;
+ sess->macacl = user->macacl;
+ }
+ return ret;
}
@@ -1499,10 +2010,18 @@
}
+static void radius_server_log_msg(void *ctx, const char *msg)
+{
+ struct radius_session *sess = ctx;
+ srv_log(sess, "EAP: %s", msg);
+}
+
+
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,
+ .log_msg = radius_server_log_msg,
};
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 284bd59..46ac312 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -22,6 +22,11 @@
int auth_port;
/**
+ * acct_port - UDP port to listen to as an accounting server
+ */
+ int acct_port;
+
+ /**
* client_file - RADIUS client configuration file
*
* This file contains the RADIUS clients and the shared secret to be
@@ -35,6 +40,11 @@
char *client_file;
/**
+ * sqlite_file - SQLite database for storing debug log information
+ */
+ const char *sqlite_file;
+
+ /**
* conf_ctx - Context pointer for callbacks
*
* This is used as the ctx argument in get_eap_user() calls.
@@ -204,6 +214,9 @@
#ifdef CONFIG_RADIUS_TEST
const char *dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */
+
+ char *subscr_remediation_url;
+ u8 subscr_remediation_method;
};
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index 9c41962..adfd3df 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index 789ac25..aab8b7e 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -516,7 +516,6 @@
struct wpa_peerkey *peerkey;
struct wpa_eapol_ie_parse kde;
u32 lifetime;
- struct os_time now;
if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
@@ -568,10 +567,8 @@
lifetime = WPA_GET_BE32(kde.lifetime);
wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
if (lifetime > 1000000000)
- lifetime = 1000000000; /* avoid overflowing expiration time */
+ lifetime = 1000000000; /* avoid overflowing eloop time */
peerkey->lifetime = lifetime;
- os_get_time(&now);
- peerkey->expiration = now.sec + lifetime;
eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
sm, peerkey);
@@ -656,11 +653,11 @@
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)
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
{
struct wpa_eapol_ie_parse ie;
- const u8 *kde;
- size_t len, kde_buf_len;
+ size_t kde_buf_len;
struct wpa_ptk *stk;
u8 buf[8], *kde_buf, *pos;
be32 lifetime;
@@ -671,14 +668,13 @@
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_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 ||
+ ie.pmkid == NULL) {
wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
return;
}
- if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+ if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
ie.pmkid, PMKID_LEN);
return;
@@ -736,7 +732,6 @@
struct wpa_eapol_ie_parse *kde)
{
u32 lifetime;
- struct os_time now;
if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
return;
@@ -755,8 +750,6 @@
lifetime, peerkey->lifetime);
peerkey->lifetime = lifetime;
- os_get_time(&now);
- peerkey->expiration = now.sec + lifetime;
eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
sm, peerkey);
@@ -766,11 +759,10 @@
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)
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
{
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);
@@ -779,16 +771,14 @@
/* 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 ||
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 ||
kde.pmkid == NULL || kde.rsn_ie == NULL) {
wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
return;
}
- if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+ if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
kde.pmkid, PMKID_LEN);
return;
@@ -815,11 +805,11 @@
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)
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
{
struct wpa_eapol_ie_parse kde;
- const u8 *keydata;
- size_t len, key_len;
+ size_t key_len;
const u8 *_key;
u8 key_buf[32], rsc[6];
@@ -830,10 +820,8 @@
/* 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_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
"STK 3/4");
return;
@@ -864,7 +852,7 @@
if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
WPA_GET_BE16(key->key_info),
- NULL, 0, &peerkey->stk))
+ &peerkey->stk))
return;
_key = (u8 *) peerkey->stk.tk1;
@@ -941,7 +929,7 @@
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) {
+ if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
"when using TSTK - ignoring TSTK");
} else {
@@ -957,7 +945,7 @@
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) {
+ if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
"- dropping packet");
return -1;
@@ -1116,28 +1104,32 @@
while (peerkey) {
prev = peerkey;
peerkey = peerkey->next;
- os_free(prev);
+ 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)
+ struct wpa_eapol_key *key, u16 key_info, u16 ver,
+ const u8 *key_data, size_t key_data_len)
{
if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
(WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
/* 3/4 STK 4-Way Handshake */
- wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver);
+ wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver,
+ key_data, key_data_len);
} else if (key_info & WPA_KEY_INFO_ACK) {
/* 1/4 STK 4-Way Handshake */
- wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver);
+ wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver,
+ key_data, key_data_len);
} else if (key_info & WPA_KEY_INFO_SECURE) {
/* 4/4 STK 4-Way Handshake */
wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
} else {
/* 2/4 STK 4-Way Handshake */
- wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver);
+ wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver,
+ key_data, key_data_len);
}
}
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
index b8845f7..4c17eae 100644
--- a/src/rsn_supp/peerkey.h
+++ b/src/rsn_supp/peerkey.h
@@ -24,7 +24,6 @@
int smk_complete;
u8 smkid[PMKID_LEN];
u32 lifetime;
- os_time_t expiration;
int cipher; /* Selected cipher (WPA_CIPHER_*) */
u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
int replay_counter_set;
@@ -42,7 +41,8 @@
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);
+ struct wpa_eapol_key *key, u16 key_info, u16 ver,
+ const u8 *key_data, size_t key_data_len);
void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
struct wpa_eapol_key *key, size_t extra_len,
u16 key_info, u16 ver);
@@ -61,7 +61,8 @@
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)
+ struct wpa_eapol_key *key, u16 key_info, u16 ver,
+ const u8 *key_data, size_t key_data_len)
{
}
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 33fa1a2..b5a87fc 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -53,9 +53,9 @@
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&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;
@@ -80,13 +80,13 @@
{
int sec;
struct rsn_pmksa_cache_entry *entry;
- struct os_time now;
+ 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_time(&now);
+ os_get_reltime(&now);
sec = pmksa->pmksa->expiration - now.sec;
if (sec < 0)
sec = 0;
@@ -125,7 +125,7 @@
const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
{
struct rsn_pmksa_cache_entry *entry, *pos, *prev;
- struct os_time now;
+ struct os_reltime now;
if (pmk_len > PMK_LEN)
return NULL;
@@ -137,7 +137,7 @@
entry->pmk_len = pmk_len;
rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
wpa_key_mgmt_sha256(akmp));
- os_get_time(&now);
+ os_get_reltime(&now);
entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
@@ -152,9 +152,9 @@
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) {
+ os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 &&
+ os_memcmp_const(pos->pmkid, entry->pmkid,
+ PMKID_LEN) == 0) {
wpa_printf(MSG_DEBUG, "WPA: reusing previous "
"PMKSA entry");
os_free(entry);
@@ -466,9 +466,9 @@
int i, ret;
char *pos = buf;
struct rsn_pmksa_cache_entry *entry;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
ret = os_snprintf(pos, buf + len - pos,
"Index / AA / PMKID / expiration (in seconds) / "
"opportunistic\n");
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index c51620e..915f85e 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -70,13 +70,14 @@
}
-static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
+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 (success) {
+ if (result == EAPOL_SUPP_RESULT_SUCCESS) {
int res, pmk_len;
pmk_len = PMK_LEN;
res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
@@ -100,13 +101,14 @@
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: failed to get master session key from "
"pre-auth EAPOL state machines");
- success = 0;
+ result = EAPOL_SUPP_RESULT_FAILURE;
}
}
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
MACSTR " %s", MAC2STR(sm->preauth_bssid),
- success ? "completed successfully" : "failed");
+ result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" :
+ "failed");
rsn_preauth_deinit(sm);
rsn_preauth_candidate_process(sm);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index e911ad0..cd34223 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -33,6 +33,7 @@
#define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
#define TDLS_TESTING_DECLINE_RESP BIT(9)
#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
+#define TDLS_TESTING_WRONG_MIC BIT(11)
unsigned int tdls_testing = 0;
#endif /* CONFIG_TDLS_TESTING */
@@ -83,6 +84,8 @@
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);
+static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
+ u16 reason_code);
#define TDLS_MAX_IE_LEN 80
@@ -118,6 +121,7 @@
u8 action_code; /* TDLS frame type */
u8 dialog_token;
u16 status_code;
+ u32 peer_capab;
int buf_len; /* length of TPK message for retransmission */
u8 *buf; /* buffer for TPK message */
} sm_tmr;
@@ -136,6 +140,14 @@
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;
+
+ u8 wmm_capable;
};
@@ -205,26 +217,27 @@
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)
+ u16 status_code, u32 peer_capab,
+ const u8 *buf, size_t len)
{
return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
- status_code, buf, len);
+ status_code, peer_capab, buf, len);
}
static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
- u8 dialog_token, u16 status_code,
+ u8 dialog_token, u16 status_code, u32 peer_capab,
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",
+ "dialog_token=%u status_code=%u peer_capab=%u msg_len=%u",
MAC2STR(dest), action_code, dialog_token, status_code,
- (unsigned int) msg_len);
+ peer_capab, (unsigned int) msg_len);
if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
- status_code, msg, msg_len)) {
+ status_code, peer_capab, msg, msg_len)) {
wpa_printf(MSG_INFO, "TDLS: Failed to send message "
"(action_code=%u)", action_code);
return -1;
@@ -262,6 +275,7 @@
peer->sm_tmr.action_code = action_code;
peer->sm_tmr.dialog_token = dialog_token;
peer->sm_tmr.status_code = status_code;
+ peer->sm_tmr.peer_capab = peer_capab;
peer->sm_tmr.buf_len = msg_len;
os_free(peer->sm_tmr.buf);
peer->sm_tmr.buf = os_malloc(msg_len);
@@ -318,6 +332,7 @@
peer->sm_tmr.action_code,
peer->sm_tmr.dialog_token,
peer->sm_tmr.status_code,
+ peer->sm_tmr.peer_capab,
peer->sm_tmr.buf,
peer->sm_tmr.buf_len)) {
wpa_printf(MSG_INFO, "TDLS: Failed to retry "
@@ -549,7 +564,7 @@
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) {
+ if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
"dropping packet");
wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
@@ -576,7 +591,7 @@
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) {
+ if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
"dropping packet");
return -1;
@@ -616,7 +631,33 @@
}
-static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
+ struct wpa_tdls_peer *peer)
+{
+ struct wpa_tdls_peer *cur, *prev;
+
+ cur = sm->tdls;
+ prev = NULL;
+ while (cur && cur != peer) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur != peer) {
+ wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
+ " to remove it from the list",
+ MAC2STR(peer->addr));
+ return;
+ }
+
+ if (prev)
+ prev->next = peer->next;
+ else
+ sm->tdls = peer->next;
+}
+
+
+static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
MAC2STR(peer->addr));
@@ -633,8 +674,14 @@
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->qos_info = 0;
+ peer->wmm_capable = 0;
peer->tpk_set = peer->tpk_success = 0;
os_memset(&peer->tpk, 0, sizeof(peer->tpk));
os_memset(peer->inonce, 0, WPA_NONCE_LEN);
@@ -642,6 +689,14 @@
}
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ wpa_tdls_peer_clear(sm, peer);
+ wpa_tdls_peer_remove_from_list(sm, peer);
+ os_free(peer);
+}
+
+
static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
struct wpa_tdls_lnkid *lnkid)
{
@@ -658,7 +713,8 @@
}
-int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+static 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;
@@ -737,12 +793,9 @@
/* request driver to send Teardown using this FTIE */
wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
- reason_code, rbuf, pos - rbuf);
+ reason_code, 0, rbuf, pos - rbuf);
os_free(rbuf);
- /* clear the Peerkey statemachine */
- wpa_tdls_peer_free(sm, peer);
-
return 0;
}
@@ -783,7 +836,7 @@
}
-void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr)
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
{
struct wpa_tdls_peer *peer;
@@ -792,8 +845,47 @@
break;
}
- if (peer)
+ if (!peer || !peer->tpk_success) {
+ wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+ " not connected - cannot teardown unreachable link",
+ MAC2STR(addr));
+ return;
+ }
+
+ if (wpa_tdls_is_external_setup(sm)) {
+ /*
+ * Disable the link, send a teardown packet through the
+ * AP, and then reset link data.
+ */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
+ wpa_tdls_send_teardown(sm, addr,
+ WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
+ wpa_tdls_peer_free(sm, peer);
+ } else {
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";
}
@@ -886,7 +978,7 @@
" (action=%u status=%u)",
MAC2STR(dst), tdls_action, status);
return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
- NULL, 0);
+ 0, NULL, 0);
}
@@ -1092,7 +1184,7 @@
MAC2STR(peer->addr));
status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
- 1, 0, rbuf, pos - rbuf);
+ 1, 0, 0, rbuf, pos - rbuf);
os_free(rbuf);
return status;
@@ -1173,10 +1265,16 @@
/* compute MIC before sending */
wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
(u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
+ ftie->mic[0] ^= 0x01;
+ }
+#endif /* CONFIG_TDLS_TESTING */
skip_ies:
status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
- dtoken, 0, rbuf, pos - rbuf);
+ dtoken, 0, 0, rbuf, pos - rbuf);
os_free(rbuf);
return status;
@@ -1194,6 +1292,7 @@
struct wpa_tdls_timeoutie timeoutie;
u32 lifetime;
int status;
+ u32 peer_capab = 0;
buf_len = 0;
if (wpa_tdls_get_privacy(sm)) {
@@ -1255,10 +1354,24 @@
/* compute MIC before sending */
wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
(u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
+ ftie->mic[0] ^= 0x01;
+ }
+#endif /* CONFIG_TDLS_TESTING */
skip_ies:
+
+ if (peer->vht_capabilities)
+ peer_capab |= TDLS_PEER_VHT;
+ if (peer->ht_capabilities)
+ peer_capab |= TDLS_PEER_HT;
+ if (peer->wmm_capable)
+ peer_capab |= TDLS_PEER_WMM;
+
status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
- dtoken, 0, rbuf, pos - rbuf);
+ dtoken, 0, peer_capab, rbuf, pos - rbuf);
os_free(rbuf);
return status;
@@ -1273,7 +1386,7 @@
"(peer " MACSTR ")", MAC2STR(peer->addr));
return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
- dialog_token, 0, NULL, 0);
+ dialog_token, 0, 0, NULL, 0);
}
@@ -1299,10 +1412,17 @@
dialog_token = buf[sizeof(struct wpa_tdls_frame)];
+ /*
+ * Some APs will tack on a weird IE to the end of a TDLS
+ * discovery request packet. This needn't fail the response,
+ * since the required IE are verified separately.
+ */
if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
len - (sizeof(struct wpa_tdls_frame) + 1),
- &kde) < 0)
- return -1;
+ &kde) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
+ }
if (!kde.lnkid) {
wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
@@ -1334,7 +1454,7 @@
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);
+ 1, 0, 0, NULL, 0);
}
@@ -1434,6 +1554,100 @@
}
+static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
+ struct wpa_tdls_peer *peer)
+{
+ struct wmm_information_element *wmm;
+
+ if (!kde->wmm) {
+ wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
+ return 0;
+ }
+
+ if (kde->wmm_len < sizeof(struct wmm_information_element)) {
+ wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
+ return -1;
+ }
+
+ wmm = (struct wmm_information_element *) kde->wmm;
+ peer->qos_info = wmm->qos_info;
+
+ peer->wmm_capable = 1;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
+ 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_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+ int add)
+{
+ return wpa_sm_tdls_peer_addset(sm, peer->addr, add, 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);
+}
+
+
static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
@@ -1481,16 +1695,16 @@
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) {
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ wpa_tdls_peer_clear(sm, peer);
+ } else if (peer->initiator) {
+ /*
+ * 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 (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
wpa_printf(MSG_DEBUG, "TDLS: Discard request "
"from peer with higher address "
@@ -1502,7 +1716,9 @@
MACSTR " (terminate previously "
"initiated negotiation",
MAC2STR(src_addr));
- wpa_tdls_disable_peer_link(sm, peer);
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+ peer->addr);
+ wpa_tdls_peer_clear(sm, peer);
}
}
}
@@ -1546,8 +1762,18 @@
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;
+ /* Overwrite with the qos_info obtained in WMM IE */
+ if (copy_peer_wmm_capab(&kde, peer) < 0)
+ goto error;
+
peer->aid = kde.aid;
#ifdef CONFIG_TDLS_TESTING
@@ -1682,7 +1908,6 @@
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;
}
}
@@ -1737,14 +1962,15 @@
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);
+ /* add supported rates, capabilities, and qos_info to the TDLS peer */
+ if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
+ goto error;
+
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);
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
goto error;
}
@@ -1753,6 +1979,8 @@
error:
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
status);
+ if (peer)
+ wpa_tdls_peer_free(sm, peer);
return -1;
}
@@ -1781,16 +2009,6 @@
#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) < 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");
@@ -1817,7 +2035,7 @@
int ielen;
u16 status;
const u8 *pos;
- int ret;
+ int ret = 0;
wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
"(Peer " MACSTR ")", MAC2STR(src_addr));
@@ -1916,8 +2134,18 @@
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;
+ /* Overwrite with the qos_info obtained in WMM IE */
+ if (copy_peer_wmm_capab(&kde, peer) < 0)
+ goto error;
+
peer->aid = kde.aid;
if (!wpa_tdls_get_privacy(sm)) {
@@ -1935,6 +2163,13 @@
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
kde.rsn_ie, kde.rsn_ie_len);
+ if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
+ wpa_printf(MSG_INFO,
+ "TDLS: Too long Responder RSN IE in TPK M2");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
/*
* FIX: bitwise comparison of RSN IE is not the correct way of
* validation this. It can be different, but certain fields must
@@ -2027,18 +2262,28 @@
skip_rsn:
peer->dtoken = dtoken;
+ /* add supported rates, capabilities, and qos_info to the TDLS peer */
+ if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+ goto error;
+
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 (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
+ goto error;
- 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);
+ 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;
@@ -2062,7 +2307,7 @@
u16 status;
const u8 *pos;
u32 lifetime;
- int ret;
+ int ret = 0;
wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
"(Peer " MACSTR ")", MAC2STR(src_addr));
@@ -2092,9 +2337,16 @@
pos += 2 /* status code */ + 1 /* dialog token */;
ielen = len - (pos - buf); /* start of IE in buf */
+
+ /*
+ * Don't reject the message if failing to parse IEs. The IEs we need are
+ * explicitly checked below. Some APs piggy-back broken IEs to the end
+ * of a TDLS Confirm packet, which will fail the link if we don't ignore
+ * this error.
+ */
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;
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
}
if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
@@ -2179,15 +2431,25 @@
}
skip_rsn:
- 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);
+ /* add supported rates, capabilities, and qos_info to the TDLS peer */
+ if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+ goto error;
+
+ 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");
+ goto error;
+ }
}
return ret;
error:
- wpa_tdls_disable_peer_link(sm, peer);
+ wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
return -1;
}
@@ -2250,8 +2512,11 @@
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);
+ if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+ NULL, 0, NULL, 0, NULL, 0, NULL, 0)) {
+ wpa_tdls_disable_peer_link(sm, peer);
+ return -1;
+ }
peer->tpk_in_progress = 1;
@@ -2284,7 +2549,8 @@
* Disable previous link to allow renegotiation to be completed
* on AP path.
*/
- wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ wpa_tdls_do_teardown(sm, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
}
}
@@ -2400,22 +2666,23 @@
void wpa_tdls_teardown_peers(struct wpa_sm *sm)
{
- struct wpa_tdls_peer *peer;
+ struct wpa_tdls_peer *peer, *tmp;
peer = sm->tdls;
wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
while (peer) {
+ tmp = peer->next;
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);
+ wpa_tdls_do_teardown(sm, peer,
+ WLAN_REASON_DEAUTH_LEAVING);
else
wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
- peer = peer->next;
+ peer = tmp;
}
}
@@ -2425,7 +2692,6 @@
struct wpa_tdls_peer *peer, *tmp;
peer = sm->tdls;
- sm->tdls = NULL;
while (peer) {
int res;
@@ -2434,7 +2700,6 @@
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;
}
}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 292255c..d6fb6a1 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -89,7 +89,10 @@
int key_info, ver;
u8 bssid[ETH_ALEN], *rbuf;
- if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt))
+ if (sm->key_mgmt == WPA_KEY_MGMT_OSEN)
+ ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+ else 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;
@@ -107,7 +110,8 @@
if (rbuf == NULL)
return;
- reply->type = sm->proto == WPA_PROTO_RSN ?
+ reply->type = (sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info = WPA_KEY_INFO_REQUEST | ver;
if (sm->ptk_set)
@@ -158,7 +162,7 @@
}
if (pmkid && sm->cur_pmksa &&
- os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
+ os_memcmp_const(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",
@@ -231,7 +235,8 @@
}
if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
- !wpa_key_mgmt_ft(sm->key_mgmt)) {
+ !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN)
+ {
/* Send EAPOL-Start to trigger full EAP authentication. */
u8 *buf;
size_t buflen;
@@ -325,11 +330,12 @@
return -1;
}
- reply->type = sm->proto == WPA_PROTO_RSN ?
+ reply->type = (sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
WPA_PUT_BE16(reply->key_info,
ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
- if (sm->proto == WPA_PROTO_RSN)
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
WPA_PUT_BE16(reply->key_length, 0);
else
os_memcpy(reply->key_length, key->key_length, 2);
@@ -356,7 +362,7 @@
const struct wpa_eapol_key *key,
struct wpa_ptk *ptk)
{
- size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
+ size_t ptk_len = wpa_cipher_key_len(sm->pairwise_cipher) + 32;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt))
return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
@@ -373,12 +379,14 @@
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)
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
{
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 "
@@ -392,12 +400,11 @@
os_memset(&ie, 0, sizeof(ie));
- if (sm->proto == WPA_PROTO_RSN) {
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
/* 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)
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
+ key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
goto failed;
if (ie.pmkid) {
wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
@@ -429,21 +436,49 @@
* 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);
+ if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
+ u8 buf[8];
+ /* 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);
+ os_memset(buf, 0, sizeof(buf));
+ }
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,
- sm->assoc_wpa_ie, sm->assoc_wpa_ie_len,
- ptk))
+ 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);
}
@@ -535,7 +570,7 @@
keylen = wpa_cipher_key_len(sm->pairwise_cipher);
rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
- if (sm->proto == WPA_PROTO_RSN) {
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
key_rsc = null_rsc;
} else {
key_rsc = key->key_rsc;
@@ -623,6 +658,7 @@
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to set GTK to the driver "
"(Group only)");
+ os_memset(gtk_buf, 0, sizeof(gtk_buf));
return -1;
}
} else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
@@ -632,8 +668,10 @@
"WPA: Failed to set GTK to "
"the driver (alg=%d keylen=%d keyidx=%d)",
gd->alg, gd->gtk_len, gd->keyidx);
+ os_memset(gtk_buf, 0, sizeof(gtk_buf));
return -1;
}
+ os_memset(gtk_buf, 0, sizeof(gtk_buf));
return 0;
}
@@ -688,14 +726,17 @@
os_memcpy(gd.gtk, gtk, gtk_len);
gd.gtk_len = gtk_len;
- if (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)) {
+ 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");
+ os_memset(&gd, 0, sizeof(gd));
return -1;
}
+ os_memset(&gd, 0, sizeof(gd));
wpa_supplicant_key_neg_complete(sm, sm->bssid,
key_info & WPA_KEY_INFO_SECURE);
@@ -707,13 +748,15 @@
struct wpa_eapol_ie_parse *ie)
{
#ifdef CONFIG_IEEE80211W
- if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+ if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher))
return 0;
if (ie->igtk) {
+ size_t len;
const struct wpa_igtk_kde *igtk;
u16 keyidx;
- if (ie->igtk_len != sizeof(*igtk))
+ len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
return -1;
igtk = (const struct wpa_igtk_kde *) ie->igtk;
keyidx = WPA_GET_LE16(igtk->keyid);
@@ -721,15 +764,16 @@
"pn %02x%02x%02x%02x%02x%02x",
keyidx, MAC2STR(igtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
- igtk->igtk, WPA_IGTK_LEN);
+ igtk->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,
+ if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
- igtk->igtk, WPA_IGTK_LEN) < 0) {
+ igtk->igtk, len) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to configure IGTK to the driver");
return -1;
@@ -862,7 +906,8 @@
return -1;
}
- if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) {
+ if (os_memcmp_const(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");
@@ -982,45 +1027,37 @@
* @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);
+ sizeof(*reply), &rlen, (void *) &reply);
if (rbuf == NULL)
return -1;
- reply->type = sm->proto == WPA_PROTO_RSN ?
+ reply->type = (sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info &= WPA_KEY_INFO_SECURE;
key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
WPA_PUT_BE16(reply->key_info, key_info);
- if (sm->proto == WPA_PROTO_RSN)
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
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_PUT_BE16(reply->key_data_length, 0);
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,
@@ -1032,10 +1069,10 @@
static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
- u16 ver)
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
{
- u16 key_info, keylen, len;
- const u8 *pos;
+ u16 key_info, keylen;
struct wpa_eapol_ie_parse ie;
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
@@ -1044,10 +1081,8 @@
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)
+ wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
goto failed;
if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1061,7 +1096,10 @@
goto failed;
}
- if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) {
+ if (ie.igtk &&
+ wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
+ ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN +
+ (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid IGTK KDE length %lu",
(unsigned long) ie.igtk_len);
@@ -1089,8 +1127,16 @@
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)) {
+ &sm->ptk)) {
goto failed;
}
@@ -1112,7 +1158,10 @@
}
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
- if (ie.gtk &&
+ 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,
@@ -1126,7 +1175,8 @@
goto failed;
}
- wpa_sm_set_rekey_offload(sm);
+ if (ie.gtk)
+ wpa_sm_set_rekey_offload(sm);
return;
@@ -1187,22 +1237,14 @@
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)
+ const u8 *key_data,
+ size_t key_data_len, u16 key_info,
+ 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;
- }
+ maxkeylen = key_data_len;
if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (maxkeylen < 8) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1221,37 +1263,39 @@
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)) {
+ u8 ek[32];
+ if (key_data_len > sizeof(gd->gtk)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: RC4 key data too long (%lu)",
- (unsigned long) keydatalen);
+ (unsigned long) key_data_len);
return -1;
}
- os_memcpy(gd->gtk, key + 1, keydatalen);
- if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) {
+ os_memcpy(ek, key->key_iv, 16);
+ os_memcpy(ek + 16, sm->ptk.kek, 16);
+ os_memcpy(gd->gtk, key_data, key_data_len);
+ if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
+ os_memset(ek, 0, sizeof(ek));
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"WPA: RC4 failed");
return -1;
}
+ os_memset(ek, 0, sizeof(ek));
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- if (keydatalen % 8) {
+ if (maxkeylen % 8) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported AES-WRAP len %lu",
- (unsigned long) keydatalen);
+ (unsigned long) maxkeylen);
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) key_data_len,
(unsigned long) maxkeylen);
return -1;
}
- if (aes_unwrap(sm->ptk.kek, maxkeylen / 8,
- (const u8 *) (key + 1), gd->gtk)) {
+ if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, key_data, gd->gtk)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: AES unwrap failed - could not decrypt "
"GTK");
@@ -1281,12 +1325,13 @@
if (rbuf == NULL)
return -1;
- reply->type = sm->proto == WPA_PROTO_RSN ?
+ reply->type = (sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
WPA_PUT_BE16(reply->key_info, key_info);
- if (sm->proto == WPA_PROTO_RSN)
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
WPA_PUT_BE16(reply->key_length, 0);
else
os_memcpy(reply->key_length, key->key_length, 2);
@@ -1306,9 +1351,10 @@
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)
+ const u8 *key_data,
+ size_t key_data_len, u16 ver)
{
- u16 key_info, keydatalen;
+ u16 key_info;
int rekey, ret;
struct wpa_gtk_data gd;
@@ -1319,17 +1365,15 @@
"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,
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+ ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
+ key_data_len, key_info,
&gd);
} else {
- ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen,
- key_info, extra_len,
- ver, &gd);
+ ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
+ key_data_len,
+ key_info, ver, &gd);
}
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
@@ -1347,13 +1391,14 @@
MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
wpa_sm_cancel_auth_timeout(sm);
wpa_sm_set_state(sm, WPA_COMPLETED);
-
- wpa_sm_set_rekey_offload(sm);
} else {
wpa_supplicant_key_neg_complete(sm, sm->bssid,
key_info &
WPA_KEY_INFO_SECURE);
}
+
+ wpa_sm_set_rekey_offload(sm);
+
return;
failed:
@@ -1374,7 +1419,7 @@
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) {
+ if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC "
"when using TPTK - ignoring TPTK");
@@ -1383,6 +1428,7 @@
sm->tptk_set = 0;
sm->ptk_set = 1;
os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
}
}
@@ -1390,7 +1436,7 @@
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) {
+ if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC - "
"dropping packet");
@@ -1415,12 +1461,11 @@
/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
- struct wpa_eapol_key *key, u16 ver)
+ struct wpa_eapol_key *key, u16 ver,
+ u8 *key_data, size_t *key_data_len)
{
- u16 keydatalen = WPA_GET_BE16(key->key_data_length);
-
wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
- (u8 *) (key + 1), keydatalen);
+ key_data, *key_data_len);
if (!sm->ptk_set) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: PTK not available, cannot decrypt EAPOL-Key Key "
@@ -1434,45 +1479,48 @@
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)) {
+ if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
+ os_memset(ek, 0, sizeof(ek));
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"WPA: RC4 failed");
return -1;
}
+ os_memset(ek, 0, sizeof(ek));
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
- ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+ sm->key_mgmt == WPA_KEY_MGMT_OSEN) {
u8 *buf;
- if (keydatalen % 8) {
+ if (*key_data_len < 8 || *key_data_len % 8) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Unsupported AES-WRAP len %d",
- keydatalen);
+ "WPA: Unsupported AES-WRAP len %u",
+ (unsigned int) *key_data_len);
return -1;
}
- keydatalen -= 8; /* AES-WRAP adds 8 bytes */
- buf = os_malloc(keydatalen);
+ *key_data_len -= 8; /* AES-WRAP adds 8 bytes */
+ buf = os_malloc(*key_data_len);
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)) {
+ if (aes_unwrap(sm->ptk.kek, *key_data_len / 8,
+ key_data, 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_memcpy(key_data, buf, *key_data_len);
os_free(buf);
- WPA_PUT_BE16(key->key_data_length, keydatalen);
+ WPA_PUT_BE16(key->key_data_length, *key_data_len);
} 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);
+ key_data, *key_data_len);
return 0;
}
@@ -1546,13 +1594,14 @@
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;
+ size_t plen, data_len, key_data_len;
+ const struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
u16 key_info, ver;
- u8 *tmp;
+ u8 *tmp = NULL;
int ret = -1;
struct wpa_peerkey *peerkey = NULL;
+ u8 *key_data;
#ifdef CONFIG_IEEE80211R
sm->ft_completed = 0;
@@ -1567,13 +1616,7 @@
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);
+ hdr = (const struct ieee802_1x_hdr *) buf;
plen = be_to_host16(hdr->length);
data_len = plen + sizeof(*hdr);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -1590,6 +1633,7 @@
ret = 0;
goto out;
}
+ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: EAPOL frame payload size %lu "
@@ -1598,6 +1642,22 @@
ret = 0;
goto out;
}
+ 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);
+ }
+
+ /*
+ * Make a copy of the frame since we need to modify the buffer during
+ * MAC validation and Key Data decryption.
+ */
+ tmp = os_malloc(data_len);
+ if (tmp == NULL)
+ goto out;
+ os_memcpy(tmp, buf, data_len);
+ key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
+ key_data = (u8 *) (key + 1);
if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
{
@@ -1609,26 +1669,38 @@
}
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_data_len = WPA_GET_BE16(key->key_data_length);
+ if (key_data_len > plen - sizeof(struct wpa_eapol_key)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
+ "frame - key_data overflow (%u > %u)",
+ (unsigned int) key_data_len,
+ (unsigned int) (plen - sizeof(struct wpa_eapol_key)));
+ goto out;
}
+
+ eapol_sm_notify_lower_layer_success(sm->eapol, 0);
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) {
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+ sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Unsupported EAPOL-Key descriptor version %d",
ver);
goto out;
}
+ if (sm->key_mgmt == WPA_KEY_MGMT_OSEN &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "OSEN: Unsupported EAPOL-Key descriptor version %d",
+ ver);
+ goto out;
+ }
+
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
/* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
@@ -1641,7 +1713,8 @@
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
- if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+ sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: AP did not use the "
"negotiated AES-128-CMAC");
@@ -1663,11 +1736,13 @@
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Backwards compatibility: allow invalid "
"version for non-CCMP group keys");
+ } else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
} else
goto out;
- }
- if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
- ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ } else 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);
@@ -1746,22 +1821,11 @@
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 &&
+ if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- if (wpa_supplicant_decrypt_key_data(sm, key, ver))
+ if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
+ &key_data_len))
goto out;
- extra_len = WPA_GET_BE16(key->key_data_length);
}
if (key_info & WPA_KEY_INFO_KEY_TYPE) {
@@ -1773,24 +1837,28 @@
}
if (peerkey) {
/* PeerKey 4-Way Handshake */
- peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver);
+ peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
+ key_data, key_data_len);
} else if (key_info & WPA_KEY_INFO_MIC) {
/* 3/4 4-Way Handshake */
- wpa_supplicant_process_3_of_4(sm, key, ver);
+ wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
+ key_data_len);
} else {
/* 1/4 4-Way Handshake */
wpa_supplicant_process_1_of_4(sm, src_addr, key,
- ver);
+ ver, key_data,
+ key_data_len);
}
} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
/* PeerKey SMK Handshake */
- peerkey_rx_eapol_smk(sm, src_addr, key, extra_len, key_info,
+ peerkey_rx_eapol_smk(sm, src_addr, key, key_data_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);
+ key_data, key_data_len,
+ ver);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: EAPOL-Key (Group) without Mic bit - "
@@ -1811,7 +1879,8 @@
{
switch (sm->key_mgmt) {
case WPA_KEY_MGMT_IEEE8021X:
- return (sm->proto == WPA_PROTO_RSN ?
+ return ((sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
case WPA_KEY_MGMT_PSK:
@@ -2074,12 +2143,18 @@
*/
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
sm->ptk_set = 0;
+ os_memset(&sm->ptk, 0, sizeof(sm->ptk));
sm->tptk_set = 0;
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
}
#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 */
}
@@ -2092,6 +2167,7 @@
*/
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)
@@ -2202,6 +2278,7 @@
} 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;
@@ -2211,6 +2288,7 @@
sm->eap_conf_ctx = NULL;
sm->ssid_len = 0;
sm->wpa_ptk_rekey = 0;
+ sm->p2p = 0;
}
}
@@ -2321,44 +2399,6 @@
/**
- * 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
@@ -2599,6 +2639,7 @@
}
+#ifdef CONFIG_TESTING_OPTIONS
void wpa_sm_drop_sa(struct wpa_sm *sm)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
@@ -2608,6 +2649,7 @@
os_memset(&sm->ptk, 0, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
}
+#endif /* CONFIG_TESTING_OPTIONS */
int wpa_sm_has_ptk(struct wpa_sm *sm)
@@ -2633,29 +2675,22 @@
#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) {
+ struct wpa_gtk_data gd;
+
+ os_memset(&gd, 0, sizeof(gd));
+ 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;
+ }
+
key_rsc = buf + 5;
keyinfo = WPA_GET_LE16(buf + 2);
gd.gtk_len = keylen;
@@ -2673,27 +2708,37 @@
wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
gd.gtk, gd.gtk_len);
if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+ os_memset(&gd, 0, sizeof(gd));
wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
"WNM mode");
return -1;
}
+ os_memset(&gd, 0, sizeof(gd));
#ifdef CONFIG_IEEE80211W
} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
+ struct wpa_igtk_kde igd;
+ u16 keyidx;
+
+ os_memset(&igd, 0, sizeof(igd));
+ keylen = wpa_cipher_key_len(sm->mgmt_group_cipher);
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);
+ os_memcpy(igd.igtk, buf + 10, keylen);
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,
+ igd.igtk, keylen);
+ if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr,
keyidx, 0, igd.pn, sizeof(igd.pn),
- igd.igtk, WPA_IGTK_LEN) < 0) {
+ igd.igtk, keylen) < 0) {
wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
"WNM mode");
+ os_memset(&igd, 0, sizeof(igd));
return -1;
}
+ os_memset(&igd, 0, sizeof(igd));
#endif /* CONFIG_IEEE80211W */
} else {
wpa_printf(MSG_DEBUG, "Unknown element id");
@@ -2703,3 +2748,37 @@
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 */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 26e9c6c..07a7bf9 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -54,7 +54,8 @@
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);
+ u16 status_code, u32 peer_capab,
+ 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,
@@ -62,7 +63,10 @@
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);
+ 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);
@@ -92,6 +96,7 @@
const u8 *ssid;
size_t ssid_len;
int wpa_ptk_rekey;
+ int p2p;
};
#ifndef CONFIG_NO_WPA
@@ -118,8 +123,6 @@
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);
@@ -142,6 +145,8 @@
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)
@@ -233,12 +238,6 @@
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)
{
@@ -306,11 +305,19 @@
#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
@@ -372,14 +379,14 @@
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);
+void wpa_tdls_disable_unreachable_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);
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 3a40c96..4a75b92 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -207,6 +207,8 @@
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);
@@ -400,8 +402,7 @@
}
}
- if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
- sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+ 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;
@@ -441,7 +442,8 @@
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
- os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+ os_memcmp_const(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",
@@ -457,7 +459,8 @@
}
if (parse.rsn_pmkid == NULL ||
- os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) {
+ os_memcmp_const(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN))
+ {
wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
"RSNIE");
return -1;
@@ -526,8 +529,7 @@
if (sm == NULL)
return 0;
- if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
- sm->key_mgmt != WPA_KEY_MGMT_FT_PSK)
+ if (!wpa_key_mgmt_ft(sm->key_mgmt))
return 0;
return sm->ft_completed;
@@ -678,8 +680,7 @@
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
- if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
- sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+ 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;
@@ -728,7 +729,8 @@
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
- os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+ os_memcmp_const(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",
@@ -743,14 +745,15 @@
return -1;
}
- if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
+ if (os_memcmp_const(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)) {
+ os_memcmp_const(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;
@@ -776,7 +779,7 @@
return -1;
}
- if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ if (os_memcmp_const(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);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 0e0d373..e20e9da 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -58,6 +58,7 @@
u8 ssid[32];
size_t ssid_len;
int wpa_ptk_rekey;
+ int p2p;
u8 own_addr[ETH_ALEN];
const char *ifname;
@@ -122,6 +123,10 @@
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 */
};
@@ -262,13 +267,13 @@
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)
+ u16 status_code, u32 peer_capab,
+ 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);
+ peer_capab, buf, len);
return -1;
}
@@ -286,14 +291,21 @@
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)
+ 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);
+ ext_capab, ext_capab_len,
+ supp_channels,
+ supp_channels_len,
+ supp_oper_classes,
+ supp_oper_classes_len);
return -1;
}
#endif /* CONFIG_TDLS */
@@ -309,7 +321,6 @@
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,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 50b9272..2329033 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -201,7 +201,7 @@
}
#ifdef CONFIG_IEEE80211W
- if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
if (!sm->cur_pmksa) {
/* PMKID Count */
WPA_PUT_LE16(pos, 0);
@@ -209,7 +209,8 @@
}
/* Management Group Cipher Suite */
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+ mgmt_group_cipher));
pos += RSN_SELECTOR_LEN;
}
#endif /* CONFIG_IEEE80211W */
@@ -222,6 +223,64 @@
}
+#ifdef CONFIG_HS20
+static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len,
+ int pairwise_cipher, int group_cipher,
+ int key_mgmt)
+{
+ u8 *pos, *len;
+ u32 suite;
+
+ if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN +
+ 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN)
+ return -1;
+
+ pos = wpa_ie;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = pos++; /* to be filled */
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ 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;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ 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;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+ pos += RSN_SELECTOR_LEN;
+
+ *len = pos - len - 1;
+
+ WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+ return pos - wpa_ie;
+}
+#endif /* CONFIG_HS20 */
+
+
/**
* wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -237,6 +296,13 @@
sm->group_cipher,
sm->key_mgmt, sm->mgmt_group_cipher,
sm);
+#ifdef CONFIG_HS20
+ else if (sm->proto == WPA_PROTO_OSEN)
+ return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt);
+#endif /* CONFIG_HS20 */
else
return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
sm->pairwise_cipher,
@@ -246,6 +312,42 @@
/**
+ * wpa_parse_vendor_specific - Parse Vendor Specific 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_vendor_specific(const u8 *pos, const u8 *end,
+ struct wpa_eapol_ie_parse *ie)
+{
+ unsigned int oui;
+
+ if (pos[1] < 4) {
+ wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)",
+ pos[1]);
+ return 1;
+ }
+
+ oui = WPA_GET_BE24(&pos[2]);
+ if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
+ if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
+ ie->wmm = &pos[2];
+ ie->wmm_len = pos[1];
+ wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
+ ie->wmm, ie->wmm_len);
+ } else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
+ ie->wmm = &pos[2];
+ ie->wmm_len = pos[1];
+ wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
+ ie->wmm, ie->wmm_len);
+ }
+ }
+ return 0;
+}
+
+
+/**
* 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
@@ -345,6 +447,25 @@
}
#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;
}
@@ -434,6 +555,20 @@
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) {
+ /*
+ * The value of the Length field of the Supported
+ * Operating Classes element is between 2 and 253.
+ * Silently skip invalid elements to avoid interop
+ * issues when trying to use the value.
+ */
+ if (pos[1] >= 2 && pos[1] <= 253) {
+ 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)
@@ -442,6 +577,14 @@
ret = 0;
break;
}
+
+ ret = wpa_parse_vendor_specific(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]);
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 2c78801..0fc42cc 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -53,8 +53,18 @@
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;
+ const u8 *wmm;
+ size_t wmm_len;
+#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,
diff --git a/src/tls/asn1.c b/src/tls/asn1.c
index 53acd53..97462fa 100644
--- a/src/tls/asn1.c
+++ b/src/tls/asn1.c
@@ -1,6 +1,6 @@
/*
* ASN.1 DER parsing
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -11,6 +11,17 @@
#include "common.h"
#include "asn1.h"
+struct asn1_oid asn1_sha1_oid = {
+ .oid = { 1, 3, 14, 3, 2, 26 },
+ .len = 6
+};
+
+struct asn1_oid asn1_sha256_oid = {
+ .oid = { 2, 16, 840, 1, 101, 3, 4, 2, 1 },
+ .len = 9
+};
+
+
int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
{
const u8 *pos, *end;
@@ -140,7 +151,7 @@
}
-void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len)
{
char *pos = buf;
size_t i;
@@ -204,3 +215,19 @@
return val;
}
+
+
+int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b)
+{
+ size_t i;
+
+ if (a->len != b->len)
+ return 0;
+
+ for (i = 0; i < a->len; i++) {
+ if (a->oid[i] != b->oid[i])
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/src/tls/asn1.h b/src/tls/asn1.h
index 6342c4c..7475007 100644
--- a/src/tls/asn1.h
+++ b/src/tls/asn1.h
@@ -60,7 +60,11 @@
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);
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len);
unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len);
+int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b);
+
+extern struct asn1_oid asn1_sha1_oid;
+extern struct asn1_oid asn1_sha256_oid;
#endif /* ASN1_H */
diff --git a/src/tls/pkcs1.c b/src/tls/pkcs1.c
index b6fde5e..141ac50 100644
--- a/src/tls/pkcs1.c
+++ b/src/tls/pkcs1.c
@@ -1,6 +1,6 @@
/*
* PKCS #1 (RSA Encryption)
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,7 +9,9 @@
#include "includes.h"
#include "common.h"
+#include "crypto/crypto.h"
#include "rsa.h"
+#include "asn1.h"
#include "pkcs1.h"
@@ -113,6 +115,11 @@
pos++;
if (pos == end)
return -1;
+ if (pos - out - 2 < 8) {
+ /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+ wpa_printf(MSG_INFO, "LibTomCrypt: Too short padding");
+ return -1;
+ }
pos++;
*outlen -= pos - out;
@@ -142,35 +149,26 @@
* BT = 00 or 01
* PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
* k = length of modulus in octets
+ *
+ * Based on 10.1.3, "The block type shall be 01" for a signature.
*/
if (len < 3 + 8 + 16 /* min hash len */ ||
- plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
+ plain[0] != 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++;
+ /* 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 */
@@ -193,3 +191,130 @@
return 0;
}
+
+
+int pkcs1_v15_sig_ver(struct crypto_public_key *pk,
+ const u8 *s, size_t s_len,
+ const struct asn1_oid *hash_alg,
+ const u8 *hash, size_t hash_len)
+{
+ int res;
+ u8 *decrypted;
+ size_t decrypted_len;
+ const u8 *pos, *end, *next, *da_end;
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+
+ decrypted = os_malloc(s_len);
+ if (decrypted == NULL)
+ return -1;
+ decrypted_len = s_len;
+ res = crypto_public_key_decrypt_pkcs1(pk, s, s_len, decrypted,
+ &decrypted_len);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "PKCS #1: RSA decrypt failed");
+ os_free(decrypted);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "Decrypted(S)", decrypted, decrypted_len);
+
+ /*
+ * PKCS #1 v1.5, 10.1.2:
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * digest Digest
+ * }
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * Digest ::= OCTET STRING
+ *
+ */
+ if (asn1_get_next(decrypted, decrypted_len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG,
+ "PKCS #1: Expected SEQUENCE (DigestInfo) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(decrypted);
+ 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,
+ "PKCS #1: Expected SEQUENCE (AlgorithmIdentifier) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(decrypted);
+ return -1;
+ }
+ da_end = hdr.payload + hdr.length;
+
+ if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+ wpa_printf(MSG_DEBUG,
+ "PKCS #1: Failed to parse digestAlgorithm");
+ os_free(decrypted);
+ return -1;
+ }
+
+ if (!asn1_oid_equal(&oid, hash_alg)) {
+ char txt[100], txt2[100];
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ asn1_oid_to_str(hash_alg, txt2, sizeof(txt2));
+ wpa_printf(MSG_DEBUG,
+ "PKCS #1: Hash alg OID mismatch: was %s, expected %s",
+ txt, txt2);
+ os_free(decrypted);
+ return -1;
+ }
+
+ /* Digest ::= OCTET STRING */
+ pos = da_end;
+ end = decrypted + decrypted_len;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG,
+ "PKCS #1: Expected OCTETSTRING (Digest) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(decrypted);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Decrypted Digest",
+ hdr.payload, hdr.length);
+
+ if (hdr.length != hash_len ||
+ os_memcmp_const(hdr.payload, hash, hdr.length) != 0) {
+ wpa_printf(MSG_INFO, "PKCS #1: Digest value does not match calculated hash");
+ os_free(decrypted);
+ return -1;
+ }
+
+ os_free(decrypted);
+
+ if (hdr.payload + hdr.length != end) {
+ wpa_printf(MSG_INFO,
+ "PKCS #1: Extra data after signature - reject");
+
+ wpa_hexdump(MSG_DEBUG, "PKCS #1: Extra data",
+ hdr.payload + hdr.length,
+ end - hdr.payload - hdr.length);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/tls/pkcs1.h b/src/tls/pkcs1.h
index ed64def..f37ebf3 100644
--- a/src/tls/pkcs1.h
+++ b/src/tls/pkcs1.h
@@ -9,6 +9,9 @@
#ifndef PKCS1_H
#define PKCS1_H
+struct crypto_public_key;
+struct asn1_oid;
+
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);
@@ -18,5 +21,9 @@
int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
const u8 *crypt, size_t crypt_len,
u8 *plain, size_t *plain_len);
+int pkcs1_v15_sig_ver(struct crypto_public_key *pk,
+ const u8 *s, size_t s_len,
+ const struct asn1_oid *hash_alg,
+ const u8 *hash, size_t hash_len);
#endif /* PKCS1_H */
diff --git a/src/tls/rsa.c b/src/tls/rsa.c
index 125c420..0b7b530 100644
--- a/src/tls/rsa.c
+++ b/src/tls/rsa.c
@@ -1,6 +1,6 @@
/*
* RSA
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -116,6 +116,29 @@
}
+struct crypto_rsa_key *
+crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len,
+ const u8 *e, size_t e_len)
+{
+ struct crypto_rsa_key *key;
+
+ 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 ||
+ bignum_set_unsigned_bin(key->n, n, n_len) < 0 ||
+ bignum_set_unsigned_bin(key->e, e, e_len) < 0) {
+ crypto_rsa_free(key);
+ return NULL;
+ }
+
+ return key;
+}
+
+
/**
* crypto_rsa_import_private_key - Import an RSA private key
* @buf: Key buffer (DER encoded RSA private key)
diff --git a/src/tls/rsa.h b/src/tls/rsa.h
index c236a9d..b65818e 100644
--- a/src/tls/rsa.h
+++ b/src/tls/rsa.h
@@ -14,6 +14,9 @@
struct crypto_rsa_key *
crypto_rsa_import_public_key(const u8 *buf, size_t len);
struct crypto_rsa_key *
+crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len,
+ const u8 *e, size_t e_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,
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 12148b6..4a4f0b6 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -1,6 +1,6 @@
/*
* TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -459,10 +459,15 @@
count = 0;
suites = conn->cipher_suites;
+ suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+ suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+ suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+ suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_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;
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 3269ecf..4f08e0f 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -1,6 +1,6 @@
/*
* TLSv1 client - read handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -409,10 +409,37 @@
}
-static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
- const u8 *buf, size_t len)
+static unsigned int count_bits(const u8 *val, size_t len)
{
- const u8 *pos, *end;
+ size_t i;
+ unsigned int bits;
+ u8 tmp;
+
+ for (i = 0; i < len; i++) {
+ if (val[i])
+ break;
+ }
+ if (i == len)
+ return 0;
+
+ bits = (len - i - 1) * 8;
+ tmp = val[i];
+ while (tmp) {
+ bits++;
+ tmp >>= 1;
+ }
+
+ return bits;
+}
+
+
+static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
+ const u8 *buf, size_t len,
+ tls_key_exchange key_exchange)
+{
+ const u8 *pos, *end, *server_params, *server_params_end;
+ u8 alert;
+ unsigned int bits;
tlsv1_client_free_dh(conn);
@@ -421,6 +448,7 @@
if (end - pos < 3)
goto fail;
+ server_params = pos;
conn->dh_p_len = WPA_GET_BE16(pos);
pos += 2;
if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
@@ -428,6 +456,14 @@
(unsigned long) conn->dh_p_len);
goto fail;
}
+ bits = count_bits(pos, conn->dh_p_len);
+ if (bits < 768) {
+ wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)",
+ bits);
+ wpa_hexdump(MSG_DEBUG, "TLSv1: Rejected DH prime",
+ pos, conn->dh_p_len);
+ goto fail;
+ }
conn->dh_p = os_malloc(conn->dh_p_len);
if (conn->dh_p == NULL)
goto fail;
@@ -465,6 +501,59 @@
pos += conn->dh_ys_len;
wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
conn->dh_ys, conn->dh_ys_len);
+ server_params_end = pos;
+
+ if (key_exchange == TLS_KEY_X_DHE_RSA) {
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+ int hlen;
+
+ if (conn->rl.tls_version == TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+ /*
+ * 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)
+ goto fail;
+ 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]);
+ goto fail;
+ }
+ pos += 2;
+
+ hlen = tlsv12_key_x_server_params_hash(
+ conn->rl.tls_version, conn->client_random,
+ conn->server_random, server_params,
+ server_params_end - server_params, hash);
+#else /* CONFIG_TLSV12 */
+ goto fail;
+#endif /* CONFIG_TLSV12 */
+ } else {
+ hlen = tls_key_x_server_params_hash(
+ conn->rl.tls_version, conn->client_random,
+ conn->server_random, server_params,
+ server_params_end - server_params, hash);
+ }
+
+ if (hlen < 0)
+ goto fail;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash",
+ hash, hlen);
+
+ if (tls_verify_signature(conn->rl.tls_version,
+ conn->server_rsa_key,
+ hash, hlen, pos, end - pos,
+ &alert) < 0)
+ goto fail;
+ }
return 0;
@@ -543,8 +632,10 @@
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) {
+ if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon ||
+ suite->key_exchange == TLS_KEY_X_DHE_RSA)) {
+ if (tlsv1_process_diffie_hellman(conn, pos, len,
+ suite->key_exchange) < 0) {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -871,8 +962,10 @@
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) {
+ if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
return -1;
}
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index d789efb..839eb90 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -1,6 +1,6 @@
/*
* TLSv1 client - write handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -205,7 +205,7 @@
}
-static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
{
/* ClientDiffieHellmanPublic */
u8 *csecret, *csecret_start, *dh_yc, *shared;
@@ -399,8 +399,8 @@
hs_length = pos;
pos += 3;
/* body - ClientKeyExchange */
- if (keyx == TLS_KEY_X_DH_anon) {
- if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
+ if (keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) {
+ if (tlsv1_key_x_dh(conn, &pos, end) < 0)
return -1;
} else {
if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index d212862..ced28cf 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -1,6 +1,6 @@
/*
* TLSv1 common routines
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,6 +9,7 @@
#include "includes.h"
#include "common.h"
+#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "x509v3.h"
@@ -33,6 +34,10 @@
TLS_HASH_SHA },
{ TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+ { TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_DHE_RSA, TLS_CIPHER_DES_CBC,
+ TLS_HASH_SHA},
+ { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DHE_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,
@@ -41,24 +46,31 @@
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_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_DHE_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_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_DHE_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_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DHE_RSA,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+ { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DHE_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_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
-#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
+#define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites)
static const struct tls_cipher_data tls_ciphers[] = {
@@ -84,7 +96,7 @@
CRYPTO_CIPHER_ALG_AES }
};
-#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
+#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers)
/**
@@ -320,3 +332,164 @@
return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
outlen);
}
+
+
+#ifdef CONFIG_TLSV12
+int tlsv12_key_x_server_params_hash(u16 tls_version,
+ const u8 *client_random,
+ const u8 *server_random,
+ const u8 *server_params,
+ size_t server_params_len, u8 *hash)
+{
+ size_t hlen;
+ struct crypto_hash *ctx;
+
+ ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+ if (ctx == NULL)
+ return -1;
+ crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+ crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+ crypto_hash_update(ctx, server_params, server_params_len);
+ hlen = SHA256_MAC_LEN;
+ if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+ return -1;
+
+ return hlen;
+}
+#endif /* CONFIG_TLSV12 */
+
+
+int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+ const u8 *server_random,
+ const u8 *server_params,
+ size_t server_params_len, u8 *hash)
+{
+ u8 *hpos;
+ size_t hlen;
+ enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+ struct crypto_hash *ctx;
+
+ hpos = hash;
+
+ if (alg == SIGN_ALG_RSA) {
+ ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+ if (ctx == NULL)
+ return -1;
+ crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+ crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+ crypto_hash_update(ctx, server_params, server_params_len);
+ hlen = MD5_MAC_LEN;
+ if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+ return -1;
+ hpos += hlen;
+ }
+
+ ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+ if (ctx == NULL)
+ return -1;
+ crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+ crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+ crypto_hash_update(ctx, server_params, server_params_len);
+ hlen = hash + sizeof(hash) - hpos;
+ if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
+ return -1;
+ hpos += hlen;
+ return hpos - hash;
+}
+
+
+int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
+ const u8 *data, size_t data_len,
+ const u8 *pos, size_t len, u8 *alert)
+{
+ u8 *buf;
+ const u8 *end = pos + len;
+ const u8 *decrypted;
+ u16 slen;
+ size_t buflen;
+
+ if (end - pos < 2) {
+ *alert = TLS_ALERT_DECODE_ERROR;
+ return -1;
+ }
+ slen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < slen) {
+ *alert = TLS_ALERT_DECODE_ERROR;
+ return -1;
+ }
+ if (end - pos > slen) {
+ wpa_hexdump(MSG_MSGDUMP, "Additional data after Signature",
+ pos + slen, end - pos - slen);
+ end = pos + slen;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+ if (pk == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No public key to verify signature");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+
+ buflen = end - pos;
+ buf = os_malloc(end - pos);
+ if (buf == NULL) {
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+ if (crypto_public_key_decrypt_pkcs1(pk, pos, end - pos, buf, &buflen) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+ os_free(buf);
+ *alert = TLS_ALERT_DECRYPT_ERROR;
+ return -1;
+ }
+ decrypted = buf;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+ decrypted, buflen);
+
+#ifdef CONFIG_TLSV12
+ if (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");
+ decrypted = buf + 19;
+ buflen -= 19;
+ } else {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo");
+ os_free(buf);
+ *alert = TLS_ALERT_DECRYPT_ERROR;
+ return -1;
+ }
+ }
+#endif /* CONFIG_TLSV12 */
+
+ if (buflen != data_len ||
+ os_memcmp_const(decrypted, data, data_len) != 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in CertificateVerify - did not match calculated hash");
+ os_free(buf);
+ *alert = TLS_ALERT_DECRYPT_ERROR;
+ return -1;
+ }
+
+ os_free(buf);
+
+ return 0;
+}
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index f28c0cd..26e68af 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -1,6 +1,6 @@
/*
* TLSv1 common definitions
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -257,5 +257,16 @@
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);
+int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+ const u8 *server_random,
+ const u8 *server_params,
+ size_t server_params_len, u8 *hash);
+int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+ const u8 *server_random,
+ const u8 *server_params,
+ size_t server_params_len, u8 *hash);
+int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
+ const u8 *data, size_t data_len,
+ const u8 *pos, size_t len, u8 *alert);
#endif /* TLSV1_COMMON_H */
diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c
index 3bec3be..0c6897a 100644
--- a/src/tls/tlsv1_record.c
+++ b/src/tls/tlsv1_record.c
@@ -456,7 +456,7 @@
return -1;
}
if (hlen != rl->hash_size ||
- os_memcmp(hash, out_data + plen, hlen) != 0 ||
+ os_memcmp_const(hash, out_data + plen, hlen) != 0 ||
force_mac_error) {
wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
"received message (force_mac_error=%d)",
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 2880309..23d0b81 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -1,6 +1,6 @@
/*
* TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -21,6 +21,31 @@
*/
+void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ va_start(ap, fmt);
+ vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+
+ wpa_printf(MSG_DEBUG, "TLSv1: %s", buf);
+ if (conn->log_cb)
+ conn->log_cb(conn->log_cb_ctx, buf);
+
+ os_free(buf);
+}
+
+
void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
{
conn->alert_level = level;
@@ -250,8 +275,7 @@
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_log(conn, "Record layer processing failed");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return -1;
}
@@ -265,14 +289,13 @@
if (ct == TLS_CONTENT_TYPE_ALERT) {
if (olen < 2) {
- wpa_printf(MSG_DEBUG, "TLSv1: Alert "
- "underflow");
+ tlsv1_server_log(conn, "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]);
+ tlsv1_server_log(conn, "Received alert %d:%d",
+ out_pos[0], out_pos[1]);
if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
/* Continue processing */
pos += used;
@@ -285,13 +308,23 @@
}
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
- wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
- "0x%x", pos[0]);
+ tlsv1_server_log(conn, "Unexpected content type 0x%x",
+ pos[0]);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if ((conn->test_flags &
+ (TLS_BREAK_VERIFY_DATA | TLS_BREAK_SRV_KEY_X_HASH |
+ TLS_BREAK_SRV_KEY_X_SIGNATURE)) &&
+ !conn->test_failure_reported) {
+ tlsv1_server_log(conn, "TEST-FAILURE: Client ApplData received after invalid handshake");
+ conn->test_failure_reported = 1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
out_pos += olen;
if (out_pos > out_end) {
wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
@@ -361,8 +394,15 @@
count = 0;
suites = conn->cipher_suites;
+ suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+ suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+ suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+ suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_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;
@@ -618,3 +658,125 @@
conn->session_ticket_cb = cb;
conn->session_ticket_cb_ctx = ctx;
}
+
+
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+ void (*cb)(void *ctx, const char *msg), void *ctx)
+{
+ conn->log_cb = cb;
+ conn->log_cb_ctx = ctx;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags)
+{
+ conn->test_flags = flags;
+}
+
+
+static const u8 test_tls_prime15[1] = {
+ 15
+};
+
+static const u8 test_tls_prime511b[64] = {
+ 0x50, 0xfb, 0xf1, 0xae, 0x01, 0xf1, 0xfe, 0xe6,
+ 0xe1, 0xae, 0xdc, 0x1e, 0xbe, 0xfb, 0x9e, 0x58,
+ 0x9a, 0xd7, 0x54, 0x9d, 0x6b, 0xb3, 0x78, 0xe2,
+ 0x39, 0x7f, 0x30, 0x01, 0x25, 0xa1, 0xf9, 0x7c,
+ 0x55, 0x0e, 0xa1, 0x15, 0xcc, 0x36, 0x34, 0xbb,
+ 0x6c, 0x8b, 0x64, 0x45, 0x15, 0x7f, 0xd3, 0xe7,
+ 0x31, 0xc8, 0x8e, 0x56, 0x8e, 0x95, 0xdc, 0xea,
+ 0x9e, 0xdf, 0xf7, 0x56, 0xdd, 0xb0, 0x34, 0xdb
+};
+
+static const u8 test_tls_prime767b[96] = {
+ 0x4c, 0xdc, 0xb8, 0x21, 0x20, 0x9d, 0xe8, 0xa3,
+ 0x53, 0xd9, 0x1c, 0x18, 0xc1, 0x3a, 0x58, 0x67,
+ 0xa7, 0x85, 0xf9, 0x28, 0x9b, 0xce, 0xc0, 0xd1,
+ 0x05, 0x84, 0x61, 0x97, 0xb2, 0x86, 0x1c, 0xd0,
+ 0xd1, 0x96, 0x23, 0x29, 0x8c, 0xc5, 0x30, 0x68,
+ 0x3e, 0xf9, 0x05, 0xba, 0x60, 0xeb, 0xdb, 0xee,
+ 0x2d, 0xdf, 0x84, 0x65, 0x49, 0x87, 0x90, 0x2a,
+ 0xc9, 0x8e, 0x34, 0x63, 0x6d, 0x9a, 0x2d, 0x32,
+ 0x1c, 0x46, 0xd5, 0x4e, 0x20, 0x20, 0x90, 0xac,
+ 0xd5, 0x48, 0x79, 0x99, 0x0c, 0xe6, 0xed, 0xbf,
+ 0x79, 0xc2, 0x47, 0x50, 0x95, 0x38, 0x38, 0xbc,
+ 0xde, 0xb0, 0xd2, 0xe8, 0x97, 0xcb, 0x22, 0xbb
+};
+
+static const u8 test_tls_prime58[128] = {
+ 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,
+ 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,
+ 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,
+ 0x03, 0xc1, 0xba, 0xc8, 0x25, 0xbe, 0x2d, 0xf3
+};
+
+static const u8 test_tls_non_prime[] = {
+ /*
+ * This is not a prime and the value has the following factors:
+ * 13736783488716579923 * 16254860191773456563 * 18229434976173670763 *
+ * 11112313018289079419 * 10260802278580253339 * 12394009491575311499 *
+ * 12419059668711064739 * 14317973192687985827 * 10498605410533203179 *
+ * 16338688760390249003 * 11128963991123878883 * 12990532258280301419 *
+ * 3
+ */
+ 0x0C, 0x8C, 0x36, 0x9C, 0x6F, 0x71, 0x2E, 0xA7,
+ 0xAB, 0x32, 0xD3, 0x0F, 0x68, 0x3D, 0xB2, 0x6D,
+ 0x81, 0xDD, 0xC4, 0x84, 0x0D, 0x9C, 0x6E, 0x36,
+ 0x29, 0x70, 0xF3, 0x1E, 0x9A, 0x42, 0x0B, 0x67,
+ 0x82, 0x6B, 0xB1, 0xF2, 0xAF, 0x55, 0x28, 0xE7,
+ 0xDB, 0x67, 0x6C, 0xF7, 0x6B, 0xAC, 0xAC, 0xE5,
+ 0xF7, 0x9F, 0xD4, 0x63, 0x55, 0x70, 0x32, 0x7C,
+ 0x70, 0xFB, 0xAF, 0xB8, 0xEB, 0x37, 0xCF, 0x3F,
+ 0xFE, 0x94, 0x73, 0xF9, 0x7A, 0xC7, 0x12, 0x2E,
+ 0x9B, 0xB4, 0x7D, 0x08, 0x60, 0x83, 0x43, 0x52,
+ 0x83, 0x1E, 0xA5, 0xFC, 0xFA, 0x87, 0x12, 0xF4,
+ 0x64, 0xE2, 0xCE, 0x71, 0x17, 0x72, 0xB6, 0xAB
+};
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p,
+ size_t *dh_p_len)
+{
+ *dh_p = conn->cred->dh_p;
+ *dh_p_len = conn->cred->dh_p_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conn->test_flags & TLS_DHE_PRIME_511B) {
+ tlsv1_server_log(conn, "TESTING: Use short 511-bit prime with DHE");
+ *dh_p = test_tls_prime511b;
+ *dh_p_len = sizeof(test_tls_prime511b);
+ } else if (conn->test_flags & TLS_DHE_PRIME_767B) {
+ tlsv1_server_log(conn, "TESTING: Use short 767-bit prime with DHE");
+ *dh_p = test_tls_prime767b;
+ *dh_p_len = sizeof(test_tls_prime767b);
+ } else if (conn->test_flags & TLS_DHE_PRIME_15) {
+ tlsv1_server_log(conn, "TESTING: Use bogus 15 \"prime\" with DHE");
+ *dh_p = test_tls_prime15;
+ *dh_p_len = sizeof(test_tls_prime15);
+ } else if (conn->test_flags & TLS_DHE_PRIME_58B) {
+ tlsv1_server_log(conn, "TESTING: Use short 58-bit prime in long container with DHE");
+ *dh_p = test_tls_prime58;
+ *dh_p_len = sizeof(test_tls_prime58);
+ } else if (conn->test_flags & TLS_DHE_NON_PRIME) {
+ tlsv1_server_log(conn, "TESTING: Use claim non-prime as the DHE prime");
+ *dh_p = test_tls_non_prime;
+ *dh_p_len = sizeof(test_tls_non_prime);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+}
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
index a18c69e..b2b28d1 100644
--- a/src/tls/tlsv1_server.h
+++ b/src/tls/tlsv1_server.h
@@ -45,4 +45,9 @@
tlsv1_server_session_ticket_cb cb,
void *ctx);
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+ void (*cb)(void *ctx, const char *msg), void *ctx);
+
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags);
+
#endif /* TLSV1_SERVER_H */
diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h
index 1f61533..96d79b3 100644
--- a/src/tls/tlsv1_server_i.h
+++ b/src/tls/tlsv1_server_i.h
@@ -51,13 +51,24 @@
tlsv1_server_session_ticket_cb session_ticket_cb;
void *session_ticket_cb_ctx;
+ void (*log_cb)(void *ctx, const char *msg);
+ void *log_cb_ctx;
+
int use_session_ticket;
u8 *dh_secret;
size_t dh_secret_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ u32 test_flags;
+ int test_failure_reported;
+#endif /* CONFIG_TESTING_OPTIONS */
};
+void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
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,
@@ -67,5 +78,7 @@
u8 description, size_t *out_len);
int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
const u8 *buf, size_t *len);
+void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p,
+ size_t *dh_p_len);
#endif /* TLSV1_SERVER_I_H */
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index 6f6539b..728e137 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -1,6 +1,6 @@
/*
* TLSv1 server - read handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -27,6 +27,25 @@
size_t *in_len);
+static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if ((conn->test_flags &
+ (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE |
+ TLS_DHE_PRIME_511B | TLS_DHE_PRIME_767B | TLS_DHE_PRIME_15 |
+ TLS_DHE_PRIME_58B | TLS_DHE_NON_PRIME)) &&
+ suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 &&
+ suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA &&
+ suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 &&
+ suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA &&
+ suite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA)
+ return 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return 0;
+}
+
+
static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
@@ -38,8 +57,8 @@
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_log(conn, "Expected Handshake; received content type 0x%x",
+ ct);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@@ -53,13 +72,13 @@
/* 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_log(conn, "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");
+ tlsv1_server_log(conn, "Received ClientHello");
pos++;
/* uint24 length */
len = WPA_GET_BE24(pos);
@@ -78,13 +97,13 @@
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);
+ tlsv1_server_log(conn, "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_log(conn, "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;
@@ -101,8 +120,8 @@
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));
+ tlsv1_server_log(conn, "Using TLS v%s",
+ tls_version_str(conn->rl.tls_version));
/* Random random */
if (end - pos < TLS_RANDOM_LEN)
@@ -137,6 +156,8 @@
cipher_suite = 0;
for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) {
+ if (testing_cipher_suite_filter(conn, conn->cipher_suites[i]))
+ continue;
c = pos;
for (j = 0; j < num_suites; j++) {
u16 tmp = WPA_GET_BE16(c);
@@ -149,8 +170,7 @@
}
pos += num_suites * 2;
if (!cipher_suite) {
- wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite "
- "available");
+ tlsv1_server_log(conn, "No supported cipher suite available");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_ILLEGAL_PARAMETER);
return -1;
@@ -180,16 +200,15 @@
compr_null_found = 1;
}
if (!compr_null_found) {
- wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL "
- "compression");
+ tlsv1_server_log(conn, "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);
+ tlsv1_server_log(conn, "Unexpected extra octet in the end of ClientHello: 0x%02x",
+ *pos);
goto decode_error;
}
@@ -198,12 +217,11 @@
ext_len = WPA_GET_BE16(pos);
pos += 2;
- wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello "
- "extensions", ext_len);
+ tlsv1_server_log(conn, "%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));
+ tlsv1_server_log(conn, "Invalid ClientHello extension list length %u (expected %u)",
+ ext_len, (unsigned int) (end - pos));
goto decode_error;
}
@@ -216,8 +234,7 @@
while (pos < end) {
if (end - pos < 2) {
- wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
- "extension_type field");
+ tlsv1_server_log(conn, "Invalid extension_type field");
goto decode_error;
}
@@ -225,8 +242,7 @@
pos += 2;
if (end - pos < 2) {
- wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
- "extension_data length field");
+ tlsv1_server_log(conn, "Invalid extension_data length field");
goto decode_error;
}
@@ -234,13 +250,12 @@
pos += 2;
if (end - pos < ext_len) {
- wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
- "extension_data field");
+ tlsv1_server_log(conn, "Invalid extension_data field");
goto decode_error;
}
- wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension "
- "type %u", ext_type);
+ tlsv1_server_log(conn, "ClientHello Extension type %u",
+ ext_type);
wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
"Extension data", pos, ext_len);
@@ -260,14 +275,13 @@
*in_len = end - in_data;
- wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to "
- "ServerHello");
+ tlsv1_server_log(conn, "ClientHello OK - proceed to ServerHello");
conn->state = SERVER_HELLO;
return 0;
decode_error:
- wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello");
+ tlsv1_server_log(conn, "Failed to decode ClientHello");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -284,8 +298,8 @@
int reason;
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
- wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
- "received content type 0x%x", ct);
+ tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+ ct);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@@ -295,8 +309,8 @@
left = *in_len;
if (left < 4) {
- wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
- "(len=%lu)", (unsigned long) left);
+ tlsv1_server_log(conn, "Too short Certificate message (len=%lu)",
+ (unsigned long) left);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -308,9 +322,8 @@
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_log(conn, "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;
@@ -318,8 +331,7 @@
if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
if (conn->verify_peer) {
- wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
- "Certificate");
+ tlsv1_server_log(conn, "Client did not include Certificate");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@@ -329,17 +341,15 @@
in_len);
}
if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
- wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
- "message %d (expected Certificate/"
- "ClientKeyExchange)", type);
+ tlsv1_server_log(conn, "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);
+ tlsv1_server_log(conn, "Received Certificate (certificate_list len %lu)",
+ (unsigned long) len);
/*
* opaque ASN.1Cert<2^24-1>;
@@ -352,8 +362,8 @@
end = pos + len;
if (end - pos < 3) {
- wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
- "(left=%lu)", (unsigned long) left);
+ tlsv1_server_log(conn, "Too short Certificate (left=%lu)",
+ (unsigned long) left);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -363,10 +373,9 @@
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_log(conn, "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;
@@ -375,8 +384,7 @@
idx = 0;
while (pos < end) {
if (end - pos < 3) {
- wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
- "certificate_list");
+ tlsv1_server_log(conn, "Failed to parse certificate_list");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
x509_certificate_chain_free(chain);
@@ -387,25 +395,23 @@
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_log(conn, "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);
+ tlsv1_server_log(conn, "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_log(conn, "Failed to parse the certificate");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_BAD_CERTIFICATE);
x509_certificate_chain_free(chain);
@@ -415,8 +421,7 @@
cert = x509_certificate_parse(pos, cert_len);
if (cert == NULL) {
- wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
- "the certificate");
+ tlsv1_server_log(conn, "Failed to parse the certificate");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_BAD_CERTIFICATE);
x509_certificate_chain_free(chain);
@@ -436,8 +441,8 @@
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);
+ tlsv1_server_log(conn, "Server certificate chain validation failed (reason=%d)",
+ reason);
switch (reason) {
case X509_VALIDATE_BAD_CERTIFICATE:
tls_reason = TLS_ALERT_BAD_CERTIFICATE;
@@ -494,9 +499,8 @@
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_log(conn, "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;
@@ -539,15 +543,13 @@
}
if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
- wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
- "length %lu", (unsigned long) outlen);
+ tlsv1_server_log(conn, "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");
+ tlsv1_server_log(conn, "Client version in ClientKeyExchange does not match with version in ClientHello");
use_random = 1;
}
@@ -582,7 +584,7 @@
}
-static int tls_process_client_key_exchange_dh_anon(
+static int tls_process_client_key_exchange_dh(
struct tlsv1_server *conn, const u8 *pos, const u8 *end)
{
const u8 *dh_yc;
@@ -590,6 +592,8 @@
u8 *shared;
size_t shared_len;
int res;
+ const u8 *dh_p;
+ size_t dh_p_len;
/*
* struct {
@@ -600,6 +604,7 @@
* } ClientDiffieHellmanPublic;
*/
+ tlsv1_server_log(conn, "ClientDiffieHellmanPublic received");
wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
pos, end - pos);
@@ -612,8 +617,7 @@
}
if (end - pos < 3) {
- wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value "
- "length");
+ tlsv1_server_log(conn, "Invalid client public value length");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -623,8 +627,8 @@
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_log(conn, "Client public value overflow (length %d)",
+ dh_yc_len);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -641,7 +645,9 @@
return -1;
}
- shared_len = conn->cred->dh_p_len;
+ tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
+ shared_len = dh_p_len;
shared = os_malloc(shared_len);
if (shared == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
@@ -653,8 +659,7 @@
/* 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,
+ conn->dh_secret_len, dh_p, dh_p_len,
shared, &shared_len)) {
os_free(shared);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -695,8 +700,8 @@
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_log(conn, "Expected Handshake; received content type 0x%x",
+ ct);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@@ -706,8 +711,8 @@
left = *in_len;
if (left < 4) {
- wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange "
- "(Left=%lu)", (unsigned long) left);
+ tlsv1_server_log(conn, "Too short ClientKeyExchange (Left=%lu)",
+ (unsigned long) left);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -719,9 +724,8 @@
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_log(conn, "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;
@@ -730,14 +734,14 @@
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_log(conn, "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");
+ tlsv1_server_log(conn, "Received ClientKeyExchange");
wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
@@ -747,11 +751,11 @@
else
keyx = suite->key_exchange;
- if (keyx == TLS_KEY_X_DH_anon &&
- tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0)
+ if ((keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) &&
+ tls_process_client_key_exchange_dh(conn, pos, end) < 0)
return -1;
- if (keyx != TLS_KEY_X_DH_anon &&
+ if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA &&
tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
return -1;
@@ -769,15 +773,14 @@
const u8 *pos, *end;
size_t left, len;
u8 type;
- size_t hlen, buflen;
- u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf;
+ size_t hlen;
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
- u16 slen;
+ u8 alert;
if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
if (conn->verify_peer) {
- wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
- "CertificateVerify");
+ tlsv1_server_log(conn, "Client did not include CertificateVerify");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@@ -788,8 +791,8 @@
}
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
- wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
- "received content type 0x%x", ct);
+ tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+ ct);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@@ -799,8 +802,8 @@
left = *in_len;
if (left < 4) {
- wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify "
- "message (len=%lu)", (unsigned long) left);
+ tlsv1_server_log(conn, "Too short CertificateVerify message (len=%lu)",
+ (unsigned long) left);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -812,9 +815,8 @@
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_log(conn, "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;
@@ -823,14 +825,14 @@
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_log(conn, "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");
+ tlsv1_server_log(conn, "Received CertificateVerify");
/*
* struct {
@@ -917,89 +919,12 @@
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);
+ if (tls_verify_signature(conn->rl.tls_version, conn->client_rsa_key,
+ hash, hlen, pos, end - pos, &alert) < 0) {
+ tlsv1_server_log(conn, "Invalid Signature in CertificateVerify");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
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;
@@ -1017,8 +942,8 @@
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_log(conn, "Expected ChangeCipherSpec; received content type 0x%x",
+ ct);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@@ -1028,21 +953,21 @@
left = *in_len;
if (left < 1) {
- wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+ tlsv1_server_log(conn, "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_log(conn, "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");
+ tlsv1_server_log(conn, "Received ChangeCipherSpec");
if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
"for record layer");
@@ -1067,9 +992,48 @@
u8 verify_data[TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+#ifdef CONFIG_TESTING_OPTIONS
+ if ((conn->test_flags &
+ (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) &&
+ !conn->test_failure_reported) {
+ tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after invalid ServerKeyExchange");
+ conn->test_failure_reported = 1;
+ }
+
+ if ((conn->test_flags & TLS_DHE_PRIME_15) &&
+ !conn->test_failure_reported) {
+ tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after bogus DHE \"prime\" 15");
+ conn->test_failure_reported = 1;
+ }
+
+ if ((conn->test_flags & TLS_DHE_PRIME_58B) &&
+ !conn->test_failure_reported) {
+ tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after short 58-bit DHE prime in long container");
+ conn->test_failure_reported = 1;
+ }
+
+ if ((conn->test_flags & TLS_DHE_PRIME_511B) &&
+ !conn->test_failure_reported) {
+ tlsv1_server_log(conn, "TEST-WARNING: Client Finished received after short 511-bit DHE prime (insecure)");
+ conn->test_failure_reported = 1;
+ }
+
+ if ((conn->test_flags & TLS_DHE_PRIME_767B) &&
+ !conn->test_failure_reported) {
+ tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after 767-bit DHE prime (relatively insecure)");
+ conn->test_failure_reported = 1;
+ }
+
+ if ((conn->test_flags & TLS_DHE_NON_PRIME) &&
+ !conn->test_failure_reported) {
+ tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after non-prime claimed as DHE prime");
+ conn->test_failure_reported = 1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
- wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
- "received content type 0x%x", ct);
+ tlsv1_server_log(conn, "Expected Finished; received content type 0x%x",
+ ct);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@@ -1079,9 +1043,8 @@
left = *in_len;
if (left < 4) {
- wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
- "Finished",
- (unsigned long) left);
+ tlsv1_server_log(conn, "Too short record (left=%lu) forFinished",
+ (unsigned long) left);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
@@ -1101,18 +1064,16 @@
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_log(conn, "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_log(conn, "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;
@@ -1174,19 +1135,18 @@
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");
+ if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+ tlsv1_server_log(conn, "Mismatch in verify_data");
return -1;
}
- wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+ tlsv1_server_log(conn, "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");
+ tlsv1_server_log(conn, "Abbreviated handshake completed successfully");
conn->state = ESTABLISHED;
} else {
/* Full handshake */
@@ -1202,13 +1162,12 @@
{
if (ct == TLS_CONTENT_TYPE_ALERT) {
if (*len < 2) {
- wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+ tlsv1_server_log(conn, "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]);
+ tlsv1_server_log(conn, "Received alert %d:%d", buf[0], buf[1]);
*len = 2;
conn->state = FAILED;
return -1;
@@ -1240,9 +1199,8 @@
return -1;
break;
default:
- wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
- "while processing received message",
- conn->state);
+ tlsv1_server_log(conn, "Unexpected state %d while processing received message",
+ conn->state);
return -1;
}
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index 6d8e55e..15e6692 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -1,6 +1,6 @@
/*
* TLSv1 server - write handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -48,7 +48,7 @@
pos = *msgpos;
- wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello");
+ tlsv1_server_log(conn, "Send ServerHello");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
@@ -104,8 +104,7 @@
conn->client_random, conn->server_random,
conn->master_secret);
if (res < 0) {
- wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
- "indicated failure");
+ tlsv1_server_log(conn, "SessionTicket callback indicated failure");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_HANDSHAKE_FAILURE);
return -1;
@@ -170,7 +169,7 @@
pos = *msgpos;
- wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+ tlsv1_server_log(conn, "Send Certificate");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
@@ -245,10 +244,12 @@
{
tls_key_exchange keyx;
const struct tls_cipher_suite *suite;
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos, *rhdr, *hs_start, *hs_length, *server_params;
size_t rlen;
u8 *dh_ys;
size_t dh_ys_len;
+ const u8 *dh_p;
+ size_t dh_p_len;
suite = tls_get_cipher_suite(conn->rl.cipher_suite);
if (suite == NULL)
@@ -261,8 +262,7 @@
return 0;
}
- if (keyx != TLS_KEY_X_DH_anon) {
- /* TODO? */
+ if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA) {
wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
"supported with key exchange type %d", keyx);
return -1;
@@ -275,8 +275,10 @@
return -1;
}
+ tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
os_free(conn->dh_secret);
- conn->dh_secret_len = conn->cred->dh_p_len;
+ conn->dh_secret_len = 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 "
@@ -295,8 +297,7 @@
return -1;
}
- if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) >
- 0)
+ if (os_memcmp(conn->dh_secret, dh_p, conn->dh_secret_len) > 0)
conn->dh_secret[0] = 0; /* make sure secret < p */
pos = conn->dh_secret;
@@ -311,7 +312,7 @@
conn->dh_secret, conn->dh_secret_len);
/* Ys = g^secret mod p */
- dh_ys_len = conn->cred->dh_p_len;
+ dh_ys_len = dh_p_len;
dh_ys = os_malloc(dh_ys_len);
if (dh_ys == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
@@ -322,8 +323,7 @@
}
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)) {
+ dh_p, dh_p_len, dh_ys, &dh_ys_len)) {
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(dh_ys);
@@ -354,7 +354,7 @@
pos = *msgpos;
- wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange");
+ tlsv1_server_log(conn, "Send ServerKeyExchange");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
@@ -369,8 +369,9 @@
pos += 3;
/* body - ServerDHParams */
+ server_params = pos;
/* dh_p */
- if (pos + 2 + conn->cred->dh_p_len > end) {
+ if (pos + 2 + dh_p_len > end) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
"dh_p");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -378,10 +379,10 @@
os_free(dh_ys);
return -1;
}
- WPA_PUT_BE16(pos, conn->cred->dh_p_len);
+ WPA_PUT_BE16(pos, dh_p_len);
pos += 2;
- os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len);
- pos += conn->cred->dh_p_len;
+ os_memcpy(pos, dh_p, dh_p_len);
+ pos += dh_p_len;
/* dh_g */
if (pos + 2 + conn->cred->dh_g_len > end) {
@@ -412,6 +413,138 @@
pos += dh_ys_len;
os_free(dh_ys);
+ /*
+ * select (SignatureAlgorithm)
+ * { case anonymous: struct { };
+ * case rsa:
+ * digitally-signed struct {
+ * opaque md5_hash[16];
+ * opaque sha_hash[20];
+ * };
+ * case dsa:
+ * digitally-signed struct {
+ * opaque sha_hash[20];
+ * };
+ * } Signature;
+ *
+ * md5_hash
+ * MD5(ClientHello.random + ServerHello.random + ServerParams);
+ *
+ * sha_hash
+ * SHA(ClientHello.random + ServerHello.random + ServerParams);
+ */
+
+ if (keyx == TLS_KEY_X_DHE_RSA) {
+ u8 hash[100];
+ u8 *signed_start;
+ size_t clen;
+ int hlen;
+
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+ hlen = tlsv12_key_x_server_params_hash(
+ conn->rl.tls_version, conn->client_random,
+ conn->server_random, server_params,
+ pos - server_params, hash + 19);
+
+ /*
+ * RFC 5246, 4.7:
+ * TLS v1.2 adds explicit indication of the used
+ * signature and hash algorithms.
+ *
+ * struct {
+ * HashAlgorithm hash;
+ * SignatureAlgorithm signature;
+ * } SignatureAndHashAlgorithm;
+ */
+ if (hlen < 0 || pos + 2 > end) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ *pos++ = TLS_HASH_ALG_SHA256;
+ *pos++ = TLS_SIGN_ALG_RSA;
+
+ /*
+ * 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
+ */
+ 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 /* CONFIG_TLSV12 */
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+#endif /* CONFIG_TLSV12 */
+ } else {
+ hlen = tls_key_x_server_params_hash(
+ conn->rl.tls_version, conn->client_random,
+ conn->server_random, server_params,
+ pos - server_params, hash);
+ }
+
+ if (hlen < 0) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "TLS: ServerKeyExchange signed_params hash",
+ hash, hlen);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conn->test_flags & TLS_BREAK_SRV_KEY_X_HASH) {
+ tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params hash");
+ hash[hlen - 1] ^= 0x80;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /*
+ * 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)");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ WPA_PUT_BE16(signed_start, clen);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conn->test_flags & TLS_BREAK_SRV_KEY_X_SIGNATURE) {
+ tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params signature");
+ pos[clen - 1] ^= 0x80;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ pos += clen;
+ }
+
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
@@ -445,7 +578,7 @@
pos = *msgpos;
- wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
+ tlsv1_server_log(conn, "Send CertificateRequest");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
@@ -505,7 +638,7 @@
size_t rlen;
u8 payload[4];
- wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
+ tlsv1_server_log(conn, "Send ServerHelloDone");
/* opaque fragment[TLSPlaintext.length] */
@@ -541,7 +674,7 @@
size_t rlen;
u8 payload[1];
- wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+ tlsv1_server_log(conn, "Send ChangeCipherSpec");
payload[0] = TLS_CHANGE_CIPHER_SPEC;
@@ -578,7 +711,7 @@
pos = *msgpos;
- wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+ tlsv1_server_log(conn, "Send Finished");
/* Encrypted Handshake Message: Finished */
@@ -635,6 +768,12 @@
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conn->test_flags & TLS_BREAK_VERIFY_DATA) {
+ tlsv1_server_log(conn, "TESTING: Break verify_data (server)");
+ verify_data[1 + 3 + 1] ^= 0x80;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
/* Handshake */
pos = hs_start = verify_data;
@@ -736,7 +875,7 @@
*out_len = pos - msg;
- wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
+ tlsv1_server_log(conn, "Handshake completed successfully");
conn->state = ESTABLISHED;
return msg;
@@ -755,8 +894,8 @@
/* Abbreviated handshake was already completed. */
return NULL;
}
- wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
- "generating reply", conn->state);
+ tlsv1_server_log(conn, "Unexpected state %d while generating reply",
+ conn->state);
return NULL;
}
}
@@ -767,7 +906,7 @@
{
u8 *alert, *pos, *length;
- wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+ tlsv1_server_log(conn, "Send Alert(%d:%d)", level, description);
*out_len = 0;
alert = os_malloc(10);
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index 06540bf..e1e4df8 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -1348,7 +1348,8 @@
wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
/* TODO: parse UniqueIdentifier ::= BIT STRING */
- if (hdr.payload + hdr.length == end)
+ pos = hdr.payload + hdr.length;
+ if (pos == end)
return 0;
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
@@ -1366,7 +1367,8 @@
wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
/* TODO: parse UniqueIdentifier ::= BIT STRING */
- if (hdr.payload + hdr.length == end)
+ pos = hdr.payload + hdr.length;
+ if (pos == end)
return 0;
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
@@ -1774,13 +1776,22 @@
}
if (hdr.length != hash_len ||
- os_memcmp(hdr.payload, hash, hdr.length) != 0) {
+ os_memcmp_const(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;
}
+ if (hdr.payload + hdr.length < data + data_len) {
+ wpa_hexdump(MSG_INFO,
+ "X509: Extra data after certificate signature hash",
+ hdr.payload + hdr.length,
+ data + data_len - hdr.payload - hdr.length);
+ os_free(data);
+ return -1;
+ }
+
os_free(data);
wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
diff --git a/src/utils/Makefile b/src/utils/Makefile
index 940b4d8..8aad813 100644
--- a/src/utils/Makefile
+++ b/src/utils/Makefile
@@ -1,7 +1,7 @@
all: libutils.a
clean:
- rm -f *~ *.o *.d libutils.a
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a
install:
@echo Nothing to be made.
@@ -11,6 +11,7 @@
#CFLAGS += -DWPA_TRACE
CFLAGS += -DCONFIG_IPV6
+CFLAGS += -DCONFIG_DEBUG_FILE
LIB_OBJS= \
base64.o \
diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c
new file mode 100644
index 0000000..a066392
--- /dev/null
+++ b/src/utils/browser-android.c
@@ -0,0 +1,117 @@
+/*
+ * Hotspot 2.0 client - Web browser using Android browser
+ * 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 "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+ int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+ wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+ "complete");
+ eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+ struct browser_data *data = ctx;
+ struct wpabuf *resp;
+ const char *url;
+ int done = 0;
+
+ url = http_request_get_uri(req);
+ wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+ if (os_strcmp(url, "/") == 0) {
+ data->success = 1;
+ done = 1;
+ } else if (os_strncmp(url, "/osu/", 5) == 0) {
+ data->success = atoi(url + 5);
+ done = 1;
+ }
+
+ resp = wpabuf_alloc(1);
+ if (resp == NULL) {
+ http_request_deinit(req);
+ if (done)
+ eloop_terminate();
+ return;
+ }
+
+ if (done) {
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+ }
+
+ http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+ char cmd[2000];
+ int ret;
+ struct http_server *http;
+ struct in_addr addr;
+ struct browser_data data;
+
+ wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+ os_memset(&data, 0, sizeof(data));
+
+ ret = os_snprintf(cmd, sizeof(cmd),
+ "am start -a android.intent.action.VIEW -d '%s' "
+ "-n com.android.browser/.BrowserActivity", url);
+ if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+ wpa_printf(MSG_ERROR, "Too long URL");
+ return -1;
+ }
+
+ if (eloop_init() < 0) {
+ wpa_printf(MSG_ERROR, "eloop_init failed");
+ return -1;
+ }
+ addr.s_addr = htonl((127 << 24) | 1);
+ http = http_server_init(&addr, 12345, http_req, &data);
+ if (http == NULL) {
+ wpa_printf(MSG_ERROR, "http_server_init failed");
+ eloop_destroy();
+ return -1;
+ }
+
+ if (system(cmd) != 0) {
+ wpa_printf(MSG_INFO, "Failed to launch Android browser");
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+ return -1;
+ }
+
+ eloop_register_timeout(30, 0, browser_timeout, &data, NULL);
+ eloop_run();
+ eloop_cancel_timeout(browser_timeout, &data, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+
+ wpa_printf(MSG_INFO, "Closing Android browser");
+ if (system("input keyevent 3") != 0) {
+ wpa_printf(MSG_INFO, "Failed to inject keyevent");
+ }
+
+ return data.success;
+}
diff --git a/src/utils/browser-system.c b/src/utils/browser-system.c
new file mode 100644
index 0000000..2884d34
--- /dev/null
+++ b/src/utils/browser-system.c
@@ -0,0 +1,112 @@
+/*
+ * Hotspot 2.0 client - Web browser using system browser
+ * 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 "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+ int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+ wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+ "complete");
+ eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+ struct browser_data *data = ctx;
+ struct wpabuf *resp;
+ const char *url;
+ int done = 0;
+
+ url = http_request_get_uri(req);
+ wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+ if (os_strcmp(url, "/") == 0) {
+ data->success = 1;
+ done = 1;
+ } else if (os_strncmp(url, "/osu/", 5) == 0) {
+ data->success = atoi(url + 5);
+ done = 1;
+ }
+
+ resp = wpabuf_alloc(1);
+ if (resp == NULL) {
+ http_request_deinit(req);
+ if (done)
+ eloop_terminate();
+ return;
+ }
+
+ if (done) {
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+ }
+
+ http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+ char cmd[2000];
+ int ret;
+ struct http_server *http;
+ struct in_addr addr;
+ struct browser_data data;
+
+ wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+ os_memset(&data, 0, sizeof(data));
+
+ ret = os_snprintf(cmd, sizeof(cmd), "x-www-browser '%s' &", url);
+ if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+ wpa_printf(MSG_ERROR, "Too long URL");
+ return -1;
+ }
+
+ if (eloop_init() < 0) {
+ wpa_printf(MSG_ERROR, "eloop_init failed");
+ return -1;
+ }
+ addr.s_addr = htonl((127 << 24) | 1);
+ http = http_server_init(&addr, 12345, http_req, &data);
+ if (http == NULL) {
+ wpa_printf(MSG_ERROR, "http_server_init failed");
+ eloop_destroy();
+ return -1;
+ }
+
+ if (system(cmd) != 0) {
+ wpa_printf(MSG_INFO, "Failed to launch browser");
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+ return -1;
+ }
+
+ eloop_register_timeout(120, 0, browser_timeout, &data, NULL);
+ eloop_run();
+ eloop_cancel_timeout(browser_timeout, &data, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+
+ /* TODO: Close browser */
+
+ return data.success;
+}
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
new file mode 100644
index 0000000..eeb8f65
--- /dev/null
+++ b/src/utils/browser-wpadebug.c
@@ -0,0 +1,123 @@
+/*
+ * Hotspot 2.0 client - Web browser using wpadebug on Android
+ * 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 "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+ int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+ wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+ "complete");
+ eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+ struct browser_data *data = ctx;
+ struct wpabuf *resp;
+ const char *url;
+ int done = 0;
+
+ url = http_request_get_uri(req);
+ wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+ if (os_strcmp(url, "/") == 0) {
+ data->success = 1;
+ done = 1;
+ } else if (os_strncmp(url, "/osu/", 5) == 0) {
+ data->success = atoi(url + 5);
+ done = 1;
+ }
+
+ resp = wpabuf_alloc(100);
+ if (resp == NULL) {
+ http_request_deinit(req);
+ if (done)
+ eloop_terminate();
+ return;
+ }
+ wpabuf_put_str(resp, "User input completed");
+
+ if (done) {
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+ }
+
+ http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+ char cmd[2000];
+ int ret;
+ struct http_server *http;
+ struct in_addr addr;
+ struct browser_data data;
+
+ wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url);
+
+ os_memset(&data, 0, sizeof(data));
+
+ ret = os_snprintf(cmd, sizeof(cmd),
+ "am start -a android.action.MAIN "
+ "-c android.intent.category.LAUNCHER "
+ "-n w1.fi.wpadebug/.WpaWebViewActivity "
+ "-e w1.fi.wpadebug.URL '%s'", url);
+ if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
+ wpa_printf(MSG_ERROR, "Too long URL");
+ return -1;
+ }
+
+ if (eloop_init() < 0) {
+ wpa_printf(MSG_ERROR, "eloop_init failed");
+ return -1;
+ }
+ addr.s_addr = htonl((127 << 24) | 1);
+ http = http_server_init(&addr, 12345, http_req, &data);
+ if (http == NULL) {
+ wpa_printf(MSG_ERROR, "http_server_init failed");
+ eloop_destroy();
+ return -1;
+ }
+
+ if (system(cmd) != 0) {
+ wpa_printf(MSG_INFO, "Failed to launch wpadebug browser");
+ eloop_cancel_timeout(browser_timeout, NULL, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+ return -1;
+ }
+
+ eloop_register_timeout(300, 0, browser_timeout, &data, NULL);
+ eloop_run();
+ eloop_cancel_timeout(browser_timeout, &data, NULL);
+ http_server_deinit(http);
+ eloop_destroy();
+
+ wpa_printf(MSG_INFO, "Closing Android browser");
+ if (system("am start -a android.action.MAIN "
+ "-c android.intent.category.LAUNCHER "
+ "-n w1.fi.wpadebug/.WpaWebViewActivity "
+ "-e w1.fi.wpadebug.URL FINISH") != 0) {
+ wpa_printf(MSG_INFO, "Failed to close wpadebug browser");
+ }
+
+ return data.success;
+}
diff --git a/src/utils/browser.c b/src/utils/browser.c
new file mode 100644
index 0000000..9cf6152
--- /dev/null
+++ b/src/utils/browser.c
@@ -0,0 +1,219 @@
+/*
+ * Hotspot 2.0 client - Web browser using WebKit
+ * 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 <webkit/webkit.h>
+
+#include "common.h"
+#include "browser.h"
+
+
+struct browser_context {
+ GtkWidget *win;
+ int success;
+ int progress;
+ char *hover_link;
+ char *title;
+};
+
+static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx)
+{
+ wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__);
+ gtk_main_quit();
+}
+
+
+static void browser_update_title(struct browser_context *ctx)
+{
+ char buf[100];
+
+ if (ctx->hover_link) {
+ gtk_window_set_title(GTK_WINDOW(ctx->win), ctx->hover_link);
+ return;
+ }
+
+ if (ctx->progress == 100) {
+ gtk_window_set_title(GTK_WINDOW(ctx->win),
+ ctx->title ? ctx->title :
+ "Hotspot 2.0 client");
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "[%d%%] %s", ctx->progress,
+ ctx->title ? ctx->title : "Hotspot 2.0 client");
+ gtk_window_set_title(GTK_WINDOW(ctx->win), buf);
+}
+
+
+static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec,
+ struct browser_context *ctx)
+{
+ ctx->progress = 100 * webkit_web_view_get_progress(view);
+ wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
+ ctx->progress);
+ browser_update_title(ctx);
+}
+
+
+static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec,
+ struct browser_context *ctx)
+{
+ int status = webkit_web_view_get_load_status(view);
+ wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s",
+ __func__, status, webkit_web_view_get_uri(view));
+}
+
+
+static void view_cb_resource_request_starting(WebKitWebView *view,
+ WebKitWebFrame *frame,
+ WebKitWebResource *res,
+ WebKitNetworkRequest *req,
+ WebKitNetworkResponse *resp,
+ struct browser_context *ctx)
+{
+ const gchar *uri = webkit_network_request_get_uri(req);
+ wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+ if (g_str_has_suffix(uri, "/favicon.ico"))
+ webkit_network_request_set_uri(req, "about:blank");
+ if (g_str_has_prefix(uri, "osu://")) {
+ ctx->success = atoi(uri + 6);
+ gtk_main_quit();
+ }
+ if (g_str_has_prefix(uri, "http://localhost:12345")) {
+ /*
+ * This is used as a special trigger to indicate that the
+ * user exchange has been completed.
+ */
+ ctx->success = 1;
+ gtk_main_quit();
+ }
+}
+
+
+static gboolean view_cb_mime_type_policy_decision(
+ WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *req,
+ gchar *mime, WebKitWebPolicyDecision *policy,
+ struct browser_context *ctx)
+{
+ wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime);
+
+ if (!webkit_web_view_can_show_mime_type(view, mime)) {
+ webkit_web_policy_decision_download(policy);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean view_cb_download_requested(WebKitWebView *view,
+ WebKitDownload *dl,
+ struct browser_context *ctx)
+{
+ const gchar *uri;
+ uri = webkit_download_get_uri(dl);
+ wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+ return FALSE;
+}
+
+
+static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title,
+ gchar *uri, struct browser_context *ctx)
+{
+ wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title,
+ uri);
+ os_free(ctx->hover_link);
+ if (uri)
+ ctx->hover_link = os_strdup(uri);
+ else
+ ctx->hover_link = NULL;
+
+ browser_update_title(ctx);
+}
+
+
+static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame,
+ const char *title,
+ struct browser_context *ctx)
+{
+ wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
+ os_free(ctx->title);
+ ctx->title = os_strdup(title);
+ browser_update_title(ctx);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+ GtkWidget *scroll;
+ SoupSession *s;
+ WebKitWebView *view;
+ WebKitWebSettings *settings;
+ struct browser_context ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ if (!gtk_init_check(NULL, NULL))
+ return -1;
+
+ s = webkit_get_default_session();
+ g_object_set(G_OBJECT(s), "ssl-ca-file",
+ "/etc/ssl/certs/ca-certificates.crt", NULL);
+ g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL);
+
+ ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_wmclass(GTK_WINDOW(ctx.win), "Hotspot 2.0 client",
+ "Hotspot 2.0 client");
+ gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
+
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+ GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+
+ g_signal_connect(G_OBJECT(ctx.win), "destroy",
+ G_CALLBACK(win_cb_destroy), &ctx);
+
+ view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ g_signal_connect(G_OBJECT(view), "notify::progress",
+ G_CALLBACK(view_cb_notify_progress), &ctx);
+ g_signal_connect(G_OBJECT(view), "notify::load-status",
+ G_CALLBACK(view_cb_notify_load_status), &ctx);
+ g_signal_connect(G_OBJECT(view), "resource-request-starting",
+ G_CALLBACK(view_cb_resource_request_starting), &ctx);
+ g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested",
+ G_CALLBACK(view_cb_mime_type_policy_decision), &ctx);
+ g_signal_connect(G_OBJECT(view), "download-requested",
+ G_CALLBACK(view_cb_download_requested), &ctx);
+ g_signal_connect(G_OBJECT(view), "hovering-over-link",
+ G_CALLBACK(view_cb_hovering_over_link), &ctx);
+ g_signal_connect(G_OBJECT(view), "title-changed",
+ G_CALLBACK(view_cb_title_changed), &ctx);
+
+ gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+ gtk_widget_show_all(ctx.win);
+
+ settings = webkit_web_view_get_settings(view);
+ g_object_set(G_OBJECT(settings), "user-agent",
+ "Mozilla/5.0 (X11; U; Unix; en-US) "
+ "AppleWebKit/537.15 (KHTML, like Gecko) "
+ "hs20-client/1.0", NULL);
+ g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL);
+
+ webkit_web_view_load_uri(view, url);
+
+ gtk_main();
+ gtk_widget_destroy(ctx.win);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ free(ctx.hover_link);
+ free(ctx.title);
+ return ctx.success;
+}
diff --git a/src/utils/browser.h b/src/utils/browser.h
new file mode 100644
index 0000000..aaa0eed
--- /dev/null
+++ b/src/utils/browser.h
@@ -0,0 +1,21 @@
+/*
+ * Hotspot 2.0 client - Web browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BROWSER_H
+#define BROWSER_H
+
+#ifdef CONFIG_NO_BROWSER
+static inline int hs20_web_browser(const char *url)
+{
+ return -1;
+}
+#else /* CONFIG_NO_BROWSER */
+int hs20_web_browser(const char *url);
+#endif /* CONFIG_NO_BROWSER */
+
+#endif /* BROWSER_H */
diff --git a/src/utils/common.c b/src/utils/common.c
index bf326cd..5b017e5 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -350,7 +350,7 @@
size_t i;
for (i = 0; i < len; i++) {
- if (txt + 4 > end)
+ if (txt + 4 >= end)
break;
switch (data[i]) {
@@ -400,7 +400,7 @@
int val;
while (*pos) {
- if (len == maxlen)
+ if (len + 1 >= maxlen)
break;
switch (*pos) {
case '\\':
@@ -468,6 +468,8 @@
break;
}
}
+ if (maxlen > len)
+ buf[len] = '\0';
return len;
}
@@ -576,6 +578,21 @@
}
+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)
@@ -622,3 +639,210 @@
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;
+}
+
+
+void str_clear_free(char *str)
+{
+ if (str) {
+ size_t len = os_strlen(str);
+ os_memset(str, 0, len);
+ os_free(str);
+ }
+}
+
+
+void bin_clear_free(void *bin, size_t len)
+{
+ if (bin) {
+ os_memset(bin, 0, len);
+ os_free(bin);
+ }
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 29f0b95..2bc8fe1 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -459,6 +459,14 @@
#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);
@@ -485,6 +493,7 @@
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);
@@ -505,6 +514,31 @@
#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]))
+
+
+void str_clear_free(char *str);
+void bin_clear_free(void *bin, size_t len);
+
+
/*
* 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
diff --git a/src/utils/edit.c b/src/utils/edit.c
index 177ecf4..d340bfa 100644
--- a/src/utils/edit.c
+++ b/src/utils/edit.c
@@ -14,7 +14,7 @@
#include "list.h"
#include "edit.h"
-#define CMD_BUF_LEN 256
+#define CMD_BUF_LEN 4096
static char cmdbuf[CMD_BUF_LEN];
static int cmdbuf_pos = 0;
static int cmdbuf_len = 0;
diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c
index a095ea6..13173cb 100644
--- a/src/utils/edit_simple.c
+++ b/src/utils/edit_simple.c
@@ -13,7 +13,7 @@
#include "edit.h"
-#define CMD_BUF_LEN 256
+#define CMD_BUF_LEN 4096
static char cmdbuf[CMD_BUF_LEN];
static int cmdbuf_pos = 0;
static const char *ps2 = NULL;
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index f62e2b7..0da6de4 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -7,17 +7,28 @@
*/
#include "includes.h"
+#include <assert.h>
#include "common.h"
#include "trace.h"
#include "list.h"
#include "eloop.h"
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
+#error Do not define both of poll and epoll
+#endif
+
+#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL)
+#define CONFIG_ELOOP_SELECT
+#endif
+
#ifdef CONFIG_ELOOP_POLL
-#include <assert.h>
#include <poll.h>
#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+#include <sys/epoll.h>
+#endif /* CONFIG_ELOOP_EPOLL */
struct eloop_sock {
int sock;
@@ -31,7 +42,7 @@
struct eloop_timeout {
struct dl_list list;
- struct os_time time;
+ struct os_reltime time;
void *eloop_data;
void *user_data;
eloop_timeout_handler handler;
@@ -50,7 +61,11 @@
struct eloop_sock_table {
int count;
struct eloop_sock *table;
+#ifdef CONFIG_ELOOP_EPOLL
+ eloop_event_type type;
+#else /* CONFIG_ELOOP_EPOLL */
int changed;
+#endif /* CONFIG_ELOOP_EPOLL */
};
struct eloop_data {
@@ -63,6 +78,13 @@
struct pollfd *pollfds;
struct pollfd **pollfds_map;
#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+ int epollfd;
+ int epoll_max_event_num;
+ int epoll_max_fd;
+ struct eloop_sock *epoll_table;
+ struct epoll_event *epoll_events;
+#endif /* CONFIG_ELOOP_EPOLL */
struct eloop_sock_table readers;
struct eloop_sock_table writers;
struct eloop_sock_table exceptions;
@@ -75,7 +97,6 @@
int pending_terminate;
int terminate;
- int reader_table_changed;
};
static struct eloop_data eloop;
@@ -128,6 +149,17 @@
{
os_memset(&eloop, 0, sizeof(eloop));
dl_list_init(&eloop.timeout);
+#ifdef CONFIG_ELOOP_EPOLL
+ eloop.epollfd = epoll_create1(0);
+ if (eloop.epollfd < 0) {
+ wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
+ __func__, strerror(errno));
+ return -1;
+ }
+ eloop.readers.type = EVENT_TYPE_READ;
+ eloop.writers.type = EVENT_TYPE_WRITE;
+ eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
+#endif /* CONFIG_ELOOP_EPOLL */
#ifdef WPA_TRACE
signal(SIGSEGV, eloop_sigsegv_handler);
#endif /* WPA_TRACE */
@@ -139,6 +171,11 @@
int sock, eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
+#ifdef CONFIG_ELOOP_EPOLL
+ struct eloop_sock *temp_table;
+ struct epoll_event ev, *temp_events;
+ int next;
+#endif /* CONFIG_ELOOP_EPOLL */
struct eloop_sock *tmp;
int new_max_sock;
@@ -174,6 +211,33 @@
eloop.pollfds = n;
}
#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+ if (new_max_sock >= eloop.epoll_max_fd) {
+ next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2;
+ temp_table = os_realloc_array(eloop.epoll_table, next,
+ sizeof(struct eloop_sock));
+ if (temp_table == NULL)
+ return -1;
+
+ eloop.epoll_max_fd = next;
+ eloop.epoll_table = temp_table;
+ }
+
+ if (eloop.count + 1 > eloop.epoll_max_event_num) {
+ next = eloop.epoll_max_event_num == 0 ? 8 :
+ eloop.epoll_max_event_num * 2;
+ temp_events = os_realloc_array(eloop.epoll_events, next,
+ sizeof(struct epoll_event));
+ if (temp_events == NULL) {
+ wpa_printf(MSG_ERROR, "%s: malloc for epoll failed. "
+ "%s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ eloop.epoll_max_event_num = next;
+ eloop.epoll_events = temp_events;
+ }
+#endif /* CONFIG_ELOOP_EPOLL */
eloop_trace_sock_remove_ref(table);
tmp = os_realloc_array(table->table, table->count + 1,
@@ -190,9 +254,38 @@
table->table = tmp;
eloop.max_sock = new_max_sock;
eloop.count++;
+#ifndef CONFIG_ELOOP_EPOLL
table->changed = 1;
+#endif /* CONFIG_ELOOP_EPOLL */
eloop_trace_sock_add_ref(table);
+#ifdef CONFIG_ELOOP_EPOLL
+ os_memset(&ev, 0, sizeof(ev));
+ switch (table->type) {
+ case EVENT_TYPE_READ:
+ ev.events = EPOLLIN;
+ break;
+ case EVENT_TYPE_WRITE:
+ ev.events = EPOLLOUT;
+ break;
+ /*
+ * Exceptions are always checked when using epoll, but I suppose it's
+ * possible that someone registered a socket *only* for exception
+ * handling.
+ */
+ case EVENT_TYPE_EXCEPTION:
+ ev.events = EPOLLERR | EPOLLHUP;
+ break;
+ }
+ ev.data.fd = sock;
+ if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
+ wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d "
+ "failed. %s\n", __func__, sock, strerror(errno));
+ return -1;
+ }
+ os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1],
+ sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
return 0;
}
@@ -219,8 +312,18 @@
}
table->count--;
eloop.count--;
+#ifndef CONFIG_ELOOP_EPOLL
table->changed = 1;
+#endif /* CONFIG_ELOOP_EPOLL */
eloop_trace_sock_add_ref(table);
+#ifdef CONFIG_ELOOP_EPOLL
+ if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
+ wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d "
+ "failed. %s\n", __func__, sock, strerror(errno));
+ return;
+ }
+ os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
}
@@ -362,7 +465,9 @@
max_pollfd_map, POLLERR | POLLHUP);
}
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_SELECT
static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
fd_set *fds)
@@ -374,8 +479,10 @@
if (table->table == NULL)
return;
- for (i = 0; i < table->count; i++)
+ for (i = 0; i < table->count; i++) {
+ assert(table->table[i].sock >= 0);
FD_SET(table->table[i].sock, fds);
+ }
}
@@ -399,7 +506,24 @@
}
}
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+
+
+#ifdef CONFIG_ELOOP_EPOLL
+static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
+{
+ struct eloop_sock *table;
+ int i;
+
+ for (i = 0; i < nfds; i++) {
+ table = &eloop.epoll_table[events[i].data.fd];
+ if (table->handler == NULL)
+ continue;
+ table->handler(table->sock, table->eloop_data,
+ table->user_data);
+ }
+}
+#endif /* CONFIG_ELOOP_EPOLL */
static void eloop_sock_table_destroy(struct eloop_sock_table *table)
@@ -459,6 +583,7 @@
{
struct eloop_sock_table *table;
+ assert(sock >= 0);
table = eloop_get_sock_table(type);
return eloop_sock_table_add_sock(table, sock, handler,
eloop_data, user_data);
@@ -484,7 +609,7 @@
timeout = os_zalloc(sizeof(*timeout));
if (timeout == NULL)
return -1;
- if (os_get_time(&timeout->time) < 0) {
+ if (os_get_reltime(&timeout->time) < 0) {
os_free(timeout);
return -1;
}
@@ -514,7 +639,7 @@
/* Maintain timeouts in order of increasing time */
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
- if (os_time_before(&timeout->time, &tmp->time)) {
+ if (os_reltime_before(&timeout->time, &tmp->time)) {
dl_list_add(tmp->list.prev, &timeout->list);
return 0;
}
@@ -558,13 +683,13 @@
int eloop_cancel_timeout_one(eloop_timeout_handler handler,
void *eloop_data, void *user_data,
- struct os_time *remaining)
+ struct os_reltime *remaining)
{
struct eloop_timeout *timeout, *prev;
int removed = 0;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
remaining->sec = remaining->usec = 0;
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
@@ -573,8 +698,8 @@
(timeout->eloop_data == eloop_data) &&
(timeout->user_data == user_data)) {
removed = 1;
- if (os_time_before(&now, &timeout->time))
- os_time_sub(&timeout->time, &now, remaining);
+ if (os_reltime_before(&now, &timeout->time))
+ os_reltime_sub(&timeout->time, &now, remaining);
eloop_remove_timeout(timeout);
break;
}
@@ -599,6 +724,70 @@
}
+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)
{
@@ -709,20 +898,24 @@
#ifdef CONFIG_ELOOP_POLL
int num_poll_fds;
int timeout_ms = 0;
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
fd_set *rfds, *wfds, *efds;
struct timeval _tv;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+ int timeout_ms = -1;
+#endif /* CONFIG_ELOOP_EPOLL */
int res;
- struct os_time tv, now;
+ struct os_reltime tv, now;
-#ifndef CONFIG_ELOOP_POLL
+#ifdef CONFIG_ELOOP_SELECT
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 */
+#endif /* CONFIG_ELOOP_SELECT */
while (!eloop.terminate &&
(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
@@ -731,17 +924,18 @@
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
- os_get_time(&now);
- if (os_time_before(&now, &timeout->time))
- os_time_sub(&timeout->time, &now, &tv);
+ 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
+#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
timeout_ms = tv.sec * 1000 + tv.usec / 1000;
-#else /* CONFIG_ELOOP_POLL */
+#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+#ifdef CONFIG_ELOOP_SELECT
_tv.tv_sec = tv.sec;
_tv.tv_usec = tv.usec;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
}
#ifdef CONFIG_ELOOP_POLL
@@ -751,30 +945,44 @@
eloop.max_pollfd_map);
res = poll(eloop.pollfds, num_poll_fds,
timeout ? timeout_ms : -1);
-
- if (res < 0 && errno != EINTR && errno != 0) {
- perror("poll");
- goto out;
- }
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
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);
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+ if (eloop.count == 0) {
+ res = 0;
+ } else {
+ res = epoll_wait(eloop.epollfd, eloop.epoll_events,
+ eloop.count, timeout_ms);
+ }
+#endif /* CONFIG_ELOOP_EPOLL */
if (res < 0 && errno != EINTR && errno != 0) {
- perror("select");
+ wpa_printf(MSG_ERROR, "eloop: %s: %s",
+#ifdef CONFIG_ELOOP_POLL
+ "poll"
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+ "select"
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+ "epoll"
+#endif /* CONFIG_ELOOP_EPOLL */
+ , 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_time(&now);
- if (!os_time_before(&now, &timeout->time)) {
+ 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 =
@@ -792,20 +1000,24 @@
eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
&eloop.exceptions, eloop.pollfds_map,
eloop.max_pollfd_map);
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
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 */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+ eloop_sock_table_dispatch(eloop.epoll_events, res);
+#endif /* CONFIG_ELOOP_EPOLL */
}
eloop.terminate = 0;
out:
-#ifndef CONFIG_ELOOP_POLL
+#ifdef CONFIG_ELOOP_SELECT
os_free(rfds);
os_free(wfds);
os_free(efds);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
return;
}
@@ -819,9 +1031,9 @@
void eloop_destroy(void)
{
struct eloop_timeout *timeout, *prev;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
struct eloop_timeout, list) {
int sec, usec;
@@ -849,6 +1061,11 @@
os_free(eloop.pollfds);
os_free(eloop.pollfds_map);
#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+ os_free(eloop.epoll_table);
+ os_free(eloop.epoll_events);
+ close(eloop.epollfd);
+#endif /* CONFIG_ELOOP_EPOLL */
}
@@ -871,7 +1088,13 @@
pfd.events = POLLIN;
poll(&pfd, 1, -1);
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
+ /*
+ * We can use epoll() here. But epoll() requres 4 system calls.
+ * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
+ * epoll fd. So select() is better for performance here.
+ */
fd_set rfds;
if (sock < 0)
@@ -880,5 +1103,9 @@
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
select(sock + 1, &rfds, NULL, NULL, NULL);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
}
+
+#ifdef CONFIG_ELOOP_SELECT
+#undef CONFIG_ELOOP_SELECT
+#endif /* CONFIG_ELOOP_SELECT */
diff --git a/src/utils/eloop.h b/src/utils/eloop.h
index 0037c63..07b8c0d 100644
--- a/src/utils/eloop.h
+++ b/src/utils/eloop.h
@@ -207,7 +207,7 @@
*/
int eloop_cancel_timeout_one(eloop_timeout_handler handler,
void *eloop_data, void *user_data,
- struct os_time *remaining);
+ struct os_reltime *remaining);
/**
* eloop_is_timeout_registered - Check if a timeout is already registered
@@ -223,6 +223,40 @@
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 <handler,eloop_data,user_data> 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 <handler,eloop_data,user_data> 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
diff --git a/src/utils/eloop_none.c b/src/utils/eloop_none.c
deleted file mode 100644
index cb5e922..0000000
--- a/src/utils/eloop_none.c
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Event loop - empty template (basic structure, but no OS specific operations)
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "list.h"
-#include "eloop.h"
-
-
-struct eloop_sock {
- int sock;
- void *eloop_data;
- void *user_data;
- eloop_sock_handler handler;
-};
-
-struct eloop_timeout {
- struct dl_list list;
- struct os_time 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, reader_count;
- struct eloop_sock *readers;
-
- 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;
-
-
-int eloop_init(void)
-{
- os_memset(&eloop, 0, sizeof(eloop));
- dl_list_init(&eloop.timeout);
- return 0;
-}
-
-
-int eloop_register_read_sock(int sock, eloop_sock_handler handler,
- void *eloop_data, void *user_data)
-{
- struct eloop_sock *tmp;
-
- tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1,
- sizeof(struct eloop_sock));
- if (tmp == NULL)
- 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;
- 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)
-{
- int 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;
- 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_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_time(&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_time_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_time *remaining)
-{
- struct eloop_timeout *timeout, *prev;
- int removed = 0;
- struct os_time now;
-
- os_get_time(&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_time_before(&now, &timeout->time))
- os_time_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;
-}
-
-
-/* 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);
- }
- }
-}
-
-
-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;
-}
-
-
-int eloop_register_signal_terminate(eloop_signal_handler handler,
- void *user_data)
-{
-#if 0
- /* TODO: for example */
- int ret = eloop_register_signal(SIGINT, handler, user_data);
- if (ret == 0)
- ret = eloop_register_signal(SIGTERM, handler, user_data);
- return ret;
-#endif
- return 0;
-}
-
-
-int eloop_register_signal_reconfig(eloop_signal_handler handler,
- void *user_data)
-{
-#if 0
- /* TODO: for example */
- return eloop_register_signal(SIGHUP, handler, user_data);
-#endif
- return 0;
-}
-
-
-void eloop_run(void)
-{
- int i;
- struct os_time tv, now;
-
- while (!eloop.terminate &&
- (!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0)) {
- struct eloop_timeout *timeout;
- timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
- list);
- if (timeout) {
- os_get_time(&now);
- if (os_time_before(&now, &timeout->time))
- os_time_sub(&timeout->time, &now, &tv);
- else
- tv.sec = tv.usec = 0;
- }
-
- /*
- * TODO: wait for any event (read socket ready, timeout (tv),
- * signal
- */
- os_sleep(1, 0); /* just a dummy wait for testing */
-
- 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_time(&now);
- if (!os_time_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);
- }
-
- }
-
- eloop.reader_table_changed = 0;
- for (i = 0; i < eloop.reader_count; i++) {
- /*
- * TODO: call each handler that has pending data to
- * read
- */
- if (0 /* TODO: eloop.readers[i].sock ready */) {
- 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;
-}
-
-
-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);
-}
-
-
-int eloop_terminated(void)
-{
- return eloop.terminate;
-}
-
-
-void eloop_wait_for_read_sock(int sock)
-{
- /*
- * TODO: wait for the file descriptor to have something available for
- * reading
- */
-}
diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c
index eda412f..de47fb2 100644
--- a/src/utils/eloop_win.c
+++ b/src/utils/eloop_win.c
@@ -31,7 +31,7 @@
struct eloop_timeout {
struct dl_list list;
- struct os_time time;
+ struct os_reltime time;
void *eloop_data;
void *user_data;
eloop_timeout_handler handler;
@@ -244,7 +244,7 @@
timeout = os_zalloc(sizeof(*timeout));
if (timeout == NULL)
return -1;
- if (os_get_time(&timeout->time) < 0) {
+ if (os_get_reltime(&timeout->time) < 0) {
os_free(timeout);
return -1;
}
@@ -271,7 +271,7 @@
/* Maintain timeouts in order of increasing time */
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
- if (os_time_before(&timeout->time, &tmp->time)) {
+ if (os_reltime_before(&timeout->time, &tmp->time)) {
dl_list_add(tmp->list.prev, &timeout->list);
return 0;
}
@@ -313,13 +313,13 @@
int eloop_cancel_timeout_one(eloop_timeout_handler handler,
void *eloop_data, void *user_data,
- struct os_time *remaining)
+ struct os_reltime *remaining)
{
struct eloop_timeout *timeout, *prev;
int removed = 0;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
remaining->sec = remaining->usec = 0;
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
@@ -328,8 +328,8 @@
(timeout->eloop_data == eloop_data) &&
(timeout->user_data == user_data)) {
removed = 1;
- if (os_time_before(&now, &timeout->time))
- os_time_sub(&timeout->time, &now, remaining);
+ if (os_reltime_before(&now, &timeout->time))
+ os_reltime_sub(&timeout->time, &now, remaining);
eloop_remove_timeout(timeout);
break;
}
@@ -354,6 +354,70 @@
}
+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)
@@ -468,7 +532,7 @@
void eloop_run(void)
{
- struct os_time tv, now;
+ struct os_reltime tv, now;
DWORD count, ret, timeout_val, err;
size_t i;
@@ -480,9 +544,9 @@
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
- os_get_time(&now);
- if (os_time_before(&now, &timeout->time))
- os_time_sub(&timeout->time, &now, &tv);
+ os_get_reltime(&now);
+ if (os_reltime_before(&now, &timeout->time))
+ os_reltime_sub(&timeout->time, &now, &tv);
}
count = 0;
@@ -521,8 +585,8 @@
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
- os_get_time(&now);
- if (!os_time_before(&now, &timeout->time)) {
+ 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 =
diff --git a/src/utils/ext_password_test.c b/src/utils/ext_password_test.c
index 3801bb8..b3a4552 100644
--- a/src/utils/ext_password_test.c
+++ b/src/utils/ext_password_test.c
@@ -36,7 +36,7 @@
{
struct ext_password_test_data *data = ctx;
- os_free(data->params);
+ str_clear_free(data->params);
os_free(data);
}
diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h
new file mode 100644
index 0000000..8d4399a
--- /dev/null
+++ b/src/utils/http-utils.h
@@ -0,0 +1,63 @@
+/*
+ * HTTP wrapper
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HTTP_UTILS_H
+#define HTTP_UTILS_H
+
+struct http_ctx;
+
+struct http_othername {
+ char *oid;
+ u8 *data;
+ size_t len;
+};
+
+#define HTTP_MAX_CERT_LOGO_HASH 32
+
+struct http_logo {
+ char *alg_oid;
+ u8 *hash;
+ size_t hash_len;
+ char *uri;
+};
+
+struct http_cert {
+ char **dnsname;
+ unsigned int num_dnsname;
+ struct http_othername *othername;
+ unsigned int num_othername;
+ struct http_logo *logo;
+ unsigned int num_logo;
+};
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+ const char *ca_fname, const char *username,
+ const char *password, const char *client_cert,
+ const char *client_key);
+int soap_reinit_client(struct http_ctx *ctx);
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node);
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx);
+void http_ocsp_set(struct http_ctx *ctx, int val);
+void http_deinit_ctx(struct http_ctx *ctx);
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+ const char *fname, const char *ca_fname);
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+ const char *content_type, const char *ext_hdr,
+ const char *ca_fname,
+ const char *username, const char *password,
+ const char *client_cert, const char *client_key,
+ size_t *resp_len);
+void http_set_cert_cb(struct http_ctx *ctx,
+ int (*cb)(void *ctx, struct http_cert *cert),
+ void *cb_ctx);
+const char * http_get_err(struct http_ctx *ctx);
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname);
+
+#endif /* HTTP_UTILS_H */
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
new file mode 100644
index 0000000..eb79b86
--- /dev/null
+++ b/src/utils/http_curl.c
@@ -0,0 +1,1641 @@
+/*
+ * HTTP wrapper for libcurl
+ * Copyright (c) 2012-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 <curl/curl.h>
+#ifdef EAP_TLS_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509v3.h>
+
+#ifdef SSL_set_tlsext_status_type
+#ifndef OPENSSL_NO_TLSEXT
+#define HAVE_OCSP
+#include <openssl/err.h>
+#include <openssl/ocsp.h>
+#endif /* OPENSSL_NO_TLSEXT */
+#endif /* SSL_set_tlsext_status_type */
+#endif /* EAP_TLS_OPENSSL */
+
+#include "common.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+
+
+struct http_ctx {
+ void *ctx;
+ struct xml_node_ctx *xml;
+ CURL *curl;
+ struct curl_slist *curl_hdr;
+ char *svc_address;
+ char *svc_ca_fname;
+ char *svc_username;
+ char *svc_password;
+ char *svc_client_cert;
+ char *svc_client_key;
+ char *curl_buf;
+ size_t curl_buf_len;
+
+ int (*cert_cb)(void *ctx, struct http_cert *cert);
+ void *cert_cb_ctx;
+
+ enum {
+ NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP
+ } ocsp;
+ X509 *peer_cert;
+ X509 *peer_issuer;
+ X509 *peer_issuer_issuer;
+
+ const char *last_err;
+};
+
+
+static void clear_curl(struct http_ctx *ctx)
+{
+ if (ctx->curl) {
+ curl_easy_cleanup(ctx->curl);
+ ctx->curl = NULL;
+ }
+ if (ctx->curl_hdr) {
+ curl_slist_free_all(ctx->curl_hdr);
+ ctx->curl_hdr = NULL;
+ }
+}
+
+
+static void clone_str(char **dst, const char *src)
+{
+ os_free(*dst);
+ if (src)
+ *dst = os_strdup(src);
+ else
+ *dst = NULL;
+}
+
+
+static void debug_dump(struct http_ctx *ctx, const char *title,
+ const char *buf, size_t len)
+{
+ char *txt;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] < 32 && buf[i] != '\t' && buf[i] != '\n' &&
+ buf[i] != '\r') {
+ wpa_hexdump_ascii(MSG_MSGDUMP, title, buf, len);
+ return;
+ }
+ }
+
+ txt = os_malloc(len + 1);
+ if (txt == NULL)
+ return;
+ os_memcpy(txt, buf, len);
+ txt[len] = '\0';
+ while (len > 0) {
+ len--;
+ if (txt[len] == '\n' || txt[len] == '\r')
+ txt[len] = '\0';
+ else
+ break;
+ }
+ wpa_printf(MSG_MSGDUMP, "%s[%s]", title, txt);
+ os_free(txt);
+}
+
+
+static int curl_cb_debug(CURL *curl, curl_infotype info, char *buf, size_t len,
+ void *userdata)
+{
+ struct http_ctx *ctx = userdata;
+ switch (info) {
+ case CURLINFO_TEXT:
+ debug_dump(ctx, "CURLINFO_TEXT", buf, len);
+ break;
+ case CURLINFO_HEADER_IN:
+ debug_dump(ctx, "CURLINFO_HEADER_IN", buf, len);
+ break;
+ case CURLINFO_HEADER_OUT:
+ debug_dump(ctx, "CURLINFO_HEADER_OUT", buf, len);
+ break;
+ case CURLINFO_DATA_IN:
+ debug_dump(ctx, "CURLINFO_DATA_IN", buf, len);
+ break;
+ case CURLINFO_DATA_OUT:
+ debug_dump(ctx, "CURLINFO_DATA_OUT", buf, len);
+ break;
+ case CURLINFO_SSL_DATA_IN:
+ wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_IN - %d",
+ (int) len);
+ break;
+ case CURLINFO_SSL_DATA_OUT:
+ wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_OUT - %d",
+ (int) len);
+ break;
+ case CURLINFO_END:
+ wpa_printf(MSG_DEBUG, "debug - CURLINFO_END - %d",
+ (int) len);
+ break;
+ }
+ return 0;
+}
+
+
+static size_t curl_cb_write(void *ptr, size_t size, size_t nmemb,
+ void *userdata)
+{
+ struct http_ctx *ctx = userdata;
+ char *n;
+ n = os_realloc(ctx->curl_buf, ctx->curl_buf_len + size * nmemb + 1);
+ if (n == NULL)
+ return 0;
+ ctx->curl_buf = n;
+ os_memcpy(n + ctx->curl_buf_len, ptr, size * nmemb);
+ n[ctx->curl_buf_len + size * nmemb] = '\0';
+ ctx->curl_buf_len += size * nmemb;
+ return size * nmemb;
+}
+
+
+#ifdef EAP_TLS_OPENSSL
+
+static void debug_dump_cert(const char *title, X509 *cert)
+{
+ BIO *out;
+ char *txt;
+ size_t rlen;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ int res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_MSGDUMP, "%s:\n%s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+}
+
+
+static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert,
+ OTHERNAME *o)
+{
+ char txt[100];
+ int res;
+ struct http_othername *on;
+ ASN1_TYPE *val;
+
+ on = os_realloc_array(cert->othername, cert->num_othername + 1,
+ sizeof(struct http_othername));
+ if (on == NULL)
+ return;
+ cert->othername = on;
+ on = &on[cert->num_othername];
+ os_memset(on, 0, sizeof(*on));
+
+ res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ return;
+
+ on->oid = os_strdup(txt);
+ if (on->oid == NULL)
+ return;
+
+ val = o->value;
+ on->data = val->value.octet_string->data;
+ on->len = val->value.octet_string->length;
+
+ cert->num_othername++;
+}
+
+
+static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert,
+ ASN1_STRING *name)
+{
+ char *buf;
+ char **n;
+
+ buf = NULL;
+ if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0)
+ return;
+
+ n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1,
+ sizeof(char *));
+ if (n == NULL)
+ return;
+
+ cert->dnsname = n;
+ n[cert->num_dnsname] = buf;
+ cert->num_dnsname++;
+}
+
+
+static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert,
+ const GENERAL_NAME *name)
+{
+ switch (name->type) {
+ case GEN_OTHERNAME:
+ add_alt_name_othername(ctx, cert, name->d.otherName);
+ break;
+ case GEN_DNS:
+ add_alt_name_dns(ctx, cert, name->d.dNSName);
+ break;
+ }
+}
+
+
+static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert,
+ GENERAL_NAMES *names)
+{
+ int num, i;
+
+ num = sk_GENERAL_NAME_num(names);
+ for (i = 0; i < num; i++) {
+ const GENERAL_NAME *name;
+ name = sk_GENERAL_NAME_value(names, i);
+ add_alt_name(ctx, cert, name);
+ }
+}
+
+
+/* RFC 3709 */
+
+typedef struct {
+ X509_ALGOR *hashAlg;
+ ASN1_OCTET_STRING *hashValue;
+} HashAlgAndValue;
+
+typedef struct {
+ STACK_OF(HashAlgAndValue) *refStructHash;
+ STACK_OF(ASN1_IA5STRING) *refStructURI;
+} LogotypeReference;
+
+typedef struct {
+ ASN1_IA5STRING *mediaType;
+ STACK_OF(HashAlgAndValue) *logotypeHash;
+ STACK_OF(ASN1_IA5STRING) *logotypeURI;
+} LogotypeDetails;
+
+typedef struct {
+ int type;
+ union {
+ ASN1_INTEGER *numBits;
+ ASN1_INTEGER *tableSize;
+ } d;
+} LogotypeImageResolution;
+
+typedef struct {
+ ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */
+ ASN1_INTEGER *fileSize;
+ ASN1_INTEGER *xSize;
+ ASN1_INTEGER *ySize;
+ LogotypeImageResolution *resolution;
+ ASN1_IA5STRING *language;
+} LogotypeImageInfo;
+
+typedef struct {
+ LogotypeDetails *imageDetails;
+ LogotypeImageInfo *imageInfo;
+} LogotypeImage;
+
+typedef struct {
+ ASN1_INTEGER *fileSize;
+ ASN1_INTEGER *playTime;
+ ASN1_INTEGER *channels;
+ ASN1_INTEGER *sampleRate;
+ ASN1_IA5STRING *language;
+} LogotypeAudioInfo;
+
+typedef struct {
+ LogotypeDetails *audioDetails;
+ LogotypeAudioInfo *audioInfo;
+} LogotypeAudio;
+
+typedef struct {
+ STACK_OF(LogotypeImage) *image;
+ STACK_OF(LogotypeAudio) *audio;
+} LogotypeData;
+
+typedef struct {
+ int type;
+ union {
+ LogotypeData *direct;
+ LogotypeReference *indirect;
+ } d;
+} LogotypeInfo;
+
+typedef struct {
+ ASN1_OBJECT *logotypeType;
+ LogotypeInfo *info;
+} OtherLogotypeInfo;
+
+typedef struct {
+ STACK_OF(LogotypeInfo) *communityLogos;
+ LogotypeInfo *issuerLogo;
+ LogotypeInfo *subjectLogo;
+ STACK_OF(OtherLogotypeInfo) *otherLogos;
+} LogotypeExtn;
+
+ASN1_SEQUENCE(HashAlgAndValue) = {
+ ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR),
+ ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(HashAlgAndValue);
+
+ASN1_SEQUENCE(LogotypeReference) = {
+ ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue),
+ ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeReference);
+
+ASN1_SEQUENCE(LogotypeDetails) = {
+ ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING),
+ ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue),
+ ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeDetails);
+
+ASN1_CHOICE(LogotypeImageResolution) = {
+ ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1),
+ ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2)
+} ASN1_CHOICE_END(LogotypeImageResolution);
+
+ASN1_SEQUENCE(LogotypeImageInfo) = {
+ ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0),
+ ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER),
+ ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER),
+ ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER),
+ ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution),
+ ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4),
+} ASN1_SEQUENCE_END(LogotypeImageInfo);
+
+ASN1_SEQUENCE(LogotypeImage) = {
+ ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails),
+ ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo)
+} ASN1_SEQUENCE_END(LogotypeImage);
+
+ASN1_SEQUENCE(LogotypeAudioInfo) = {
+ ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER),
+ ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER),
+ ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER),
+ ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3),
+ ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4)
+} ASN1_SEQUENCE_END(LogotypeAudioInfo);
+
+ASN1_SEQUENCE(LogotypeAudio) = {
+ ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails),
+ ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo)
+} ASN1_SEQUENCE_END(LogotypeAudio);
+
+ASN1_SEQUENCE(LogotypeData) = {
+ ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage),
+ ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1)
+} ASN1_SEQUENCE_END(LogotypeData);
+
+ASN1_CHOICE(LogotypeInfo) = {
+ ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0),
+ ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1)
+} ASN1_CHOICE_END(LogotypeInfo);
+
+ASN1_SEQUENCE(OtherLogotypeInfo) = {
+ ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT),
+ ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo)
+} ASN1_SEQUENCE_END(OtherLogotypeInfo);
+
+ASN1_SEQUENCE(LogotypeExtn) = {
+ ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0),
+ ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1),
+ ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2),
+ ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3)
+} ASN1_SEQUENCE_END(LogotypeExtn);
+
+IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
+
+#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
+#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
+#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
+#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i))
+#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st))
+#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i))
+#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st))
+#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
+#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
+#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+
+
+static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
+ HashAlgAndValue *hash, ASN1_IA5STRING *uri)
+{
+ char txt[100];
+ int res, len;
+ struct http_logo *n;
+
+ if (hash == NULL || uri == NULL)
+ return;
+
+ res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ return;
+
+ n = os_realloc_array(hcert->logo, hcert->num_logo + 1,
+ sizeof(struct http_logo));
+ if (n == NULL)
+ return;
+ hcert->logo = n;
+ n = &hcert->logo[hcert->num_logo];
+ os_memset(n, 0, sizeof(*n));
+
+ n->alg_oid = os_strdup(txt);
+ if (n->alg_oid == NULL)
+ return;
+
+ n->hash_len = ASN1_STRING_length(hash->hashValue);
+ n->hash = os_malloc(n->hash_len);
+ if (n->hash == NULL) {
+ os_free(n->alg_oid);
+ return;
+ }
+ os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len);
+
+ len = ASN1_STRING_length(uri);
+ n->uri = os_malloc(len + 1);
+ if (n->uri == NULL) {
+ os_free(n->alg_oid);
+ os_free(n->hash);
+ return;
+ }
+ os_memcpy(n->uri, ASN1_STRING_data(uri), len);
+ n->uri[len] = '\0';
+
+ hcert->num_logo++;
+}
+
+
+static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert,
+ LogotypeData *data)
+{
+ int i, num;
+
+ if (data->image == NULL)
+ return;
+
+ num = sk_LogotypeImage_num(data->image);
+ for (i = 0; i < num; i++) {
+ LogotypeImage *image;
+ LogotypeDetails *details;
+ int j, hash_num, uri_num;
+ HashAlgAndValue *found_hash = NULL;
+
+ image = sk_LogotypeImage_value(data->image, i);
+ if (image == NULL)
+ continue;
+
+ details = image->imageDetails;
+ if (details == NULL)
+ continue;
+
+ hash_num = sk_HashAlgAndValue_num(details->logotypeHash);
+ for (j = 0; j < hash_num; j++) {
+ HashAlgAndValue *hash;
+ char txt[100];
+ int res;
+ hash = sk_HashAlgAndValue_value(details->logotypeHash,
+ j);
+ if (hash == NULL)
+ continue;
+ res = OBJ_obj2txt(txt, sizeof(txt),
+ hash->hashAlg->algorithm, 1);
+ if (res < 0 || res >= (int) sizeof(txt))
+ continue;
+ if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) {
+ found_hash = hash;
+ break;
+ }
+ }
+
+ if (!found_hash) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo");
+ continue;
+ }
+
+ uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI);
+ for (j = 0; j < uri_num; j++) {
+ ASN1_IA5STRING *uri;
+ uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j);
+ add_logo(ctx, hcert, found_hash, uri);
+ }
+ }
+}
+
+
+static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert,
+ LogotypeReference *ref)
+{
+ int j, hash_num, uri_num;
+
+ hash_num = sk_HashAlgAndValue_num(ref->refStructHash);
+ uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI);
+ if (hash_num != uri_num) {
+ wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d",
+ hash_num, uri_num);
+ return;
+ }
+
+ for (j = 0; j < hash_num; j++) {
+ HashAlgAndValue *hash;
+ ASN1_IA5STRING *uri;
+ hash = sk_HashAlgAndValue_value(ref->refStructHash, j);
+ uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j);
+ add_logo(ctx, hcert, hash, uri);
+ }
+}
+
+
+static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent)
+{
+ int i;
+ const unsigned char *data;
+
+ BIO_printf(out, "%*shashAlg: ", indent, "");
+ i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm);
+ BIO_printf(out, "\n");
+
+ BIO_printf(out, "%*shashValue: ", indent, "");
+ data = hash->hashValue->data;
+ for (i = 0; i < hash->hashValue->length; i++)
+ BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]);
+ BIO_printf(out, "\n");
+}
+
+static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent)
+{
+ int i, num;
+
+ BIO_printf(out, "%*sLogotypeDetails\n", indent, "");
+ if (details->mediaType) {
+ BIO_printf(out, "%*smediaType: ", indent, "");
+ ASN1_STRING_print(out, details->mediaType);
+ BIO_printf(out, "\n");
+ }
+
+ num = details->logotypeHash ?
+ sk_HashAlgAndValue_num(details->logotypeHash) : 0;
+ for (i = 0; i < num; i++) {
+ HashAlgAndValue *hash;
+ hash = sk_HashAlgAndValue_value(details->logotypeHash, i);
+ i2r_HashAlgAndValue(hash, out, indent);
+ }
+
+ num = details->logotypeURI ?
+ sk_ASN1_IA5STRING_num(details->logotypeURI) : 0;
+ for (i = 0; i < num; i++) {
+ ASN1_IA5STRING *uri;
+ uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i);
+ BIO_printf(out, "%*slogotypeURI: ", indent, "");
+ ASN1_STRING_print(out, uri);
+ BIO_printf(out, "\n");
+ }
+}
+
+static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent)
+{
+ long val;
+
+ BIO_printf(out, "%*sLogotypeImageInfo\n", indent, "");
+ if (info->type) {
+ val = ASN1_INTEGER_get(info->type);
+ BIO_printf(out, "%*stype: %ld\n", indent, "", val);
+ } else {
+ BIO_printf(out, "%*stype: default (1)\n", indent, "");
+ }
+ val = ASN1_INTEGER_get(info->xSize);
+ BIO_printf(out, "%*sxSize: %ld\n", indent, "", val);
+ val = ASN1_INTEGER_get(info->ySize);
+ BIO_printf(out, "%*sySize: %ld\n", indent, "", val);
+ if (info->resolution) {
+ BIO_printf(out, "%*sresolution\n", indent, "");
+ /* TODO */
+ }
+ if (info->language) {
+ BIO_printf(out, "%*slanguage: ", indent, "");
+ ASN1_STRING_print(out, info->language);
+ BIO_printf(out, "\n");
+ }
+}
+
+static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent)
+{
+ BIO_printf(out, "%*sLogotypeImage\n", indent, "");
+ if (image->imageDetails) {
+ i2r_LogotypeDetails(image->imageDetails, out, indent + 4);
+ }
+ if (image->imageInfo) {
+ i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4);
+ }
+}
+
+static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out,
+ int indent)
+{
+ int i, num;
+
+ BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title);
+
+ num = data->image ? sk_LogotypeImage_num(data->image) : 0;
+ for (i = 0; i < num; i++) {
+ LogotypeImage *image = sk_LogotypeImage_value(data->image, i);
+ i2r_LogotypeImage(image, out, indent + 4);
+ }
+
+ num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0;
+ for (i = 0; i < num; i++) {
+ BIO_printf(out, "%*saudio: TODO\n", indent, "");
+ }
+}
+
+static void i2r_LogotypeReference(LogotypeReference *ref, const char *title,
+ BIO *out, int indent)
+{
+ int i, hash_num, uri_num;
+
+ BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title);
+
+ hash_num = ref->refStructHash ?
+ sk_HashAlgAndValue_num(ref->refStructHash) : 0;
+ uri_num = ref->refStructURI ?
+ sk_ASN1_IA5STRING_num(ref->refStructURI) : 0;
+ if (hash_num != uri_num) {
+ BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n",
+ indent, "", hash_num, uri_num);
+ return;
+ }
+
+ for (i = 0; i < hash_num; i++) {
+ HashAlgAndValue *hash;
+ ASN1_IA5STRING *uri;
+
+ hash = sk_HashAlgAndValue_value(ref->refStructHash, i);
+ i2r_HashAlgAndValue(hash, out, indent);
+
+ uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i);
+ BIO_printf(out, "%*srefStructURI: ", indent, "");
+ ASN1_STRING_print(out, uri);
+ BIO_printf(out, "\n");
+ }
+}
+
+static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out,
+ int indent)
+{
+ switch (info->type) {
+ case 0:
+ i2r_LogotypeData(info->d.direct, title, out, indent);
+ break;
+ case 1:
+ i2r_LogotypeReference(info->d.indirect, title, out, indent);
+ break;
+ }
+}
+
+static void debug_print_logotypeext(LogotypeExtn *logo)
+{
+ BIO *out;
+ int i, num;
+ int indent = 0;
+
+ out = BIO_new_fp(stdout, BIO_NOCLOSE);
+ if (out == NULL)
+ return;
+
+ if (logo->communityLogos) {
+ num = sk_LogotypeInfo_num(logo->communityLogos);
+ for (i = 0; i < num; i++) {
+ LogotypeInfo *info;
+ info = sk_LogotypeInfo_value(logo->communityLogos, i);
+ i2r_LogotypeInfo(info, "communityLogo", out, indent);
+ }
+ }
+
+ if (logo->issuerLogo) {
+ i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent );
+ }
+
+ if (logo->subjectLogo) {
+ i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent);
+ }
+
+ if (logo->otherLogos) {
+ BIO_printf(out, "%*sotherLogos - TODO\n", indent, "");
+ }
+
+ BIO_free(out);
+}
+
+
+static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
+ X509 *cert)
+{
+ ASN1_OBJECT *obj;
+ int pos;
+ X509_EXTENSION *ext;
+ ASN1_OCTET_STRING *os;
+ LogotypeExtn *logo;
+ const unsigned char *data;
+ int i, num;
+
+ obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0);
+ if (obj == NULL)
+ return;
+
+ pos = X509_get_ext_by_OBJ(cert, obj, -1);
+ if (pos < 0) {
+ wpa_printf(MSG_INFO, "No logotype extension included");
+ return;
+ }
+
+ wpa_printf(MSG_INFO, "Parsing logotype extension");
+ ext = X509_get_ext(cert, pos);
+ if (!ext) {
+ wpa_printf(MSG_INFO, "Could not get logotype extension");
+ return;
+ }
+
+ os = X509_EXTENSION_get_data(ext);
+ if (os == NULL) {
+ wpa_printf(MSG_INFO, "Could not get logotype extension data");
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "logotypeExtn",
+ ASN1_STRING_data(os), ASN1_STRING_length(os));
+
+ data = ASN1_STRING_data(os);
+ logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os));
+ if (logo == NULL) {
+ wpa_printf(MSG_INFO, "Failed to parse logotypeExtn");
+ return;
+ }
+
+ if (wpa_debug_level < MSG_INFO)
+ debug_print_logotypeext(logo);
+
+ if (!logo->communityLogos) {
+ wpa_printf(MSG_INFO, "No communityLogos included");
+ LogotypeExtn_free(logo);
+ return;
+ }
+
+ num = sk_LogotypeInfo_num(logo->communityLogos);
+ for (i = 0; i < num; i++) {
+ LogotypeInfo *info;
+ info = sk_LogotypeInfo_value(logo->communityLogos, i);
+ switch (info->type) {
+ case 0:
+ add_logo_direct(ctx, hcert, info->d.direct);
+ break;
+ case 1:
+ add_logo_indirect(ctx, hcert, info->d.indirect);
+ break;
+ }
+ }
+
+ LogotypeExtn_free(logo);
+}
+
+
+static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert,
+ X509 *cert, GENERAL_NAMES **names)
+{
+ os_memset(hcert, 0, sizeof(*hcert));
+
+ *names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+ if (*names)
+ add_alt_names(ctx, hcert, *names);
+
+ add_logotype_ext(ctx, hcert, cert);
+}
+
+
+static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names)
+{
+ unsigned int i;
+
+ for (i = 0; i < hcert->num_dnsname; i++)
+ OPENSSL_free(hcert->dnsname[i]);
+ os_free(hcert->dnsname);
+
+ for (i = 0; i < hcert->num_othername; i++)
+ os_free(hcert->othername[i].oid);
+ os_free(hcert->othername);
+
+ for (i = 0; i < hcert->num_logo; i++) {
+ os_free(hcert->logo[i].alg_oid);
+ os_free(hcert->logo[i].hash);
+ os_free(hcert->logo[i].uri);
+ }
+ os_free(hcert->logo);
+
+ sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+}
+
+
+static int validate_server_cert(struct http_ctx *ctx, X509 *cert)
+{
+ GENERAL_NAMES *names;
+ struct http_cert hcert;
+ int ret;
+
+ if (ctx->cert_cb == NULL)
+ return 0;
+
+ if (0) {
+ BIO *out;
+ out = BIO_new_fp(stdout, BIO_NOCLOSE);
+ X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+ BIO_free(out);
+ }
+
+ parse_cert(ctx, &hcert, cert, &names);
+ ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert);
+ parse_cert_free(&hcert, names);
+
+ return ret;
+}
+
+
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname)
+{
+ BIO *in, *out;
+ X509 *cert;
+ GENERAL_NAMES *names;
+ struct http_cert hcert;
+ unsigned int i;
+
+ in = BIO_new_file(fname, "r");
+ if (in == NULL) {
+ wpa_printf(MSG_ERROR, "Could not read '%s'", fname);
+ return;
+ }
+
+ cert = d2i_X509_bio(in, NULL);
+ BIO_free(in);
+
+ if (cert == NULL) {
+ wpa_printf(MSG_ERROR, "Could not parse certificate");
+ return;
+ }
+
+ out = BIO_new_fp(stdout, BIO_NOCLOSE);
+ if (out) {
+ X509_print_ex(out, cert, XN_FLAG_COMPAT,
+ X509_FLAG_COMPAT);
+ BIO_free(out);
+ }
+
+ wpa_printf(MSG_INFO, "Additional parsing information:");
+ parse_cert(ctx, &hcert, cert, &names);
+ for (i = 0; i < hcert.num_othername; i++) {
+ if (os_strcmp(hcert.othername[i].oid,
+ "1.3.6.1.4.1.40808.1.1.1") == 0) {
+ char *name = os_zalloc(hcert.othername[i].len + 1);
+ if (name) {
+ os_memcpy(name, hcert.othername[i].data,
+ hcert.othername[i].len);
+ wpa_printf(MSG_INFO,
+ "id-wfa-hotspot-friendlyName: %s",
+ name);
+ os_free(name);
+ }
+ wpa_hexdump_ascii(MSG_INFO,
+ "id-wfa-hotspot-friendlyName",
+ hcert.othername[i].data,
+ hcert.othername[i].len);
+ } else {
+ wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s",
+ hcert.othername[i].oid);
+ wpa_hexdump_ascii(MSG_INFO, "unknown othername",
+ hcert.othername[i].data,
+ hcert.othername[i].len);
+ }
+ }
+ parse_cert_free(&hcert, names);
+
+ X509_free(cert);
+}
+
+
+static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ struct http_ctx *ctx;
+ X509 *cert;
+ int err, depth;
+ char buf[256];
+ X509_NAME *name;
+ const char *err_str;
+ SSL *ssl;
+ SSL_CTX *ssl_ctx;
+
+ ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ ssl_ctx = ssl->ctx;
+ ctx = SSL_CTX_get_app_data(ssl_ctx);
+
+ wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify");
+
+ err = X509_STORE_CTX_get_error(x509_ctx);
+ err_str = X509_verify_cert_error_string(err);
+ depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+ cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+ if (!cert) {
+ wpa_printf(MSG_INFO, "No server certificate available");
+ ctx->last_err = "No server certificate available";
+ return 0;
+ }
+
+ if (depth == 0)
+ ctx->peer_cert = cert;
+ else if (depth == 1)
+ ctx->peer_issuer = cert;
+ else if (depth == 2)
+ ctx->peer_issuer_issuer = cert;
+
+ name = X509_get_subject_name(cert);
+ X509_NAME_oneline(name, buf, sizeof(buf));
+ wpa_printf(MSG_INFO, "Server certificate chain - depth=%d err=%d (%s) subject=%s",
+ depth, err, err_str, buf);
+ debug_dump_cert("Server certificate chain - certificate", cert);
+
+ if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
+ return 0;
+
+ if (!preverify_ok)
+ ctx->last_err = "TLS validation failed";
+
+ return preverify_ok;
+}
+
+
+#ifdef HAVE_OCSP
+
+static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
+{
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+
+ 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_MSGDUMP, "OpenSSL: OCSP Response\n%s", txt);
+ }
+ os_free(txt);
+ BIO_free(out);
+}
+
+
+static void tls_show_errors(const char *func, const char *txt)
+{
+ unsigned long err;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - %s %s",
+ func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: pending error: %s",
+ ERR_error_string(err, NULL));
+ }
+}
+
+
+static int ocsp_resp_cb(SSL *s, void *arg)
+{
+ struct http_ctx *ctx = 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");
+ if (ctx->ocsp == MANDATORY_OCSP)
+ ctx->last_err = "No OCSP response received";
+ return (ctx->ocsp == MANDATORY_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");
+ ctx->last_err = "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));
+ ctx->last_err = "OCSP responder error";
+ return 0;
+ }
+
+ basic = OCSP_response_get1_basic(rsp);
+ if (!basic) {
+ wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
+ ctx->last_err = "Could not find BasicOCSPResponse";
+ return 0;
+ }
+
+ store = SSL_CTX_get_cert_store(s->ctx);
+ if (ctx->peer_issuer) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer");
+ debug_dump_cert("OpenSSL: Issuer certificate",
+ ctx->peer_issuer);
+
+ if (X509_STORE_add_cert(store, ctx->peer_issuer) != 1) {
+ tls_show_errors(__func__,
+ "OpenSSL: Could not add issuer to certificate store\n");
+ }
+ certs = sk_X509_new_null();
+ if (certs) {
+ X509 *cert;
+ cert = X509_dup(ctx->peer_issuer);
+ if (cert && !sk_X509_push(certs, cert)) {
+ tls_show_errors(
+ __func__,
+ "OpenSSL: Could not add issuer to OCSP responder trust store\n");
+ X509_free(cert);
+ sk_X509_free(certs);
+ certs = NULL;
+ }
+ if (ctx->peer_issuer_issuer) {
+ X509 *cert;
+ cert = X509_dup(ctx->peer_issuer_issuer);
+ if (cert && !sk_X509_push(certs, cert)) {
+ tls_show_errors(
+ __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(__func__,
+ "OpenSSL: OCSP response failed verification");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "OCSP response failed verification";
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
+
+ if (!ctx->peer_cert) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "Peer certificate not available for OCSP status check";
+ return 0;
+ }
+
+ if (!ctx->peer_issuer) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "Peer issuer certificate not available for OCSP status check";
+ return 0;
+ }
+
+ id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+ if (!id) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "Could not create OCSP certificate identifier";
+ return 0;
+ }
+
+ if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+ &this_update, &next_update)) {
+ wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
+ (ctx->ocsp == MANDATORY_OCSP) ? "" :
+ " (OCSP not required)");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ if (ctx->ocsp == MANDATORY_OCSP)
+
+ ctx->last_err = "Could not find current server certificate from OCSP response";
+ return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+ }
+
+ if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
+ tls_show_errors(__func__, "OpenSSL: OCSP status times invalid");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err = "OCSP status times invalid";
+ 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)
+ ctx->last_err = "Server certificate has been revoked";
+ return 0;
+ if (ctx->ocsp == MANDATORY_OCSP) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
+ ctx->last_err = "OCSP status unknown";
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+ return 1;
+}
+
+
+static SSL_METHOD patch_ssl_method;
+static const SSL_METHOD *real_ssl_method;
+
+static int curl_patch_ssl_new(SSL *s)
+{
+ SSL_CTX *ssl = s->ctx;
+ int ret;
+
+ ssl->method = real_ssl_method;
+ s->method = real_ssl_method;
+
+ ret = s->method->ssl_new(s);
+ SSL_set_tlsext_status_type(s, TLSEXT_STATUSTYPE_ocsp);
+
+ return ret;
+}
+
+#endif /* HAVE_OCSP */
+
+
+static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
+{
+ struct http_ctx *ctx = parm;
+ SSL_CTX *ssl = sslctx;
+
+ wpa_printf(MSG_DEBUG, "curl_cb_ssl");
+ SSL_CTX_set_app_data(ssl, ctx);
+ SSL_CTX_set_verify(ssl, SSL_VERIFY_PEER, curl_cb_ssl_verify);
+
+#ifdef HAVE_OCSP
+ if (ctx->ocsp != NO_OCSP) {
+ SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb);
+ SSL_CTX_set_tlsext_status_arg(ssl, ctx);
+
+ /*
+ * Use a temporary SSL_METHOD to get a callback on SSL_new()
+ * from libcurl since there is no proper callback registration
+ * available for this.
+ */
+ os_memset(&patch_ssl_method, 0, sizeof(patch_ssl_method));
+ patch_ssl_method.ssl_new = curl_patch_ssl_new;
+ real_ssl_method = ssl->method;
+ ssl->method = &patch_ssl_method;
+ }
+#endif /* HAVE_OCSP */
+
+ return CURLE_OK;
+}
+
+#endif /* EAP_TLS_OPENSSL */
+
+
+static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
+ const char *ca_fname, const char *username,
+ const char *password, const char *client_cert,
+ const char *client_key)
+{
+ CURL *curl;
+
+ wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s "
+ "username=%s", address, ca_fname, username);
+
+ curl = curl_easy_init();
+ if (curl == NULL)
+ return NULL;
+
+ curl_easy_setopt(curl, CURLOPT_URL, address);
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+ if (ca_fname) {
+ curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+#ifdef EAP_TLS_OPENSSL
+ curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
+ curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
+#endif /* EAP_TLS_OPENSSL */
+ } else {
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+ }
+ if (client_cert && client_key) {
+ curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert);
+ curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key);
+ }
+ /* TODO: use curl_easy_getinfo() with CURLINFO_CERTINFO to fetch
+ * information about the server certificate */
+ curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+ curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+ curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+ if (username) {
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
+ curl_easy_setopt(curl, CURLOPT_USERNAME, username);
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
+ }
+
+ return curl;
+}
+
+
+static int post_init_client(struct http_ctx *ctx, const char *address,
+ const char *ca_fname, const char *username,
+ const char *password, const char *client_cert,
+ const char *client_key)
+{
+ char *pos;
+ int count;
+
+ clone_str(&ctx->svc_address, address);
+ clone_str(&ctx->svc_ca_fname, ca_fname);
+ clone_str(&ctx->svc_username, username);
+ clone_str(&ctx->svc_password, password);
+ clone_str(&ctx->svc_client_cert, client_cert);
+ clone_str(&ctx->svc_client_key, client_key);
+
+ /*
+ * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname
+ * 'foo' provided via HTTP are different.
+ */
+ for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos;
+ pos++) {
+ if (*pos == '/')
+ count++;
+ *pos = tolower(*pos);
+ }
+
+ ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username,
+ password, client_cert, client_key);
+ if (ctx->curl == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+ const char *ca_fname, const char *username,
+ const char *password, const char *client_cert,
+ const char *client_key)
+{
+ if (post_init_client(ctx, address, ca_fname, username, password,
+ client_cert, client_key) < 0)
+ return -1;
+
+ ctx->curl_hdr = curl_slist_append(ctx->curl_hdr,
+ "Content-Type: application/soap+xml");
+ ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: ");
+ ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:");
+ curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr);
+
+ return 0;
+}
+
+
+int soap_reinit_client(struct http_ctx *ctx)
+{
+ char *address = NULL;
+ char *ca_fname = NULL;
+ char *username = NULL;
+ char *password = NULL;
+ char *client_cert = NULL;
+ char *client_key = NULL;
+ int ret;
+
+ clear_curl(ctx);
+
+ clone_str(&address, ctx->svc_address);
+ clone_str(&ca_fname, ctx->svc_ca_fname);
+ clone_str(&username, ctx->svc_username);
+ clone_str(&password, ctx->svc_password);
+ clone_str(&client_cert, ctx->svc_client_cert);
+ clone_str(&client_key, ctx->svc_client_key);
+
+ ret = soap_init_client(ctx, address, ca_fname, username, password,
+ client_cert, client_key);
+ os_free(address);
+ os_free(ca_fname);
+ str_clear_free(username);
+ str_clear_free(password);
+ os_free(client_cert);
+ os_free(client_key);
+ return ret;
+}
+
+
+static void free_curl_buf(struct http_ctx *ctx)
+{
+ os_free(ctx->curl_buf);
+ ctx->curl_buf = NULL;
+ ctx->curl_buf_len = 0;
+}
+
+
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node)
+{
+ char *str;
+ xml_node_t *envelope, *ret, *resp, *n;
+ CURLcode res;
+ long http = 0;
+
+ ctx->last_err = NULL;
+
+ wpa_printf(MSG_DEBUG, "SOAP: Sending message");
+ envelope = soap_build_envelope(ctx->xml, node);
+ str = xml_node_to_str(ctx->xml, envelope);
+ xml_node_free(ctx->xml, envelope);
+ wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str);
+
+ curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str);
+ free_curl_buf(ctx);
+
+ res = curl_easy_perform(ctx->curl);
+ if (res != CURLE_OK) {
+ if (!ctx->last_err)
+ ctx->last_err = curl_easy_strerror(res);
+ wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+ ctx->last_err);
+ os_free(str);
+ free_curl_buf(ctx);
+ return NULL;
+ }
+ os_free(str);
+
+ curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http);
+ wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http);
+ if (http != 200) {
+ ctx->last_err = "HTTP download failed";
+ wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+ free_curl_buf(ctx);
+ return NULL;
+ }
+
+ if (ctx->curl_buf == NULL)
+ return NULL;
+
+ wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf);
+ resp = xml_node_from_buf(ctx->xml, ctx->curl_buf);
+ free_curl_buf(ctx);
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse SOAP response");
+ ctx->last_err = "Could not parse SOAP response";
+ return NULL;
+ }
+
+ ret = soap_get_body(ctx->xml, resp);
+ if (ret == NULL) {
+ wpa_printf(MSG_INFO, "Could not get SOAP body");
+ ctx->last_err = "Could not get SOAP body";
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'",
+ xml_node_get_localname(ctx->xml, ret));
+ n = xml_node_copy(ctx->xml, ret);
+ xml_node_free(ctx->xml, resp);
+
+ return n;
+}
+
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx)
+{
+ struct http_ctx *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ ctx->ctx = upper_ctx;
+ ctx->xml = xml_ctx;
+ ctx->ocsp = OPTIONAL_OCSP;
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ return ctx;
+}
+
+
+void http_ocsp_set(struct http_ctx *ctx, int val)
+{
+ if (val == 0)
+ ctx->ocsp = NO_OCSP;
+ else if (val == 1)
+ ctx->ocsp = OPTIONAL_OCSP;
+ if (val == 2)
+ ctx->ocsp = MANDATORY_OCSP;
+}
+
+
+void http_deinit_ctx(struct http_ctx *ctx)
+{
+ clear_curl(ctx);
+ os_free(ctx->curl_buf);
+ curl_global_cleanup();
+
+ os_free(ctx->svc_address);
+ os_free(ctx->svc_ca_fname);
+ str_clear_free(ctx->svc_username);
+ str_clear_free(ctx->svc_password);
+ os_free(ctx->svc_client_cert);
+ os_free(ctx->svc_client_key);
+
+ os_free(ctx);
+}
+
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+ const char *fname, const char *ca_fname)
+{
+ CURL *curl;
+ FILE *f;
+ CURLcode res;
+ long http = 0;
+
+ ctx->last_err = NULL;
+
+ wpa_printf(MSG_DEBUG, "curl: Download file from %s to %s (ca=%s)",
+ url, fname, ca_fname);
+ curl = curl_easy_init();
+ if (curl == NULL)
+ return -1;
+
+ f = fopen(fname, "wb");
+ if (f == NULL) {
+ curl_easy_cleanup(curl);
+ return -1;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ if (ca_fname) {
+ curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+ curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+ } else {
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+ }
+ curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+ curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ if (!ctx->last_err)
+ ctx->last_err = curl_easy_strerror(res);
+ wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+ ctx->last_err);
+ curl_easy_cleanup(curl);
+ fclose(f);
+ return -1;
+ }
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+ wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+ if (http != 200) {
+ ctx->last_err = "HTTP download failed";
+ wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+ curl_easy_cleanup(curl);
+ fclose(f);
+ return -1;
+ }
+
+ curl_easy_cleanup(curl);
+ fclose(f);
+
+ return 0;
+}
+
+
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+ const char *content_type, const char *ext_hdr,
+ const char *ca_fname,
+ const char *username, const char *password,
+ const char *client_cert, const char *client_key,
+ size_t *resp_len)
+{
+ long http = 0;
+ CURLcode res;
+ char *ret;
+ CURL *curl;
+ struct curl_slist *curl_hdr = NULL;
+
+ ctx->last_err = NULL;
+ wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url);
+ curl = setup_curl_post(ctx, url, ca_fname, username, password,
+ client_cert, client_key);
+ if (curl == NULL)
+ return NULL;
+
+ if (content_type) {
+ char ct[200];
+ snprintf(ct, sizeof(ct), "Content-Type: %s", content_type);
+ curl_hdr = curl_slist_append(curl_hdr, ct);
+ }
+ if (ext_hdr)
+ curl_hdr = curl_slist_append(curl_hdr, ext_hdr);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdr);
+
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
+ free_curl_buf(ctx);
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ if (!ctx->last_err)
+ ctx->last_err = curl_easy_strerror(res);
+ wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+ ctx->last_err);
+ free_curl_buf(ctx);
+ return NULL;
+ }
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+ wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+ if (http != 200) {
+ ctx->last_err = "HTTP POST failed";
+ wpa_printf(MSG_INFO, "HTTP POST failed - code %ld", http);
+ free_curl_buf(ctx);
+ return NULL;
+ }
+
+ if (ctx->curl_buf == NULL)
+ return NULL;
+
+ ret = ctx->curl_buf;
+ if (resp_len)
+ *resp_len = ctx->curl_buf_len;
+ ctx->curl_buf = NULL;
+ ctx->curl_buf_len = 0;
+
+ wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ret);
+
+ return ret;
+}
+
+
+void http_set_cert_cb(struct http_ctx *ctx,
+ int (*cb)(void *ctx, struct http_cert *cert),
+ void *cb_ctx)
+{
+ ctx->cert_cb = cb;
+ ctx->cert_cb_ctx = cb_ctx;
+}
+
+
+const char * http_get_err(struct http_ctx *ctx)
+{
+ return ctx->last_err;
+}
diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c
index 3647c76..92a3590 100644
--- a/src/utils/ip_addr.c
+++ b/src/utils/ip_addr.c
@@ -33,30 +33,6 @@
}
-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
diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h
index 79ac20c..0670411 100644
--- a/src/utils/ip_addr.h
+++ b/src/utils/ip_addr.h
@@ -22,7 +22,6 @@
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 */
diff --git a/src/utils/os.h b/src/utils/os.h
index ad20834..b9247d8 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -23,6 +23,11 @@
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
@@ -30,21 +35,84 @@
*/
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);
-/* Helper macros for handling struct os_time */
-#define os_time_before(a, b) \
- ((a)->sec < (b)->sec || \
- ((a)->sec == (b)->sec && (a)->usec < (b)->usec))
+/* Helpers for handling struct os_time */
-#define os_time_sub(a, b, res) do { \
- (res)->sec = (a)->sec - (b)->sec; \
- (res)->usec = (a)->usec - (b)->usec; \
- if ((res)->usec < 0) { \
- (res)->sec--; \
- (res)->usec += 1000000; \
- } \
-} while (0)
+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
@@ -172,6 +240,13 @@
char * os_readfile(const char *name, size_t *len);
/**
+ * os_file_exists - Check whether the specified file exists
+ * @fname: Path and name of the file
+ * Returns: 1 if the file exists or 0 if not
+ */
+int os_file_exists(const char *fname);
+
+/**
* os_zalloc - Allocate and zero memory
* @size: Number of bytes to allocate
* Returns: Pointer to allocated and zeroed memory or %NULL on failure
@@ -361,15 +436,6 @@
int os_strncmp(const char *s1, const char *s2, size_t n);
/**
- * os_strncpy - Copy a string
- * @dest: Destination
- * @src: Source
- * @n: Maximum number of characters to copy
- * Returns: dest
- */
-char * os_strncpy(char *dest, const char *src, size_t n);
-
-/**
* os_strstr - Locate a substring
* @haystack: String (haystack) to search from
* @needle: Needle to search from haystack
@@ -465,9 +531,6 @@
#ifndef os_strncmp
#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
#endif
-#ifndef os_strncpy
-#define os_strncpy(d, s, n) strncpy((d), (s), (n))
-#endif
#ifndef os_strrchr
#define os_strrchr(s, c) strrchr((s), (c))
#endif
@@ -493,6 +556,21 @@
return os_realloc(ptr, nmemb * size);
}
+/**
+ * os_remove_in_array - Remove a member from an array by index
+ * @ptr: Pointer to the array
+ * @nmemb: Current member count of the array
+ * @size: The size per member of the array
+ * @idx: Index of the member to be removed
+ */
+static inline void os_remove_in_array(void *ptr, size_t nmemb, size_t size,
+ size_t idx)
+{
+ if (idx < nmemb - 1)
+ os_memmove(((unsigned char *) ptr) + idx * size,
+ ((unsigned char *) ptr) + (idx + 1) * size,
+ (nmemb - idx - 1) * size);
+}
/**
* os_strlcpy - Copy a string with size bound and NUL-termination
@@ -506,6 +584,32 @@
*/
size_t os_strlcpy(char *dest, const char *src, size_t siz);
+/**
+ * os_memcmp_const - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: 0 if buffers are equal, non-zero if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time could provide external observer information
+ * about the location of the difference in the memory buffers. The return value
+ * does not behave like os_memcmp(), i.e., os_memcmp_const() cannot be used to
+ * sort items into a defined order. Unlike os_memcmp(), execution time of
+ * os_memcmp_const() does not depend on the contents of the compared memory
+ * buffers, but only on the total compared length.
+ */
+int os_memcmp_const(const void *a, const void *b, size_t len);
+
+/**
+ * os_exec - Execute an external program
+ * @program: Path to the program
+ * @arg: Command line argument string
+ * @wait_completion: Whether to wait until the program execution completes
+ * Returns: 0 on success, -1 on error
+ */
+int os_exec(const char *program, const char *arg, int wait_completion);
+
#ifdef OS_REJECT_C_LIB_FUNCTIONS
#define malloc OS_DO_NOT_USE_malloc
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
index e4b7fdb..90b6688 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -41,6 +41,17 @@
}
+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)
{
@@ -452,6 +463,20 @@
}
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+ const u8 *aa = a;
+ const u8 *bb = b;
+ size_t i;
+ u8 res;
+
+ for (res = 0, i = 0; i < len; i++)
+ res |= aa[i] ^ bb[i];
+
+ return res;
+}
+
+
char * os_strstr(const char *haystack, const char *needle)
{
size_t len = os_strlen(needle);
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index cabf73b..2649111 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -26,6 +26,12 @@
}
+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)
{
@@ -212,6 +218,11 @@
}
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+ return 0;
+}
+
char * os_strstr(const char *haystack, const char *needle)
{
return NULL;
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index 10b9e0d..523a4d0 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -9,18 +9,19 @@
#include "includes.h"
#include <time.h>
+#include <sys/wait.h>
#ifdef ANDROID
#include <sys/capability.h>
-#include <linux/prctl.h>
+#include <sys/prctl.h>
#include <private/android_filesystem_config.h>
#endif /* ANDROID */
#include "os.h"
+#include "common.h"
#ifdef WPA_TRACE
-#include "common.h"
#include "wpa_debug.h"
#include "trace.h"
#include "list.h"
@@ -60,6 +61,43 @@
}
+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)
{
@@ -268,7 +306,7 @@
struct __user_cap_header_struct header;
struct __user_cap_data_struct cap;
- setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+ setgroups(ARRAY_SIZE(groups), groups);
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
@@ -370,6 +408,16 @@
}
+int os_file_exists(const char *fname)
+{
+ FILE *f = fopen(fname, "rb");
+ if (f == NULL)
+ return 0;
+ fclose(f);
+ return 1;
+}
+
+
#ifndef WPA_TRACE
void * os_zalloc(size_t size)
{
@@ -403,6 +451,20 @@
}
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+ const u8 *aa = a;
+ const u8 *bb = b;
+ size_t i;
+ u8 res;
+
+ for (res = 0, i = 0; i < len; i++)
+ res |= aa[i] ^ bb[i];
+
+ return res;
+}
+
+
#ifdef WPA_TRACE
void * os_malloc(size_t size)
@@ -493,3 +555,57 @@
}
#endif /* WPA_TRACE */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+ pid_t pid;
+ int pid_status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return -1;
+ }
+
+ if (pid == 0) {
+ /* run the external command in the child process */
+ const int MAX_ARG = 30;
+ char *_program, *_arg, *pos;
+ char *argv[MAX_ARG + 1];
+ int i;
+
+ _program = os_strdup(program);
+ _arg = os_strdup(arg);
+
+ argv[0] = _program;
+
+ i = 1;
+ pos = _arg;
+ while (i < MAX_ARG && pos && *pos) {
+ while (*pos == ' ')
+ pos++;
+ if (*pos == '\0')
+ break;
+ argv[i++] = pos;
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ *pos++ = '\0';
+ }
+ argv[i] = NULL;
+
+ execv(program, argv);
+ perror("execv");
+ os_free(_program);
+ os_free(_arg);
+ exit(0);
+ return -1;
+ }
+
+ if (wait_completion) {
+ /* wait for the child process to complete in the parent */
+ waitpid(pid, &pid_status, 0);
+ }
+
+ return 0;
+}
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index 163cebe..57ee132 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -47,6 +47,17 @@
}
+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)
{
@@ -233,3 +244,23 @@
return s - src - 1;
}
+
+
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+ const u8 *aa = a;
+ const u8 *bb = b;
+ size_t i;
+ u8 res;
+
+ for (res = 0, i = 0; i < len; i++)
+ res |= aa[i] ^ bb[i];
+
+ return res;
+}
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+ return -1;
+}
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index 08510d0..d955dc4 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -485,17 +485,15 @@
/**
* scard_init - Initialize SIM/USIM connection using PC/SC
- * @sim_type: Allowed SIM types (SIM, USIM, or both)
* @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 and the card is verified to support the
- * selected sim_type. 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.
+ * 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(scard_sim_type sim_type, const char *reader)
+struct scard_data * scard_init(const char *reader)
{
long ret;
unsigned long len, pos;
@@ -612,20 +610,14 @@
blen = sizeof(buf);
- scard->sim_type = SCARD_GSM_SIM;
- if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) {
- 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");
- if (sim_type == SCARD_USIM_ONLY)
- goto failed;
- wpa_printf(MSG_DEBUG, "SCARD: 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;
- }
+ 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) {
@@ -1245,6 +1237,7 @@
cmd[4] = 17;
cmd[5] = 16;
os_memcpy(cmd + 6, _rand, 16);
+ get_resp[0] = USIM_CLA;
}
len = sizeof(resp);
ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
@@ -1413,6 +1406,12 @@
pos += IK_LEN;
wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
+ if (end > pos) {
+ wpa_hexdump(MSG_DEBUG,
+ "SCARD: Ignore extra data in end",
+ pos, end - pos);
+ }
+
return 0;
}
diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h
index b4ebc99..eacd2a2 100644
--- a/src/utils/pcsc_funcs.h
+++ b/src/utils/pcsc_funcs.h
@@ -9,15 +9,8 @@
#ifndef PCSC_FUNCS_H
#define PCSC_FUNCS_H
-typedef enum {
- SCARD_GSM_SIM_ONLY,
- SCARD_USIM_ONLY,
- SCARD_TRY_BOTH
-} scard_sim_type;
-
-
#ifdef PCSC_FUNCS
-struct scard_data * scard_init(scard_sim_type sim_type, const char *reader);
+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);
@@ -34,7 +27,7 @@
#else /* PCSC_FUNCS */
-#define scard_init(s, r) NULL
+#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
diff --git a/src/utils/platform.h b/src/utils/platform.h
new file mode 100644
index 0000000..46cfe78
--- /dev/null
+++ b/src/utils/platform.h
@@ -0,0 +1,21 @@
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include "includes.h"
+#include "common.h"
+
+#define le16_to_cpu le_to_host16
+#define le32_to_cpu le_to_host32
+
+#define get_unaligned(p) \
+({ \
+ struct packed_dummy_struct { \
+ typeof(*(p)) __val; \
+ } __attribute__((packed)) *__ptr = (void *) (p); \
+ \
+ __ptr->__val; \
+})
+#define get_unaligned_le16(p) le16_to_cpu(get_unaligned((uint16_t *)(p)))
+#define get_unaligned_le32(p) le32_to_cpu(get_unaligned((uint32_t *)(p)))
+
+#endif /* PLATFORM_H */
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
index 804473f..197a4af 100644
--- a/src/utils/radiotap.c
+++ b/src/utils/radiotap.c
@@ -2,6 +2,7 @@
* Radiotap parser
*
* Copyright 2007 Andy Green <andy@warmcat.com>
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
*
* 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
@@ -10,34 +11,44 @@
* 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 <johannes@sipsolutions.net>
- * I only modified some things on top to ease syncing should bugs be found.
+ * See COPYING for more details.
*/
-
-#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; \
-})
+#include "platform.h"
/* function prototypes and related defs are in radiotap_iter.h */
+static const struct radiotap_align_size rtap_namespace_sizes[] = {
+ [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
+ [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
+ [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
+ [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
+ /*
+ * add more here as they are defined in radiotap.h
+ */
+};
+
+static const struct ieee80211_radiotap_namespace radiotap_ns = {
+ .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]),
+ .align_size = rtap_namespace_sizes,
+};
+
/**
* ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
* @iterator: radiotap_iterator to initialize
@@ -73,38 +84,52 @@
* 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
+ * Example code: parse.c
*/
int ieee80211_radiotap_iterator_init(
- struct ieee80211_radiotap_iterator *iterator,
- struct ieee80211_radiotap_header *radiotap_header,
- int max_length)
+ struct ieee80211_radiotap_iterator *iterator,
+ struct ieee80211_radiotap_header *radiotap_header,
+ int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
{
+ /* must at least have the radiotap header */
+ if (max_length < (int)sizeof(struct ieee80211_radiotap_header))
+ return -EINVAL;
+
/* 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)))
+ if (max_length < get_unaligned_le16(&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;
+ iterator->_rtheader = radiotap_header;
+ iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
+ iterator->_arg_index = 0;
+ iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
+ iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
+ iterator->_reset_on_ext = 0;
+ iterator->_next_bitmap = &radiotap_header->it_present;
+ iterator->_next_bitmap++;
+ iterator->_vns = vns;
+ iterator->current_namespace = &radiotap_ns;
+ iterator->is_radiotap_ns = 1;
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+ iterator->n_overrides = 0;
+ iterator->overrides = NULL;
+#endif
/* find payload start allowing for extended bitmap(s) */
- if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
- while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) &
- (1<<IEEE80211_RADIOTAP_EXT)) {
- iterator->arg += sizeof(u32);
+ if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+ if ((unsigned long)iterator->_arg -
+ (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
+ (unsigned long)iterator->_max_length)
+ return -EINVAL;
+ while (get_unaligned_le32(iterator->_arg) &
+ (1 << IEEE80211_RADIOTAP_EXT)) {
+ iterator->_arg += sizeof(uint32_t);
/*
* check for insanity where the present bitmaps
@@ -112,12 +137,14 @@
* stated radiotap header length
*/
- if (((ulong)iterator->arg - (ulong)iterator->rtheader)
- > (ulong)iterator->max_length)
+ if ((unsigned long)iterator->_arg -
+ (unsigned long)iterator->_rtheader +
+ sizeof(uint32_t) >
+ (unsigned long)iterator->_max_length)
return -EINVAL;
}
- iterator->arg += sizeof(u32);
+ iterator->_arg += sizeof(uint32_t);
/*
* no need to check again for blowing past stated radiotap
@@ -126,11 +153,57 @@
*/
}
+ iterator->this_arg = iterator->_arg;
+
/* we are all initialized happily */
return 0;
}
+static void find_ns(struct ieee80211_radiotap_iterator *iterator,
+ uint32_t oui, uint8_t subns)
+{
+ int i;
+
+ iterator->current_namespace = NULL;
+
+ if (!iterator->_vns)
+ return;
+
+ for (i = 0; i < iterator->_vns->n_ns; i++) {
+ if (iterator->_vns->ns[i].oui != oui)
+ continue;
+ if (iterator->_vns->ns[i].subns != subns)
+ continue;
+
+ iterator->current_namespace = &iterator->_vns->ns[i];
+ break;
+ }
+}
+
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+static int find_override(struct ieee80211_radiotap_iterator *iterator,
+ int *align, int *size)
+{
+ int i;
+
+ if (!iterator->overrides)
+ return 0;
+
+ for (i = 0; i < iterator->n_overrides; i++) {
+ if (iterator->_arg_index == iterator->overrides[i].field) {
+ *align = iterator->overrides[i].align;
+ *size = iterator->overrides[i].size;
+ if (!*align) /* erroneous override */
+ return 0;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
/**
* ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
@@ -156,99 +229,106 @@
*/
int ieee80211_radiotap_iterator_next(
- struct ieee80211_radiotap_iterator *iterator)
+ 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)) {
+ while (1) {
int hit = 0;
- int pad;
+ int pad, align, size, subns;
+ uint32_t oui;
- if (!(iterator->bitmap_shifter & 1))
+ /* if no more EXT bits, that's it */
+ if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
+ !(iterator->_bitmap_shifter & 1))
+ return -ENOENT;
+
+ if (!(iterator->_bitmap_shifter & 1))
goto next_entry; /* arg not present */
+ /* get alignment/size of data */
+ switch (iterator->_arg_index % 32) {
+ case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+ case IEEE80211_RADIOTAP_EXT:
+ align = 1;
+ size = 0;
+ break;
+ case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+ align = 2;
+ size = 6;
+ break;
+ default:
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+ if (find_override(iterator, &align, &size)) {
+ /* all set */
+ } else
+#endif
+ if (!iterator->current_namespace ||
+ iterator->_arg_index >= iterator->current_namespace->n_bits) {
+ if (iterator->current_namespace == &radiotap_ns)
+ return -ENOENT;
+ align = 0;
+ } else {
+ align = iterator->current_namespace->align_size[iterator->_arg_index].align;
+ size = iterator->current_namespace->align_size[iterator->_arg_index].size;
+ }
+ if (!align) {
+ /* skip all subsequent data */
+ iterator->_arg = iterator->_next_ns_data;
+ /* give up on this namespace */
+ iterator->current_namespace = NULL;
+ goto next_entry;
+ }
+ break;
+ }
+
/*
* 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
+ * Note that 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
+ * 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);
+ pad = ((unsigned long)iterator->_arg -
+ (unsigned long)iterator->_rtheader) & (align - 1);
if (pad)
- iterator->arg +=
- (rt_sizes[iterator->arg_index] >> 4) - pad;
+ iterator->_arg += align - pad;
+
+ if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
+ int vnslen;
+
+ if ((unsigned long)iterator->_arg + size -
+ (unsigned long)iterator->_rtheader >
+ (unsigned long)iterator->_max_length)
+ return -EINVAL;
+
+ oui = (*iterator->_arg << 16) |
+ (*(iterator->_arg + 1) << 8) |
+ *(iterator->_arg + 2);
+ subns = *(iterator->_arg + 3);
+
+ find_ns(iterator, oui, subns);
+
+ vnslen = get_unaligned_le16(iterator->_arg + 4);
+ iterator->_next_ns_data = iterator->_arg + size + vnslen;
+ if (!iterator->current_namespace)
+ size += vnslen;
+ }
/*
* 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;
+ iterator->this_arg_index = iterator->_arg_index;
+ iterator->this_arg = iterator->_arg;
+ iterator->this_arg_size = size;
/* internally move on the size of this arg */
- iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
+ iterator->_arg += size;
/*
* check for insanity where we are given a bitmap that
@@ -257,31 +337,57 @@
* max_length on the last arg, never exceeding it.
*/
- if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
- (ulong) iterator->max_length)
+ if ((unsigned long)iterator->_arg -
+ (unsigned long)iterator->_rtheader >
+ (unsigned long)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;
+ /* these special ones are valid in each bitmap word */
+ switch (iterator->_arg_index % 32) {
+ case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+ iterator->_reset_on_ext = 1;
+
+ iterator->is_radiotap_ns = 0;
+ /*
+ * If parser didn't register this vendor
+ * namespace with us, allow it to show it
+ * as 'raw. Do do that, set argument index
+ * to vendor namespace.
+ */
+ iterator->this_arg_index =
+ IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
+ if (!iterator->current_namespace)
+ hit = 1;
+ goto next_entry;
+ case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+ iterator->_reset_on_ext = 1;
+ iterator->current_namespace = &radiotap_ns;
+ iterator->is_radiotap_ns = 1;
+ goto next_entry;
+ case IEEE80211_RADIOTAP_EXT:
+ /*
+ * bit 31 was set, there is more
+ * -- move to next u32 bitmap
+ */
+ iterator->_bitmap_shifter =
+ get_unaligned_le32(iterator->_next_bitmap);
+ iterator->_next_bitmap++;
+ if (iterator->_reset_on_ext)
+ iterator->_arg_index = 0;
+ else
+ iterator->_arg_index++;
+ iterator->_reset_on_ext = 0;
+ break;
+ default:
+ /* we've got a hit! */
+ hit = 1;
+ next_entry:
+ iterator->_bitmap_shifter >>= 1;
+ iterator->_arg_index++;
+ }
/* 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;
}
diff --git a/src/utils/radiotap.h b/src/utils/radiotap.h
index 137288f..0572e7c 100644
--- a/src/utils/radiotap.h
+++ b/src/utils/radiotap.h
@@ -1,6 +1,3 @@
-/* $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.
*
@@ -178,6 +175,14 @@
*
* Number of unicast retries a transmitted frame used.
*
+ * IEEE80211_RADIOTAP_MCS u8, u8, u8 unitless
+ *
+ * Contains a bitmap of known fields/flags, the flags, and
+ * the MCS index.
+ *
+ * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitlesss
+ *
+ * Contains the AMPDU information for the subframe.
*/
enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_TSFT = 0,
@@ -198,6 +203,13 @@
IEEE80211_RADIOTAP_TX_FLAGS = 15,
IEEE80211_RADIOTAP_RTS_RETRIES = 16,
IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+
+ IEEE80211_RADIOTAP_MCS = 19,
+ IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+
+ /* valid in every it_present bitmap, even vendor namespaces */
+ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
+ IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30,
IEEE80211_RADIOTAP_EXT = 31
};
@@ -230,8 +242,10 @@
* 802.11 header and payload
* (to 32-bit boundary)
*/
+#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* frame failed FCS check */
+
/* For IEEE80211_RADIOTAP_RX_FLAGS */
-#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */
+#define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* bad PLCP */
/* For IEEE80211_RADIOTAP_TX_FLAGS */
#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive
@@ -240,4 +254,38 @@
#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */
#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */
+/* For IEEE80211_RADIOTAP_AMPDU_STATUS */
+#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001
+#define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN 0x0002
+#define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN 0x0004
+#define IEEE80211_RADIOTAP_AMPDU_IS_LAST 0x0008
+#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010
+#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020
+
+/* For IEEE80211_RADIOTAP_MCS */
+#define IEEE80211_RADIOTAP_MCS_HAVE_BW 0x01
+#define IEEE80211_RADIOTAP_MCS_HAVE_MCS 0x02
+#define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04
+#define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08
+#define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10
+#define IEEE80211_RADIOTAP_MCS_HAVE_STBC 0x20
+#define IEEE80211_RADIOTAP_MCS_HAVE_NESS 0x40
+#define IEEE80211_RADIOTAP_MCS_NESS_BIT1 0x80
+
+
+#define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03
+#define IEEE80211_RADIOTAP_MCS_BW_20 0
+#define IEEE80211_RADIOTAP_MCS_BW_40 1
+#define IEEE80211_RADIOTAP_MCS_BW_20L 2
+#define IEEE80211_RADIOTAP_MCS_BW_20U 3
+#define IEEE80211_RADIOTAP_MCS_SGI 0x04
+#define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08
+#define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10
+#define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60
+#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5
+#define IEEE80211_RADIOTAP_MCS_STBC_1 1
+#define IEEE80211_RADIOTAP_MCS_STBC_2 2
+#define IEEE80211_RADIOTAP_MCS_STBC_3 3
+#define IEEE80211_RADIOTAP_MCS_NESS_BIT0 0x80
+
#endif /* IEEE80211_RADIOTAP_H */
diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h
index 2e0e872..b768c85 100644
--- a/src/utils/radiotap_iter.h
+++ b/src/utils/radiotap_iter.h
@@ -1,56 +1,96 @@
-/*
- * Radiotap parser
- *
- * Copyright 2007 Andy Green <andy@warmcat.com>
- *
- * 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 <stdint.h>
#include "radiotap.h"
/* Radiotap header iteration
* implemented in radiotap.c
*/
+
+struct radiotap_override {
+ uint8_t field;
+ uint8_t align:4, size:4;
+};
+
+struct radiotap_align_size {
+ uint8_t align:4, size:4;
+};
+
+struct ieee80211_radiotap_namespace {
+ const struct radiotap_align_size *align_size;
+ int n_bits;
+ uint32_t oui;
+ uint8_t subns;
+};
+
+struct ieee80211_radiotap_vendor_namespaces {
+ const struct ieee80211_radiotap_namespace *ns;
+ int n_ns;
+};
+
/**
* 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
+ * @this_arg_index: index of current arg, valid after each successful call
+ * to ieee80211_radiotap_iterator_next()
+ * @this_arg: pointer to current radiotap arg; it is valid after each
+ * call to ieee80211_radiotap_iterator_next() but also after
+ * ieee80211_radiotap_iterator_init() where it will point to
+ * the beginning of the actual data portion
+ * @this_arg_size: length of the current arg, for convenience
+ * @current_namespace: pointer to the current namespace definition
+ * (or internally %NULL if the current namespace is unknown)
+ * @is_radiotap_ns: indicates whether the current namespace is the default
+ * radiotap namespace or not
+ *
+ * @overrides: override standard radiotap fields
+ * @n_overrides: number of overrides
+ *
+ * @_rtheader: pointer to the radiotap header we are walking through
+ * @_max_length: length of radiotap header in cpu byte ordering
+ * @_arg_index: next argument index
+ * @_arg: next argument pointer
+ * @_next_bitmap: internal pointer to next present u32
+ * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @_vns: vendor namespace definitions
+ * @_next_ns_data: beginning of the next namespace's data
+ * @_reset_on_ext: internal; reset the arg index to 0 when going to the
+ * next bitmap word
+ *
+ * Describes the radiotap parser state. Fields prefixed with an underscore
+ * must not be used by users of the parser, only by the parser internally.
*/
struct ieee80211_radiotap_iterator {
- struct ieee80211_radiotap_header *rtheader;
- int max_length;
- int this_arg_index;
- unsigned char *this_arg;
+ struct ieee80211_radiotap_header *_rtheader;
+ const struct ieee80211_radiotap_vendor_namespaces *_vns;
+ const struct ieee80211_radiotap_namespace *current_namespace;
- int arg_index;
- unsigned char *arg;
- uint32_t *next_bitmap;
- uint32_t bitmap_shifter;
+ unsigned char *_arg, *_next_ns_data;
+ uint32_t *_next_bitmap;
+
+ unsigned char *this_arg;
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+ const struct radiotap_override *overrides;
+ int n_overrides;
+#endif
+ int this_arg_index;
+ int this_arg_size;
+
+ int is_radiotap_ns;
+
+ int _max_length;
+ int _arg_index;
+ uint32_t _bitmap_shifter;
+ int _reset_on_ext;
};
extern int ieee80211_radiotap_iterator_init(
- struct ieee80211_radiotap_iterator *iterator,
- struct ieee80211_radiotap_header *radiotap_header,
- int max_length);
+ struct ieee80211_radiotap_iterator *iterator,
+ struct ieee80211_radiotap_header *radiotap_header,
+ int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns);
extern int ieee80211_radiotap_iterator_next(
- struct ieee80211_radiotap_iterator *iterator);
+ struct ieee80211_radiotap_iterator *iterator);
#endif /* __RADIOTAP_ITER_H */
diff --git a/src/utils/trace.c b/src/utils/trace.c
index 6795d41..6044f5f 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -18,11 +18,9 @@
#ifdef WPA_TRACE_BFD
#include <bfd.h>
-#ifdef __linux__
-#include <demangle.h>
-#else /* __linux__ */
-#include <libiberty/demangle.h>
-#endif /* __linux__ */
+
+#define DMGL_PARAMS (1 << 0)
+#define DMGL_ANSI (1 << 1)
static char *prg_fname = NULL;
static bfd *cached_abfd = NULL;
@@ -187,6 +185,7 @@
wpa_printf(MSG_INFO, " %s() %s:%u",
name, filename, data.line);
free(aname);
+ aname = NULL;
data.found = bfd_find_inliner_info(abfd, &data.filename,
&data.function, &data.line);
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
new file mode 100644
index 0000000..9a9ec40
--- /dev/null
+++ b/src/utils/utils_module_tests.c
@@ -0,0 +1,266 @@
+/*
+ * utils module tests
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/bitfield.h"
+#include "utils/ext_password.h"
+#include "utils/trace.h"
+
+
+struct printf_test_data {
+ u8 *data;
+ size_t len;
+ char *encoded;
+};
+
+static const struct printf_test_data printf_tests[] = {
+ { (u8 *) "abcde", 5, "abcde" },
+ { (u8 *) "a\0b\nc\ed\re\tf\"\\", 13, "a\\0b\\nc\\ed\\re\\tf\\\"\\\\" },
+ { (u8 *) "\x00\x31\x00\x32\x00\x39", 6, "\\x001\\0002\\09" },
+ { (u8 *) "\n\n\n", 3, "\n\12\x0a" },
+ { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12,
+ "\\xc3\\xa5\xc3\\xa4\\xc3\\xb6\\xc3\\x85\\xc3\\x84\\xc3\\x96" },
+ { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12,
+ "\\303\\245\\303\\244\\303\\266\\303\\205\\303\\204\\303\\226" },
+ { (u8 *) "\xe5\xe4\xf6\xc5\xc4\xd6", 6,
+ "\\xe5\\xe4\\xf6\\xc5\\xc4\\xd6" },
+ { NULL, 0, NULL }
+};
+
+
+static int printf_encode_decode_tests(void)
+{
+ int i;
+ size_t binlen;
+ char buf[100];
+ u8 bin[100];
+ int errors = 0;
+
+ wpa_printf(MSG_INFO, "printf encode/decode tests");
+
+ for (i = 0; printf_tests[i].data; i++) {
+ const struct printf_test_data *test = &printf_tests[i];
+ printf_encode(buf, sizeof(buf), test->data, test->len);
+ wpa_printf(MSG_INFO, "%d: -> \"%s\"", i, buf);
+
+ binlen = printf_decode(bin, sizeof(bin), buf);
+ if (binlen != test->len ||
+ os_memcmp(bin, test->data, binlen) != 0) {
+ wpa_hexdump(MSG_ERROR, "Error in decoding#1",
+ bin, binlen);
+ errors++;
+ }
+
+ binlen = printf_decode(bin, sizeof(bin), test->encoded);
+ if (binlen != test->len ||
+ os_memcmp(bin, test->data, binlen) != 0) {
+ wpa_hexdump(MSG_ERROR, "Error in decoding#2",
+ bin, binlen);
+ errors++;
+ }
+ }
+
+ buf[5] = 'A';
+ printf_encode(buf, 5, (const u8 *) "abcde", 5);
+ if (buf[5] != 'A') {
+ wpa_printf(MSG_ERROR, "Error in bounds checking#1");
+ errors++;
+ }
+
+ for (i = 5; i < 10; i++) {
+ buf[i] = 'A';
+ printf_encode(buf, i, (const u8 *) "\xdd\xdd\xdd\xdd\xdd", 5);
+ if (buf[i] != 'A') {
+ wpa_printf(MSG_ERROR, "Error in bounds checking#2(%d)",
+ i);
+ errors++;
+ }
+ }
+
+ if (errors) {
+ wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int bitfield_tests(void)
+{
+ struct bitfield *bf;
+ int i;
+ int errors = 0;
+
+ wpa_printf(MSG_INFO, "bitfield tests");
+
+ bf = bitfield_alloc(123);
+ if (bf == NULL)
+ return -1;
+
+ for (i = 0; i < 123; i++) {
+ if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+ errors++;
+ if (i > 0 && bitfield_is_set(bf, i - 1))
+ errors++;
+ bitfield_set(bf, i);
+ if (!bitfield_is_set(bf, i))
+ errors++;
+ bitfield_clear(bf, i);
+ if (bitfield_is_set(bf, i))
+ errors++;
+ }
+
+ for (i = 123; i < 200; i++) {
+ if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+ errors++;
+ if (i > 0 && bitfield_is_set(bf, i - 1))
+ errors++;
+ bitfield_set(bf, i);
+ if (bitfield_is_set(bf, i))
+ errors++;
+ bitfield_clear(bf, i);
+ if (bitfield_is_set(bf, i))
+ errors++;
+ }
+
+ for (i = 0; i < 123; i++) {
+ if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+ errors++;
+ bitfield_set(bf, i);
+ if (!bitfield_is_set(bf, i))
+ errors++;
+ }
+
+ for (i = 0; i < 123; i++) {
+ if (!bitfield_is_set(bf, i))
+ errors++;
+ bitfield_clear(bf, i);
+ if (bitfield_is_set(bf, i))
+ errors++;
+ }
+
+ for (i = 0; i < 123; i++) {
+ if (bitfield_get_first_zero(bf) != i)
+ errors++;
+ bitfield_set(bf, i);
+ }
+ if (bitfield_get_first_zero(bf) != -1)
+ errors++;
+ for (i = 0; i < 123; i++) {
+ if (!bitfield_is_set(bf, i))
+ errors++;
+ bitfield_clear(bf, i);
+ if (bitfield_get_first_zero(bf) != i)
+ errors++;
+ bitfield_set(bf, i);
+ }
+ if (bitfield_get_first_zero(bf) != -1)
+ errors++;
+
+ bitfield_free(bf);
+
+ if (errors) {
+ wpa_printf(MSG_ERROR, "%d bitfield test(s) failed", errors);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int int_array_tests(void)
+{
+ int test1[] = { 1, 2, 3, 4, 5, 6, 0 };
+ int test2[] = { 1, -1, 0 };
+ int test3[] = { 1, 1, 1, -1, 2, 3, 4, 1, 2, 0 };
+ int test3_res[] = { -1, 1, 2, 3, 4, 0 };
+ int errors = 0;
+ int len;
+
+ wpa_printf(MSG_INFO, "int_array tests");
+
+ if (int_array_len(test1) != 6 ||
+ int_array_len(test2) != 2)
+ errors++;
+
+ int_array_sort_unique(test3);
+ len = int_array_len(test3_res);
+ if (int_array_len(test3) != len)
+ errors++;
+ else if (os_memcmp(test3, test3_res, len * sizeof(int)) != 0)
+ errors++;
+
+ if (errors) {
+ wpa_printf(MSG_ERROR, "%d int_array test(s) failed", errors);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int ext_password_tests(void)
+{
+ struct ext_password_data *data;
+ int ret = 0;
+ struct wpabuf *pw;
+
+ wpa_printf(MSG_INFO, "ext_password tests");
+
+ data = ext_password_init("unknown", "foo");
+ if (data != NULL)
+ return -1;
+
+ data = ext_password_init("test", NULL);
+ if (data == NULL)
+ return -1;
+ pw = ext_password_get(data, "foo");
+ if (pw != NULL)
+ ret = -1;
+ ext_password_free(pw);
+
+ ext_password_deinit(data);
+
+ pw = ext_password_get(NULL, "foo");
+ if (pw != NULL)
+ ret = -1;
+ ext_password_free(pw);
+
+ return ret;
+}
+
+
+static int trace_tests(void)
+{
+ wpa_printf(MSG_INFO, "trace tests");
+
+ wpa_trace_show("test backtrace");
+ wpa_trace_dump_funcname("test funcname", trace_tests);
+
+ return 0;
+}
+
+
+int utils_module_tests(void)
+{
+ int ret = 0;
+
+ wpa_printf(MSG_INFO, "utils module tests");
+
+ if (printf_encode_decode_tests() < 0 ||
+ ext_password_tests() < 0 ||
+ trace_tests() < 0 ||
+ bitfield_tests() < 0 ||
+ int_array_tests() < 0)
+ ret = -1;
+
+ return ret;
+}
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 38ea8aa..68cbace 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -375,19 +375,19 @@
#endif /* CONFIG_ANDROID_LOG */
}
-void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
+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 u8 *buf, size_t len)
+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 u8 *buf,
+static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
size_t len, int show)
{
size_t i, llen;
@@ -407,7 +407,7 @@
/* can do ascii processing in userspace */
for (i = 0; i < len; i++)
fprintf(wpa_debug_tracing_file,
- " %02x", buf[i]);
+ " %02x", pos[i]);
}
fflush(wpa_debug_tracing_file);
}
@@ -495,13 +495,14 @@
}
-void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
+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 u8 *buf,
+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);
@@ -595,10 +596,14 @@
{
va_list ap;
char *buf;
- const int buflen = 2048;
+ int buflen;
int len;
char prefix[130];
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
buf = os_malloc(buflen);
if (buf == NULL) {
wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
@@ -629,12 +634,16 @@
{
va_list ap;
char *buf;
- const int buflen = 2048;
+ int buflen;
int len;
if (!wpa_msg_cb)
return;
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
buf = os_malloc(buflen);
if (buf == NULL) {
wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
@@ -653,9 +662,13 @@
{
va_list ap;
char *buf;
- const int buflen = 2048;
+ int buflen;
int len;
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
buf = os_malloc(buflen);
if (buf == NULL) {
wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate "
@@ -672,13 +685,45 @@
}
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ if (!wpa_msg_cb)
+ return;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "wpa_msg_global_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, 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 buflen;
int len;
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
buf = os_malloc(buflen);
if (buf == NULL) {
wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate "
@@ -711,9 +756,13 @@
{
va_list ap;
char *buf;
- const int buflen = 2048;
+ int buflen;
int len;
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
buf = os_malloc(buflen);
if (buf == NULL) {
wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 2ed1bd8..391f197 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -11,6 +11,10 @@
#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. */
@@ -77,7 +81,7 @@
* 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 u8 *buf, size_t len);
+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)
@@ -99,7 +103,7 @@
* 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 u8 *buf, size_t len);
+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)
@@ -121,7 +125,7 @@
* 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 u8 *buf,
+void wpa_hexdump_ascii(int level, const char *title, const void *buf,
size_t len);
/**
@@ -138,7 +142,7 @@
* 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 u8 *buf,
+void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
size_t len);
/*
@@ -156,6 +160,7 @@
#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_global_ctrl(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)
@@ -208,6 +213,21 @@
PRINTF_FORMAT(3, 4);
/**
+ * wpa_msg_global_ctrl - Conditional 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_global(), but it sends the output only to the
+ * attached global 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_global_ctrl(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()
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
new file mode 100644
index 0000000..4916d29
--- /dev/null
+++ b/src/utils/xml-utils.c
@@ -0,0 +1,471 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "xml-utils.h"
+
+
+static xml_node_t * get_node_uri_iter(struct xml_node_ctx *ctx,
+ xml_node_t *root, char *uri)
+{
+ char *end;
+ xml_node_t *node;
+ const char *name;
+
+ end = strchr(uri, '/');
+ if (end)
+ *end++ = '\0';
+
+ node = root;
+ xml_node_for_each_sibling(ctx, node) {
+ xml_node_for_each_check(ctx, node);
+ name = xml_node_get_localname(ctx, node);
+ if (strcasecmp(name, uri) == 0)
+ break;
+ }
+
+ if (node == NULL)
+ return NULL;
+
+ if (end) {
+ return get_node_uri_iter(ctx, xml_node_first_child(ctx, node),
+ end);
+ }
+
+ return node;
+}
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *uri)
+{
+ char *search;
+ xml_node_t *node;
+
+ search = os_strdup(uri);
+ if (search == NULL)
+ return NULL;
+
+ node = get_node_uri_iter(ctx, root, search);
+
+ os_free(search);
+ return node;
+}
+
+
+static xml_node_t * get_node_iter(struct xml_node_ctx *ctx,
+ xml_node_t *root, const char *path)
+{
+ char *end;
+ xml_node_t *node;
+ const char *name;
+
+ end = os_strchr(path, '/');
+ if (end)
+ *end++ = '\0';
+
+ xml_node_for_each_child(ctx, node, root) {
+ xml_node_for_each_check(ctx, node);
+ name = xml_node_get_localname(ctx, node);
+ if (os_strcasecmp(name, path) == 0)
+ break;
+ }
+
+ if (node == NULL)
+ return NULL;
+ if (end)
+ return get_node_iter(ctx, node, end);
+ return node;
+}
+
+
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *path)
+{
+ char *search;
+ xml_node_t *node;
+
+ search = os_strdup(path);
+ if (search == NULL)
+ return NULL;
+
+ node = get_node_iter(ctx, root, search);
+
+ os_free(search);
+ return node;
+}
+
+
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *path)
+{
+ xml_node_t *node;
+ xml_node_t *match;
+
+ xml_node_for_each_child(ctx, node, root) {
+ xml_node_for_each_check(ctx, node);
+ match = get_node(ctx, node, path);
+ if (match)
+ return match;
+ }
+
+ return NULL;
+}
+
+
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name)
+{
+ xml_node_t *node;
+ char *buf, *buf2, *start;
+ size_t len;
+
+ buf = os_readfile(name, &len);
+ if (buf == NULL)
+ return NULL;
+ buf2 = os_realloc(buf, len + 1);
+ if (buf2 == NULL) {
+ os_free(buf);
+ return NULL;
+ }
+ buf = buf2;
+ buf[len] = '\0';
+
+ start = os_strstr(buf, "<!DOCTYPE ");
+ if (start) {
+ char *pos = start + 1;
+ int count = 1;
+ while (*pos) {
+ if (*pos == '<')
+ count++;
+ else if (*pos == '>') {
+ count--;
+ if (count == 0) {
+ pos++;
+ break;
+ }
+ }
+ pos++;
+ }
+ if (count == 0) {
+ /* Remove DOCTYPE to allow the file to be parsed */
+ os_memset(start, ' ', pos - start);
+ }
+ }
+
+ node = xml_node_from_buf(ctx, buf);
+ os_free(buf);
+
+ return node;
+}
+
+
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node)
+{
+ FILE *f;
+ char *str;
+
+ str = xml_node_to_str(ctx, node);
+ if (str == NULL)
+ return -1;
+
+ f = fopen(fname, "w");
+ if (!f) {
+ os_free(str);
+ return -1;
+ }
+
+ fprintf(f, "%s\n", str);
+ os_free(str);
+ fclose(f);
+
+ return 0;
+}
+
+
+static char * get_val(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ char *val, *pos;
+
+ val = xml_node_get_text(ctx, node);
+ if (val == NULL)
+ return NULL;
+ pos = val;
+ while (*pos) {
+ if (*pos != ' ' && *pos != '\t' && *pos != '\r' && *pos != '\n')
+ return val;
+ pos++;
+ }
+
+ return NULL;
+}
+
+
+static char * add_path(const char *prev, const char *leaf)
+{
+ size_t len;
+ char *new_uri;
+
+ if (prev == NULL)
+ return NULL;
+
+ len = os_strlen(prev) + 1 + os_strlen(leaf) + 1;
+ new_uri = os_malloc(len);
+ if (new_uri)
+ os_snprintf(new_uri, len, "%s/%s", prev, leaf);
+
+ return new_uri;
+}
+
+
+static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out,
+ xml_node_t *in, const char *uri)
+{
+ xml_node_t *node;
+ xml_node_t *tnds;
+ const char *name;
+ char *val;
+ char *new_uri;
+
+ xml_node_for_each_child(ctx, node, in) {
+ xml_node_for_each_check(ctx, node);
+ name = xml_node_get_localname(ctx, node);
+
+ tnds = xml_node_create(ctx, out, NULL, "Node");
+ if (tnds == NULL)
+ return;
+ xml_node_create_text(ctx, tnds, NULL, "NodeName", name);
+
+ if (uri)
+ xml_node_create_text(ctx, tnds, NULL, "Path", uri);
+
+ val = get_val(ctx, node);
+ if (val) {
+ xml_node_create_text(ctx, tnds, NULL, "Value", val);
+ xml_node_get_text_free(ctx, val);
+ }
+
+ new_uri = add_path(uri, name);
+ node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
+ os_free(new_uri);
+ }
+}
+
+
+static int add_ddfname(struct xml_node_ctx *ctx, xml_node_t *parent,
+ const char *urn)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx, parent, NULL, "RTProperties");
+ if (node == NULL)
+ return -1;
+ node = xml_node_create(ctx, node, NULL, "Type");
+ if (node == NULL)
+ return -1;
+ xml_node_create_text(ctx, node, NULL, "DDFName", urn);
+ return 0;
+}
+
+
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+ int use_path, const char *urn, const char *ns_uri)
+{
+ xml_node_t *root;
+ xml_node_t *node;
+ const char *name;
+
+ root = xml_node_create_root(ctx, ns_uri, NULL, NULL, "MgmtTree");
+ if (root == NULL)
+ return NULL;
+
+ xml_node_create_text(ctx, root, NULL, "VerDTD", "1.2");
+
+ name = xml_node_get_localname(ctx, mo);
+
+ node = xml_node_create(ctx, root, NULL, "Node");
+ if (node == NULL)
+ goto fail;
+ xml_node_create_text(ctx, node, NULL, "NodeName", name);
+ if (urn)
+ add_ddfname(ctx, node, urn);
+
+ node_to_tnds(ctx, use_path ? root : node, mo, use_path ? name : NULL);
+
+ return root;
+
+fail:
+ xml_node_free(ctx, root);
+ return NULL;
+}
+
+
+static xml_node_t * get_first_child_node(struct xml_node_ctx *ctx,
+ xml_node_t *node,
+ const char *name)
+{
+ const char *lname;
+ xml_node_t *child;
+
+ xml_node_for_each_child(ctx, child, node) {
+ xml_node_for_each_check(ctx, child);
+ lname = xml_node_get_localname(ctx, child);
+ if (os_strcasecmp(lname, name) == 0)
+ return child;
+ }
+
+ return NULL;
+}
+
+
+static char * get_node_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *node_name)
+{
+ node = get_first_child_node(ctx, node, node_name);
+ if (node == NULL)
+ return NULL;
+ return xml_node_get_text(ctx, node);
+}
+
+
+static xml_node_t * add_mo_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ xml_node_t *node, const char *uri)
+{
+ char *nodename, *value, *path;
+ xml_node_t *parent;
+
+ nodename = get_node_text(ctx, node, "NodeName");
+ if (nodename == NULL)
+ return NULL;
+ value = get_node_text(ctx, node, "Value");
+
+ if (root == NULL) {
+ root = xml_node_create_root(ctx, NULL, NULL, NULL,
+ nodename);
+ if (root && value)
+ xml_node_set_text(ctx, root, value);
+ } else {
+ if (uri == NULL) {
+ xml_node_get_text_free(ctx, nodename);
+ xml_node_get_text_free(ctx, value);
+ return NULL;
+ }
+ path = get_node_text(ctx, node, "Path");
+ if (path)
+ uri = path;
+ parent = get_node_uri(ctx, root, uri);
+ xml_node_get_text_free(ctx, path);
+ if (parent == NULL) {
+ printf("Could not find URI '%s'\n", uri);
+ xml_node_get_text_free(ctx, nodename);
+ xml_node_get_text_free(ctx, value);
+ return NULL;
+ }
+ if (value)
+ xml_node_create_text(ctx, parent, NULL, nodename,
+ value);
+ else
+ xml_node_create(ctx, parent, NULL, nodename);
+ }
+
+ xml_node_get_text_free(ctx, nodename);
+ xml_node_get_text_free(ctx, value);
+
+ return root;
+}
+
+
+static xml_node_t * tnds_to_mo_iter(struct xml_node_ctx *ctx, xml_node_t *root,
+ xml_node_t *node, const char *uri)
+{
+ xml_node_t *child;
+ const char *name;
+ char *nodename;
+
+ xml_node_for_each_sibling(ctx, node) {
+ xml_node_for_each_check(ctx, node);
+
+ nodename = get_node_text(ctx, node, "NodeName");
+ if (nodename == NULL)
+ return NULL;
+
+ name = xml_node_get_localname(ctx, node);
+ if (strcmp(name, "Node") == 0) {
+ if (root && !uri) {
+ printf("Invalid TNDS tree structure - "
+ "multiple top level nodes\n");
+ xml_node_get_text_free(ctx, nodename);
+ return NULL;
+ }
+ root = add_mo_node(ctx, root, node, uri);
+ }
+
+ child = get_first_child_node(ctx, node, "Node");
+ if (child) {
+ if (uri == NULL)
+ tnds_to_mo_iter(ctx, root, child, nodename);
+ else {
+ char *new_uri;
+ new_uri = add_path(uri, nodename);
+ tnds_to_mo_iter(ctx, root, child, new_uri);
+ os_free(new_uri);
+ }
+ }
+ xml_node_get_text_free(ctx, nodename);
+ }
+
+ return root;
+}
+
+
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds)
+{
+ const char *name;
+ xml_node_t *node;
+
+ name = xml_node_get_localname(ctx, tnds);
+ if (name == NULL || os_strcmp(name, "MgmtTree") != 0)
+ return NULL;
+
+ node = get_first_child_node(ctx, tnds, "Node");
+ if (!node)
+ return NULL;
+ return tnds_to_mo_iter(ctx, NULL, node, NULL);
+}
+
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ xml_node_t *envelope, *body;
+ xml_namespace_t *ns;
+
+ envelope = xml_node_create_root(
+ ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns,
+ "Envelope");
+ if (envelope == NULL)
+ return NULL;
+ body = xml_node_create(ctx, envelope, ns, "Body");
+ xml_node_add_child(ctx, body, node);
+ return envelope;
+}
+
+
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap)
+{
+ xml_node_t *body, *child;
+
+ body = get_node_uri(ctx, soap, "Envelope/Body");
+ if (body == NULL)
+ return NULL;
+ xml_node_for_each_child(ctx, child, body) {
+ xml_node_for_each_check(ctx, child);
+ return child;
+ }
+ return NULL;
+}
diff --git a/src/utils/xml-utils.h b/src/utils/xml-utils.h
new file mode 100644
index 0000000..fb6208c
--- /dev/null
+++ b/src/utils/xml-utils.h
@@ -0,0 +1,97 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef XML_UTILS_H
+#define XML_UTILS_H
+
+struct xml_node_ctx;
+typedef struct xml_node xml_node_t;
+typedef struct xml_namespace_foo xml_namespace_t;
+
+/* XML library wrappers */
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *xml_schema_fname, char **ret_err);
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *dtd_fname, char **ret_err);
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf);
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+ xml_node_t *node);
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+ xml_node_t *child);
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+ const char *ns_prefix,
+ xml_namespace_t **ret_ns, const char *name);
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+ xml_namespace_t *ns, const char *name);
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+ xml_node_t *parent, xml_namespace_t *ns,
+ const char *name, const char *value);
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+ xml_node_t *parent, const char *ns_uri,
+ const char *name, const char *value);
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *value);
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+ xml_namespace_t *ns, const char *name, const char *value);
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+ char *name);
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *ns_uri, char *name);
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val);
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+ xml_node_t *parent);
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+ xml_node_t *node);
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node);
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val);
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ int *ret_len);
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node);
+
+#define xml_node_for_each_child(ctx, child, parent) \
+for (child = xml_node_first_child(ctx, parent); \
+ child; \
+ child = xml_node_next_sibling(ctx, child))
+
+#define xml_node_for_each_sibling(ctx, node) \
+for (; \
+ node; \
+ node = xml_node_next_sibling(ctx, node))
+
+#define xml_node_for_each_check(ctx, child) \
+if (!xml_node_is_element(ctx, child)) \
+ continue
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+ const void *env);
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx);
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *uri);
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *path);
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+ const char *path);
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name);
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node);
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+ int use_path, const char *urn, const char *ns_uri);
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds);
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap);
+
+#endif /* XML_UTILS_H */
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
new file mode 100644
index 0000000..c928394
--- /dev/null
+++ b/src/utils/xml_libxml2.c
@@ -0,0 +1,457 @@
+/*
+ * XML wrapper for libxml2
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#define LIBXML_VALID_ENABLED
+#include <libxml/tree.h>
+#include <libxml/xmlschemastypes.h>
+
+#include "common.h"
+#include "base64.h"
+#include "xml-utils.h"
+
+
+struct xml_node_ctx {
+ void *ctx;
+};
+
+
+struct str_buf {
+ char *buf;
+ size_t len;
+};
+
+#define MAX_STR 1000
+
+static void add_str(void *ctx_ptr, const char *fmt, ...)
+{
+ struct str_buf *str = ctx_ptr;
+ va_list ap;
+ char *n;
+ int len;
+
+ n = os_realloc(str->buf, str->len + MAX_STR + 2);
+ if (n == NULL)
+ return;
+ str->buf = n;
+
+ va_start(ap, fmt);
+ len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap);
+ va_end(ap);
+ if (len >= MAX_STR)
+ len = MAX_STR - 1;
+ str->len += len;
+ str->buf[str->len] = '\0';
+}
+
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *xml_schema_fname, char **ret_err)
+{
+ xmlDocPtr doc;
+ xmlNodePtr n;
+ xmlSchemaParserCtxtPtr pctx;
+ xmlSchemaValidCtxtPtr vctx;
+ xmlSchemaPtr schema;
+ int ret;
+ struct str_buf errors;
+
+ if (ret_err)
+ *ret_err = NULL;
+
+ doc = xmlNewDoc((xmlChar *) "1.0");
+ if (doc == NULL)
+ return -1;
+ n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+ if (n == NULL) {
+ xmlFreeDoc(doc);
+ return -1;
+ }
+ xmlDocSetRootElement(doc, n);
+
+ os_memset(&errors, 0, sizeof(errors));
+
+ pctx = xmlSchemaNewParserCtxt(xml_schema_fname);
+ xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str,
+ (xmlSchemaValidityWarningFunc) add_str,
+ &errors);
+ schema = xmlSchemaParse(pctx);
+ xmlSchemaFreeParserCtxt(pctx);
+
+ vctx = xmlSchemaNewValidCtxt(schema);
+ xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str,
+ (xmlSchemaValidityWarningFunc) add_str,
+ &errors);
+
+ ret = xmlSchemaValidateDoc(vctx, doc);
+ xmlSchemaFreeValidCtxt(vctx);
+ xmlFreeDoc(doc);
+ xmlSchemaFree(schema);
+
+ if (ret == 0) {
+ os_free(errors.buf);
+ return 0;
+ } else if (ret > 0) {
+ if (ret_err)
+ *ret_err = errors.buf;
+ else
+ os_free(errors.buf);
+ return -1;
+ } else {
+ if (ret_err)
+ *ret_err = errors.buf;
+ else
+ os_free(errors.buf);
+ return -1;
+ }
+}
+
+
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *dtd_fname, char **ret_err)
+{
+ xmlDocPtr doc;
+ xmlNodePtr n;
+ xmlValidCtxt vctx;
+ xmlDtdPtr dtd;
+ int ret;
+ struct str_buf errors;
+
+ if (ret_err)
+ *ret_err = NULL;
+
+ doc = xmlNewDoc((xmlChar *) "1.0");
+ if (doc == NULL)
+ return -1;
+ n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+ if (n == NULL) {
+ xmlFreeDoc(doc);
+ return -1;
+ }
+ xmlDocSetRootElement(doc, n);
+
+ os_memset(&errors, 0, sizeof(errors));
+
+ dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname);
+ if (dtd == NULL) {
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ os_memset(&vctx, 0, sizeof(vctx));
+ vctx.userData = &errors;
+ vctx.error = add_str;
+ vctx.warning = add_str;
+ ret = xmlValidateDtd(&vctx, doc, dtd);
+ xmlFreeDoc(doc);
+ xmlFreeDtd(dtd);
+
+ if (ret == 1) {
+ os_free(errors.buf);
+ return 0;
+ } else {
+ if (ret_err)
+ *ret_err = errors.buf;
+ else
+ os_free(errors.buf);
+ return -1;
+ }
+}
+
+
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ xmlFreeNode((xmlNodePtr) node);
+}
+
+
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ return (xml_node_t *) ((xmlNodePtr) node)->parent;
+}
+
+
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf)
+{
+ xmlDocPtr doc;
+ xmlNodePtr node;
+
+ doc = xmlParseMemory(buf, strlen(buf));
+ if (doc == NULL)
+ return NULL;
+ node = xmlDocGetRootElement(doc);
+ node = xmlCopyNode(node, 1);
+ xmlFreeDoc(doc);
+
+ return (xml_node_t *) node;
+}
+
+
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+ xml_node_t *node)
+{
+ return (const char *) ((xmlNodePtr) node)->name;
+}
+
+
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ xmlChar *buf;
+ int bufsiz;
+ char *ret, *pos;
+ xmlNodePtr n = (xmlNodePtr) node;
+ xmlDocPtr doc;
+
+ doc = xmlNewDoc((xmlChar *) "1.0");
+ n = xmlDocCopyNode(n, doc, 1);
+ xmlDocSetRootElement(doc, n);
+ xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0);
+ xmlFreeDoc(doc);
+ pos = (char *) buf;
+ if (strncmp(pos, "<?xml", 5) == 0) {
+ pos = strchr(pos, '>');
+ if (pos)
+ pos++;
+ while (pos && (*pos == '\r' || *pos == '\n'))
+ pos++;
+ }
+ if (pos)
+ ret = os_strdup(pos);
+ else
+ ret = NULL;
+ xmlFree(buf);
+
+ if (ret) {
+ pos = ret;
+ if (pos[0]) {
+ while (pos[1])
+ pos++;
+ }
+ while (pos >= ret && *pos == '\n')
+ *pos-- = '\0';
+ }
+
+ return ret;
+}
+
+
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ xmlUnlinkNode((xmlNodePtr) node);
+}
+
+
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+ xml_node_t *child)
+{
+ xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child);
+}
+
+
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+ const char *ns_prefix,
+ xml_namespace_t **ret_ns, const char *name)
+{
+ xmlNodePtr node;
+ xmlNsPtr ns = NULL;
+
+ node = xmlNewNode(NULL, (const xmlChar *) name);
+ if (node == NULL)
+ return NULL;
+ if (ns_uri) {
+ ns = xmlNewNs(node, (const xmlChar *) ns_uri,
+ (const xmlChar *) ns_prefix);
+ xmlSetNs(node, ns);
+ }
+
+ if (ret_ns)
+ *ret_ns = (xml_namespace_t *) ns;
+
+ return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+ xml_namespace_t *ns, const char *name)
+{
+ xmlNodePtr node;
+ node = xmlNewChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+ (const xmlChar *) name, NULL);
+ return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+ xml_node_t *parent, xml_namespace_t *ns,
+ const char *name, const char *value)
+{
+ xmlNodePtr node;
+ node = xmlNewTextChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+ (const xmlChar *) name, (const xmlChar *) value);
+ return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+ xml_node_t *parent, const char *ns_uri,
+ const char *name, const char *value)
+{
+ xmlNodePtr node;
+ xmlNsPtr ns;
+
+ node = xmlNewTextChild((xmlNodePtr) parent, NULL,
+ (const xmlChar *) name, (const xmlChar *) value);
+ ns = xmlNewNs(node, (const xmlChar *) ns_uri, NULL);
+ xmlSetNs(node, ns);
+ return (xml_node_t *) node;
+}
+
+
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *value)
+{
+ /* TODO: escape XML special chars in value */
+ xmlNodeSetContent((xmlNodePtr) node, (xmlChar *) value);
+}
+
+
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+ xml_namespace_t *ns, const char *name, const char *value)
+{
+ xmlAttrPtr attr;
+
+ if (ns) {
+ attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns,
+ (const xmlChar *) name,
+ (const xmlChar *) value);
+ } else {
+ attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name,
+ (const xmlChar *) value);
+ }
+
+ return attr ? 0 : -1;
+}
+
+
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+ char *name)
+{
+ return (char *) xmlGetNoNsProp((xmlNodePtr) node,
+ (const xmlChar *) name);
+}
+
+
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+ const char *ns_uri, char *name)
+{
+ return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name,
+ (const xmlChar *) ns_uri);
+}
+
+
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val)
+{
+ if (val)
+ xmlFree((xmlChar *) val);
+}
+
+
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+ xml_node_t *parent)
+{
+ return (xml_node_t *) ((xmlNodePtr) parent)->children;
+}
+
+
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+ xml_node_t *node)
+{
+ return (xml_node_t *) ((xmlNodePtr) node)->next;
+}
+
+
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ return ((xmlNodePtr) node)->type == XML_ELEMENT_NODE;
+}
+
+
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ if (xmlChildElementCount((xmlNodePtr) node) > 0)
+ return NULL;
+ return (char *) xmlNodeGetContent((xmlNodePtr) node);
+}
+
+
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val)
+{
+ if (val)
+ xmlFree((xmlChar *) val);
+}
+
+
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+ int *ret_len)
+{
+ char *txt;
+ unsigned char *ret;
+ size_t len;
+
+ txt = xml_node_get_text(ctx, node);
+ if (txt == NULL)
+ return NULL;
+
+ ret = base64_decode((unsigned char *) txt, strlen(txt), &len);
+ if (ret_len)
+ *ret_len = len;
+ xml_node_get_text_free(ctx, txt);
+ if (ret == NULL)
+ return NULL;
+ txt = os_malloc(len + 1);
+ if (txt == NULL) {
+ os_free(ret);
+ return NULL;
+ }
+ os_memcpy(txt, ret, len);
+ txt[len] = '\0';
+ return txt;
+}
+
+
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+ if (node == NULL)
+ return NULL;
+ return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1);
+}
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+ const void *env)
+{
+ struct xml_node_ctx *xctx;
+
+ xctx = os_zalloc(sizeof(*xctx));
+ if (xctx == NULL)
+ return NULL;
+ xctx->ctx = upper_ctx;
+
+ LIBXML_TEST_VERSION
+
+ return xctx;
+}
+
+
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx)
+{
+ xmlSchemaCleanupTypes();
+ xmlCleanupParser();
+ xmlMemoryDump();
+ os_free(ctx);
+}
diff --git a/src/wps/Makefile b/src/wps/Makefile
index 9c41962..adfd3df 100644
--- a/src/wps/Makefile
+++ b/src/wps/Makefile
@@ -2,7 +2,7 @@
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/wps/http_server.c b/src/wps/http_server.c
index 6ca3214..ac088c4 100644
--- a/src/wps/http_server.c
+++ b/src/wps/http_server.c
@@ -232,6 +232,7 @@
{
struct sockaddr_in sin;
struct http_server *srv;
+ int on = 1;
srv = os_zalloc(sizeof(*srv));
if (srv == NULL)
@@ -242,6 +243,15 @@
srv->fd = socket(AF_INET, SOCK_STREAM, 0);
if (srv->fd < 0)
goto fail;
+
+ if (setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+ {
+ wpa_printf(MSG_DEBUG,
+ "HTTP: setsockopt(SO_REUSEADDR) failed: %s",
+ strerror(errno));
+ /* try to continue anyway */
+ }
+
if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
goto fail;
if (port < 0)
diff --git a/src/wps/httpread.c b/src/wps/httpread.c
index ad4f4a1..2f08f37 100644
--- a/src/wps/httpread.c
+++ b/src/wps/httpread.c
@@ -67,8 +67,6 @@
int timeout_seconds; /* 0 or total duration timeout period */
/* dynamically used information follows */
- int sd_registered; /* nonzero if we need to unregister socket */
- int to_registered; /* nonzero if we need to unregister timeout */
int got_hdr; /* nonzero when header is finalized */
char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */
@@ -129,19 +127,6 @@
}
-/* convert hex to binary
- * Requires that c have been previously tested true with isxdigit().
- */
-static int hex_value(int c)
-{
- if (isdigit(c))
- return c - '0';
- if (islower(c))
- return 10 + c - 'a';
- return 10 + c - 'A';
-}
-
-
static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
/* httpread_destroy -- if h is non-NULL, clean up
@@ -156,12 +141,8 @@
if (!h)
return;
- if (h->to_registered)
- eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
- h->to_registered = 0;
- if (h->sd_registered)
- eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
- h->sd_registered = 0;
+ 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 */
@@ -176,7 +157,6 @@
{
struct httpread *h = user_ctx;
wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
- h->to_registered = 0; /* is self-cancelling */
(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
}
@@ -295,8 +275,7 @@
int c = *rawuri;
if (c == '%' &&
isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
- *uri++ = (hex_value(rawuri[1]) << 4) |
- hex_value(rawuri[2]);
+ *uri++ = hex2byte(rawuri + 1);
rawuri += 3;
} else {
*uri++ = c;
@@ -434,8 +413,8 @@
*/
if (httpread_debug >= 10)
wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
- h->got_body = 1;
- goto got_file;
+ h->got_body = 1;
+ goto got_file;
}
rbp = readbuf;
@@ -638,7 +617,6 @@
* 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)
@@ -703,15 +681,11 @@
* and just in case somehow we don't get destroyed right away,
* unregister now.
*/
- if (h->sd_registered)
- eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
- h->sd_registered = 0;
+ eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
/* The application can destroy us whenever they feel like...
* cancel timeout.
*/
- if (h->to_registered)
- eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
- h->to_registered = 0;
+ eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
(*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
}
@@ -749,21 +723,17 @@
h->max_bytes = max_bytes;
h->timeout_seconds = timeout_seconds;
- if (timeout_seconds > 0) {
- if (eloop_register_timeout(timeout_seconds, 0,
- httpread_timeout_handler,
- NULL, h)) {
- /* No way to recover (from malloc failure) */
- goto fail;
- }
- h->to_registered = 1;
+ 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;
}
- h->sd_registered = 1;
return h;
fail:
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 96685d2..d45dfc8 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -30,6 +30,7 @@
};
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)
@@ -147,7 +148,8 @@
static int wifi_filter(struct ndef_record *record)
{
- if (record->type_length != os_strlen(wifi_handover_type))
+ if (record->type == NULL ||
+ record->type_length != os_strlen(wifi_handover_type))
return 0;
if (os_memcmp(record->type, wifi_handover_type,
os_strlen(wifi_handover_type)) != 0)
@@ -170,85 +172,27 @@
}
-struct wpabuf * ndef_build_wifi_hc(int begin)
+static int p2p_filter(struct ndef_record *record)
{
- struct wpabuf *hc, *carrier;
-
- carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
- if (carrier == NULL)
- return NULL;
- wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
- wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
- wpabuf_put_str(carrier, wifi_handover_type);
-
- hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) |
- FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
- "0", 1, carrier);
- wpabuf_free(carrier);
-
- return hc;
+ if (record->type == NULL ||
+ 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_build_wifi_hr(void)
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
{
- struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
- struct wpabuf *hc;
+ return ndef_parse_records(buf, p2p_filter);
+}
- rn = wpabuf_alloc(2);
- if (rn == NULL)
- return NULL;
- wpabuf_put_be16(rn, os_random() & 0xffff);
- cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
- NULL, 0, rn);
- wpabuf_free(rn);
-
- if (cr == NULL)
- return NULL;
-
- ac_payload = wpabuf_alloc(4);
- if (ac_payload == NULL) {
- wpabuf_free(cr);
- return NULL;
- }
- wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
- wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
- wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
- wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
-
- ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
- NULL, 0, ac_payload);
- wpabuf_free(ac_payload);
- if (ac == NULL) {
- wpabuf_free(cr);
- return NULL;
- }
-
- hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
- if (hr_payload == NULL) {
- wpabuf_free(cr);
- wpabuf_free(ac);
- return NULL;
- }
-
- wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
- wpabuf_put_buf(hr_payload, cr);
- wpabuf_put_buf(hr_payload, ac);
- wpabuf_free(cr);
- wpabuf_free(ac);
-
- hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
- NULL, 0, hr_payload);
- wpabuf_free(hr_payload);
- if (hr == NULL)
- return NULL;
-
- hc = ndef_build_wifi_hc(0);
- if (hc == NULL) {
- wpabuf_free(hr);
- return NULL;
- }
-
- return wpabuf_concat(hr, hc);
+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);
}
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 22d7eea..b0f6887 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -18,6 +18,7 @@
#ifdef CONFIG_WPS_TESTING
int wps_version_number = 0x20;
int wps_testing_dummy_cred = 0;
+int wps_corrupt_pkhash = 0;
#endif /* CONFIG_WPS_TESTING */
@@ -58,6 +59,10 @@
}
#ifdef CONFIG_WPS_NFC
+ if (cfg->pin == NULL &&
+ cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+ data->dev_pw_id = cfg->dev_pw_id;
+
if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
/* Keep AP PIN as alternative Device Password */
data->alt_dev_pw_id = data->dev_pw_id;
@@ -84,7 +89,7 @@
if (cfg->pbc) {
/* Use special PIN '00000000' for PBC */
data->dev_pw_id = DEV_PW_PUSHBUTTON;
- os_free(data->dev_password);
+ bin_clear_free(data->dev_password, data->dev_password_len);
data->dev_password = (u8 *) os_strdup("00000000");
if (data->dev_password == NULL) {
os_free(data);
@@ -117,7 +122,8 @@
data->new_ap_settings =
os_malloc(sizeof(*data->new_ap_settings));
if (data->new_ap_settings == NULL) {
- os_free(data->dev_password);
+ bin_clear_free(data->dev_password,
+ data->dev_password_len);
os_free(data);
return NULL;
}
@@ -133,6 +139,12 @@
data->use_psk_key = cfg->use_psk_key;
data->pbc_in_m1 = cfg->pbc_in_m1;
+ if (cfg->peer_pubkey_hash) {
+ os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ data->peer_pubkey_hash_set = 1;
+ }
+
return data;
}
@@ -162,13 +174,12 @@
wpabuf_free(data->dh_pubkey_e);
wpabuf_free(data->dh_pubkey_r);
wpabuf_free(data->last_msg);
- os_free(data->dev_password);
- os_free(data->alt_dev_password);
- os_free(data->new_psk);
+ bin_clear_free(data->dev_password, data->dev_password_len);
+ bin_clear_free(data->alt_dev_password, data->alt_dev_password_len);
+ bin_clear_free(data->new_psk, data->new_psk_len);
wps_device_data_free(&data->peer_dev);
- os_free(data->new_ap_settings);
+ bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings));
dh5_free(data->dh_ctx);
- os_free(data->nfc_pw_token);
os_free(data);
}
@@ -501,13 +512,11 @@
wps_build_assoc_state(NULL, ie) ||
wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(ie, pw_id) ||
-#ifdef CONFIG_WPS2
wps_build_manufacturer(dev, ie) ||
wps_build_model_name(dev, ie) ||
wps_build_model_number(dev, ie) ||
wps_build_dev_name(dev, ie) ||
wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
-#endif /* CONFIG_WPS2 */
wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
||
wps_build_secondary_dev_type(dev, ie)
@@ -516,13 +525,6 @@
return NULL;
}
-#ifndef CONFIG_WPS2
- if (dev->p2p && wps_build_dev_name(dev, ie)) {
- wpabuf_free(ie);
- return NULL;
- }
-#endif /* CONFIG_WPS2 */
-
return wps_ie_encapsulate(ie);
}
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 15137a8..192d283 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -42,7 +42,6 @@
* @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
* this may be %NULL, if not used
* @cred_attr_len: Length of cred_attr in octets
- * @ap_channel: AP channel
*/
struct wps_credential {
u8 ssid[32];
@@ -55,7 +54,6 @@
u8 mac_addr[ETH_ALEN];
const u8 *cred_attr;
size_t cred_attr_len;
- u16 ap_channel;
};
#define WPS_DEV_TYPE_LEN 8
@@ -183,6 +181,11 @@
* PBC with the AP.
*/
int pbc_in_m1;
+
+ /**
+ * peer_pubkey_hash - Peer public key hash or %NULL if not known
+ */
+ const u8 *peer_pubkey_hash;
};
struct wps_data * wps_init(const struct wps_config *cfg);
@@ -665,6 +668,16 @@
u16 auth_types;
/**
+ * encr_types - Current AP encryption type (WPS_ENCR_*)
+ */
+ u16 ap_encr_type;
+
+ /**
+ * ap_auth_type - Current AP authentication types (WPS_AUTH_*)
+ */
+ u16 ap_auth_type;
+
+ /**
* network_key - The current Network Key (PSK) or %NULL to generate new
*
* If %NULL, Registrar will generate per-device PSK. In addition, AP
@@ -801,7 +814,8 @@
struct wps_credential *cred);
int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
const u8 *pubkey_hash, u16 pw_id,
- const u8 *dev_pw, size_t dev_pw_len);
+ const u8 *dev_pw, size_t dev_pw_len,
+ int pk_hash_provided_oob);
int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
const u8 *oob_dev_pw,
size_t oob_dev_pw_len);
@@ -815,7 +829,8 @@
int wps_pin_str_valid(const char *pin);
void wps_free_pending_msgs(struct upnp_pending_message *msgs);
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+ int channel);
int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
int wps_attr_text(struct wpabuf *data, char *buf, char *end);
const char * wps_ei_str(enum wps_error_indication ei);
@@ -839,6 +854,9 @@
struct wps_credential *cred);
struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
const u8 *addr);
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+ struct wps_context *wps, const u8 *uuid,
+ const u8 *addr, struct wpabuf *pubkey);
int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
@@ -850,15 +868,27 @@
const struct wpabuf *dev_pw);
struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
struct wpabuf *dev_pw);
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey);
struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
struct wpabuf **privkey,
struct wpabuf **dev_pw);
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey,
+ const u8 *bssid, int freq);
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+ int nfc_dev_pw_id,
+ struct wpabuf *nfc_dh_pubkey,
+ struct wpabuf *nfc_dev_pw);
/* ndef.c */
struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi_hc(int begin);
-struct wpabuf * ndef_build_wifi_hr(void);
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf);
#ifdef CONFIG_WPS_STRICT
int wps_validate_beacon(const struct wpabuf *wps_ie);
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index ac9bb1e..b689357 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -38,8 +38,15 @@
wps->wps->dh_ctx = NULL;
pubkey = wpabuf_dup(wps->wps->dh_pubkey);
#ifdef CONFIG_WPS_NFC
- } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
- wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+ } else if ((wps->dev_pw_id >= 0x10 ||
+ wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) &&
+ (wps->wps->ap ||
+ (wps->wps->ap_nfc_dh_pubkey &&
+ wps->wps->ap_nfc_dev_pw_id ==
+ DEV_PW_NFC_CONNECTION_HANDOVER &&
+ wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) &&
+ (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id ||
+ wps->wps->ap_nfc_dh_pubkey)) {
wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
if (wps->wps->ap_nfc_dh_privkey == NULL) {
wpa_printf(MSG_DEBUG,
@@ -118,6 +125,8 @@
int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
{
+ if (wpabuf_tailroom(msg) < 4 + WPS_UUID_LEN)
+ return -1;
wpa_printf(MSG_DEBUG, "WPS: * UUID-E");
wpabuf_put_be16(msg, ATTR_UUID_E);
wpabuf_put_be16(msg, WPS_UUID_LEN);
@@ -183,6 +192,8 @@
* backwards compatibility reasons. The real version negotiation is
* done with Version2.
*/
+ if (wpabuf_tailroom(msg) < 5)
+ return -1;
wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)");
wpabuf_put_be16(msg, ATTR_VERSION);
wpabuf_put_be16(msg, 1);
@@ -194,9 +205,17 @@
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
const u8 *auth_macs, size_t auth_macs_count)
{
-#ifdef CONFIG_WPS2
u8 *len;
+#ifdef CONFIG_WPS_TESTING
+ if (WPS_VERSION == 0x10)
+ return 0;
+#endif /* CONFIG_WPS_TESTING */
+
+ if (wpabuf_tailroom(msg) <
+ 7 + 3 + (req_to_enroll ? 3 : 0) +
+ (auth_macs ? 2 + auth_macs_count * ETH_ALEN : 0))
+ return -1;
wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
len = wpabuf_put(msg, 2); /* to be filled */
wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
@@ -226,10 +245,11 @@
}
WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
-#endif /* CONFIG_WPS2 */
#ifdef CONFIG_WPS_TESTING
if (WPS_VERSION > 0x20) {
+ if (wpabuf_tailroom(msg) < 5)
+ return -1;
wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra "
"attribute");
wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
@@ -274,9 +294,10 @@
int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
u16 auth_types = WPS_AUTH_TYPES;
-#ifdef CONFIG_WPS2
+ /* WPA/WPA2-Enterprise enrollment not supported through WPS */
+ auth_types &= ~WPS_AUTH_WPA;
+ auth_types &= ~WPS_AUTH_WPA2;
auth_types &= ~WPS_AUTH_SHARED;
-#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags");
wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
wpabuf_put_be16(msg, 2);
@@ -288,9 +309,7 @@
int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
u16 encr_types = WPS_ENCR_TYPES;
-#ifdef CONFIG_WPS2
encr_types &= ~WPS_ENCR_WEP;
-#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags");
wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
wpabuf_put_be16(msg, 2);
@@ -372,15 +391,31 @@
const u8 *addr[1];
u8 pubkey_hash[WPS_HASH_LEN];
+ wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password (dev_pw_id=%u)",
+ dev_pw_id);
addr[0] = wpabuf_head(pubkey);
hash_len = wpabuf_len(pubkey);
sha256_vector(1, addr, &hash_len, pubkey_hash);
+#ifdef CONFIG_WPS_TESTING
+ if (wps_corrupt_pkhash) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash",
+ pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash");
+ pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++;
+ }
+#endif /* CONFIG_WPS_TESTING */
wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
+ wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+ pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
wpabuf_put_be16(msg, dev_pw_id);
- wpabuf_put_data(msg, dev_pw, dev_pw_len);
+ if (dev_pw) {
+ wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
+ dev_pw, dev_pw_len);
+ wpabuf_put_data(msg, dev_pw, dev_pw_len);
+ }
return 0;
}
@@ -428,3 +463,23 @@
wpabuf_put_data(msg, addr, ETH_ALEN);
return 0;
}
+
+
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", rf_bands);
+ wpabuf_put_be16(msg, ATTR_RF_BANDS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, rf_bands);
+ return 0;
+}
+
+
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * AP Channel (%u)", ap_channel);
+ wpabuf_put_be16(msg, ATTR_AP_CHANNEL);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, ap_channel);
+ return 0;
+}
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 3999b1b..40bc1ad 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -59,6 +59,14 @@
}
attr->settings_delay_time = pos;
break;
+ case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
+ len);
+ return -1;
+ }
+ attr->registrar_configuration_methods = pos;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
"Extension subelement %u", id);
@@ -75,7 +83,7 @@
const u8 *end = pos + len;
u8 id, elen;
- while (pos + 2 < end) {
+ while (pos + 2 <= end) {
id = *pos++;
elen = *pos++;
if (pos + elen > end)
@@ -263,10 +271,13 @@
attr->dev_password_id = pos;
break;
case ATTR_OOB_DEVICE_PASSWORD:
- if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
- WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+ if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
- WPS_OOB_DEVICE_PASSWORD_LEN) {
+ WPS_OOB_DEVICE_PASSWORD_LEN ||
+ (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
+ WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
+ DEV_PW_NFC_CONNECTION_HANDOVER)) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
"Password length %u", len);
return -1;
@@ -410,22 +421,6 @@
}
attr->mac_addr = pos;
break;
- case ATTR_KEY_PROVIDED_AUTO:
- if (len != 1) {
- wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
- "Automatically length %u", len);
- return -1;
- }
- attr->key_prov_auto = pos;
- break;
- case ATTR_802_1X_ENABLED:
- if (len != 1) {
- wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
- "length %u", len);
- return -1;
- }
- attr->dot1x_enabled = pos;
- break;
case ATTR_SELECTED_REGISTRAR:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
@@ -497,14 +492,6 @@
attr->network_key = pos;
attr->network_key_len = len;
break;
- case ATTR_EAP_TYPE:
- attr->eap_type = pos;
- attr->eap_type_len = len;
- break;
- case ATTR_EAP_IDENTITY:
- attr->eap_identity = pos;
- attr->eap_identity_len = len;
- break;
case ATTR_AP_SETUP_LOCKED:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
index 88e51a4..82c4739 100644
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -47,8 +47,6 @@
const u8 *network_idx; /* 1 octet */
const u8 *network_key_idx; /* 1 octet */
const u8 *mac_addr; /* ETH_ALEN (6) octets */
- const u8 *key_prov_auto; /* 1 octet (Bool) */
- const u8 *dot1x_enabled; /* 1 octet (Bool) */
const u8 *selected_registrar; /* 1 octet (Bool) */
const u8 *request_type; /* 1 octet */
const u8 *response_type; /* 1 octet */
@@ -57,6 +55,7 @@
const u8 *network_key_shareable; /* 1 octet (Bool) */
const u8 *request_to_enroll; /* 1 octet (Bool) */
const u8 *ap_channel; /* 2 octets */
+ const u8 *registrar_configuration_methods; /* 2 octets */
/* variable length fields */
const u8 *manufacturer;
@@ -77,10 +76,6 @@
size_t ssid_len;
const u8 *network_key; /* <= 64 octets */
size_t network_key_len;
- const u8 *eap_type; /* <= 8 octets */
- size_t eap_type_len;
- const u8 *eap_identity; /* <= 64 octets */
- size_t eap_identity_len;
const u8 *authorized_macs; /* <= 30 octets */
size_t authorized_macs_len;
const u8 *sec_dev_type_list; /* <= 128 octets */
diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c
index b81f106..eadb22f 100644
--- a/src/wps/wps_attr_process.c
+++ b/src/wps/wps_attr_process.c
@@ -41,7 +41,7 @@
len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
- if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+ if (os_memcmp_const(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
return -1;
}
@@ -71,7 +71,7 @@
}
hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
- if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+ if (os_memcmp_const(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
return -1;
}
@@ -207,70 +207,6 @@
}
-static int wps_process_cred_eap_type(struct wps_credential *cred,
- const u8 *eap_type, size_t eap_type_len)
-{
- if (eap_type == NULL)
- return 0; /* optional attribute */
-
- wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
-
- return 0;
-}
-
-
-static int wps_process_cred_eap_identity(struct wps_credential *cred,
- const u8 *identity,
- size_t identity_len)
-{
- if (identity == NULL)
- return 0; /* optional attribute */
-
- wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
- identity, identity_len);
-
- return 0;
-}
-
-
-static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
- const u8 *key_prov_auto)
-{
- if (key_prov_auto == NULL)
- return 0; /* optional attribute */
-
- wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
- *key_prov_auto);
-
- return 0;
-}
-
-
-static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
- const u8 *dot1x_enabled)
-{
- if (dot1x_enabled == NULL)
- return 0; /* optional attribute */
-
- wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
-
- return 0;
-}
-
-
-static int wps_process_cred_ap_channel(struct wps_credential *cred,
- const u8 *ap_channel)
-{
- if (ap_channel == NULL)
- return 0; /* optional attribute */
-
- cred->ap_channel = WPA_GET_BE16(ap_channel);
- wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel);
-
- return 0;
-}
-
-
static int wps_workaround_cred_key(struct wps_credential *cred)
{
if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
@@ -310,14 +246,7 @@
wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
wps_process_cred_network_key(cred, attr->network_key,
attr->network_key_len) ||
- wps_process_cred_mac_addr(cred, attr->mac_addr) ||
- wps_process_cred_eap_type(cred, attr->eap_type,
- attr->eap_type_len) ||
- wps_process_cred_eap_identity(cred, attr->eap_identity,
- attr->eap_identity_len) ||
- wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
- wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) ||
- wps_process_cred_ap_channel(cred, attr->ap_channel))
+ wps_process_cred_mac_addr(cred, attr->mac_addr))
return -1;
return wps_workaround_cred_key(cred);
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 4b431ad..a282348 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -9,6 +9,8 @@
#include "includes.h"
#include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_common.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/dh_group5.h"
@@ -16,6 +18,7 @@
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "wps_i.h"
+#include "wps_dev_attr.h"
void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
@@ -349,7 +352,8 @@
#ifdef CONFIG_WPS_OOB
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+ int channel)
{
struct wps_data data;
struct wpabuf *plain;
@@ -365,8 +369,10 @@
data.wps = wps;
data.auth_type = wps->auth_types;
data.encr_type = wps->encr_types;
- if (wps_build_version(plain) ||
- wps_build_cred(&data, plain) ||
+ if (wps_build_cred(&data, plain) ||
+ (rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
+ (channel && wps_build_ap_channel(plain, channel)) ||
+ wps_build_mac_addr(plain, wps->dev.mac_addr) ||
wps_build_wfa_ext(plain, 0, NULL, 0)) {
os_free(data.new_psk);
wpabuf_free(plain);
@@ -412,8 +418,7 @@
if (data == NULL)
return NULL;
- if (wps_build_version(data) ||
- wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+ if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
wps_build_wfa_ext(data, 0, NULL, 0)) {
wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
@@ -526,9 +531,7 @@
if (str == NULL) {
/* Default to enabling methods based on build configuration */
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS2
methods |= WPS_CONFIG_VIRT_DISPLAY;
-#endif /* CONFIG_WPS2 */
#ifdef CONFIG_WPS_NFC
methods |= WPS_CONFIG_NFC_INTERFACE;
#endif /* CONFIG_WPS_NFC */
@@ -549,7 +552,6 @@
methods |= WPS_CONFIG_PUSHBUTTON;
if (os_strstr(str, "keypad"))
methods |= WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS2
if (os_strstr(str, "virtual_display"))
methods |= WPS_CONFIG_VIRT_DISPLAY;
if (os_strstr(str, "physical_display"))
@@ -558,7 +560,6 @@
methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
if (os_strstr(str, "physical_push_button"))
methods |= WPS_CONFIG_PHY_PUSHBUTTON;
-#endif /* CONFIG_WPS2 */
}
return methods;
@@ -636,12 +637,36 @@
}
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey)
+{
+ struct wpabuf *priv = NULL, *pub = NULL;
+ void *dh_ctx;
+
+ dh_ctx = dh5_init(&priv, &pub);
+ if (dh_ctx == NULL)
+ return -1;
+ pub = wpabuf_zeropad(pub, 192);
+ if (pub == NULL) {
+ wpabuf_free(priv);
+ return -1;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
+ dh5_free(dh_ctx);
+
+ wpabuf_free(*pubkey);
+ *pubkey = pub;
+ wpabuf_free(*privkey);
+ *privkey = priv;
+
+ return 0;
+}
+
+
struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
struct wpabuf **privkey,
struct wpabuf **dev_pw)
{
- struct wpabuf *priv = NULL, *pub = NULL, *pw;
- void *dh_ctx;
+ struct wpabuf *pw;
u16 val;
pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
@@ -655,22 +680,223 @@
return NULL;
}
- dh_ctx = dh5_init(&priv, &pub);
- if (dh_ctx == NULL) {
+ if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
wpabuf_free(pw);
return NULL;
}
- dh5_free(dh_ctx);
*id = 0x10 + val % 0xfff0;
- wpabuf_free(*pubkey);
- *pubkey = pub;
- wpabuf_free(*privkey);
- *privkey = priv;
wpabuf_free(*dev_pw);
*dev_pw = pw;
return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
}
+
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey)
+{
+ struct wpabuf *msg;
+ void *len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover request");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+ len = wpabuf_put(msg, 2);
+
+ if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+ return msg;
+}
+
+
+static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select",
+ wps->ssid, wps->ssid_len);
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, wps->ssid_len);
+ wpabuf_put_data(msg, wps->ssid, wps->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_ap_freq(struct wpabuf *msg, int freq)
+{
+ enum hostapd_hw_mode mode;
+ u8 channel, rf_band;
+ u16 ap_channel;
+
+ if (freq <= 0)
+ return 0;
+
+ mode = ieee80211_freq_to_chan(freq, &channel);
+ if (mode == NUM_HOSTAPD_MODES)
+ return 0; /* Unknown channel */
+
+ if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B)
+ rf_band = WPS_RF_24GHZ;
+ else if (mode == HOSTAPD_MODE_IEEE80211A)
+ rf_band = WPS_RF_50GHZ;
+ else
+ return 0; /* Unknown band */
+ ap_channel = channel;
+
+ if (wps_build_rf_bands_attr(msg, rf_band) ||
+ wps_build_ap_channel(msg, ap_channel))
+ return -1;
+
+ return 0;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey,
+ const u8 *bssid, int freq)
+{
+ struct wpabuf *msg;
+ void *len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover select");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+ len = wpabuf_put(msg, 2);
+
+ if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_ssid(msg, ctx) ||
+ wps_build_ap_freq(msg, freq) ||
+ (bssid && wps_build_mac_addr(msg, bssid)) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey)
+{
+ struct wpabuf *msg;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover request (P2P)");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+
+ if (wps_build_manufacturer(&ctx->dev, msg) ||
+ wps_build_model_name(&ctx->dev, msg) ||
+ wps_build_model_number(&ctx->dev, msg) ||
+ wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_rf_bands(&ctx->dev, msg, 0) ||
+ wps_build_serial_number(&ctx->dev, msg) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+ int nfc_dev_pw_id,
+ struct wpabuf *nfc_dh_pubkey,
+ struct wpabuf *nfc_dev_pw)
+{
+ struct wpabuf *msg;
+ const u8 *dev_pw;
+ size_t dev_pw_len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover select (P2P)");
+
+ if (nfc_dh_pubkey == NULL ||
+ (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+ nfc_dev_pw == NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+
+ if (nfc_dev_pw) {
+ dev_pw = wpabuf_head(nfc_dev_pw);
+ dev_pw_len = wpabuf_len(nfc_dev_pw);
+ } else {
+ dev_pw = NULL;
+ dev_pw_len = 0;
+ }
+
+ if (wps_build_manufacturer(&ctx->dev, msg) ||
+ wps_build_model_name(&ctx->dev, msg) ||
+ wps_build_model_number(&ctx->dev, msg) ||
+ wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey,
+ dev_pw, dev_pw_len) ||
+ wps_build_rf_bands(&ctx->dev, msg, 0) ||
+ wps_build_serial_number(&ctx->dev, msg) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index 3421ac5..f483e2e 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -13,15 +13,12 @@
extern int wps_version_number;
extern int wps_testing_dummy_cred;
+extern int wps_corrupt_pkhash;
#define WPS_VERSION wps_version_number
#else /* CONFIG_WPS_TESTING */
-#ifdef CONFIG_WPS2
#define WPS_VERSION 0x20
-#else /* CONFIG_WPS2 */
-#define WPS_VERSION 0x10
-#endif /* CONFIG_WPS2 */
#endif /* CONFIG_WPS_TESTING */
@@ -145,7 +142,8 @@
WFA_ELEM_AUTHORIZEDMACS = 0x01,
WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
- WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
+ WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
+ WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
};
/* Device Password ID */
@@ -155,7 +153,8 @@
DEV_PW_MACHINE_SPECIFIED = 0x0002,
DEV_PW_REKEY = 0x0003,
DEV_PW_PUSHBUTTON = 0x0004,
- DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+ DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
+ DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007
};
/* Message Type */
@@ -180,7 +179,7 @@
/* Authentication Type Flags */
#define WPS_AUTH_OPEN 0x0001
#define WPS_AUTH_WPAPSK 0x0002
-#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_SHARED 0x0004 /* deprecated */
#define WPS_AUTH_WPA 0x0008
#define WPS_AUTH_WPA2 0x0010
#define WPS_AUTH_WPA2PSK 0x0020
@@ -189,7 +188,7 @@
/* Encryption Type Flags */
#define WPS_ENCR_NONE 0x0001
-#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_WEP 0x0002 /* deprecated */
#define WPS_ENCR_TKIP 0x0004
#define WPS_ENCR_AES 0x0008
#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
@@ -215,7 +214,9 @@
WPS_CFG_SETUP_LOCKED = 15,
WPS_CFG_MSG_TIMEOUT = 16,
WPS_CFG_REG_SESS_TIMEOUT = 17,
- WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+ WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18,
+ WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19,
+ WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
};
/* Vendor specific Error Indication for WPS event messages */
@@ -241,12 +242,10 @@
#define WPS_CONFIG_NFC_INTERFACE 0x0040
#define WPS_CONFIG_PUSHBUTTON 0x0080
#define WPS_CONFIG_KEYPAD 0x0100
-#ifdef CONFIG_WPS2
#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
#define WPS_CONFIG_VIRT_DISPLAY 0x2008
#define WPS_CONFIG_PHY_DISPLAY 0x4008
-#endif /* CONFIG_WPS2 */
/* Connection Type Flags */
#define WPS_CONN_ESS 0x01
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index 1b12b5a..0d01211 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -85,8 +85,7 @@
}
-static int wps_build_serial_number(struct wps_device_data *dev,
- struct wpabuf *msg)
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
@@ -220,11 +219,7 @@
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
u8 rf_band)
{
- wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands);
- wpabuf_put_be16(msg, ATTR_RF_BANDS);
- wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, rf_band ? rf_band : dev->rf_bands);
- return 0;
+ return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
}
@@ -409,25 +404,6 @@
}
-void wps_device_data_dup(struct wps_device_data *dst,
- const struct wps_device_data *src)
-{
- if (src->device_name)
- dst->device_name = os_strdup(src->device_name);
- if (src->manufacturer)
- dst->manufacturer = os_strdup(src->manufacturer);
- if (src->model_name)
- dst->model_name = os_strdup(src->model_name);
- if (src->model_number)
- dst->model_number = os_strdup(src->model_number);
- if (src->serial_number)
- dst->serial_number = os_strdup(src->serial_number);
- os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
- dst->os_version = src->os_version;
- dst->rf_bands = src->rf_bands;
-}
-
-
void wps_device_data_free(struct wps_device_data *dev)
{
os_free(dev->device_name);
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index 0158cdc..c9034ad 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -14,6 +14,7 @@
int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
@@ -29,8 +30,6 @@
struct wps_parse_attr *attr);
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
-void wps_device_data_dup(struct wps_device_data *dst,
- const struct wps_device_data *src);
void wps_device_data_free(struct wps_device_data *dev);
int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index d02ba30..f7d41b4 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -130,10 +130,8 @@
* workaround.
*/
config_methods &= ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
}
if (wps_build_version(msg) ||
@@ -243,54 +241,53 @@
static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
{
- u16 auth_type = wps->wps->auth_types;
-
- /* Select the best authentication type */
- if (auth_type & WPS_AUTH_WPA2PSK)
- auth_type = WPS_AUTH_WPA2PSK;
- else if (auth_type & WPS_AUTH_WPAPSK)
- auth_type = WPS_AUTH_WPAPSK;
- else if (auth_type & WPS_AUTH_OPEN)
- auth_type = WPS_AUTH_OPEN;
- else if (auth_type & WPS_AUTH_SHARED)
- auth_type = WPS_AUTH_SHARED;
-
- wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type);
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)",
+ wps->wps->ap_auth_type);
wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, auth_type);
+ wpabuf_put_be16(msg, wps->wps->ap_auth_type);
return 0;
}
static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
{
- u16 encr_type = wps->wps->encr_types;
-
- /* Select the best encryption type */
- if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
- if (encr_type & WPS_ENCR_AES)
- encr_type = WPS_ENCR_AES;
- else if (encr_type & WPS_ENCR_TKIP)
- encr_type = WPS_ENCR_TKIP;
- } else {
- if (encr_type & WPS_ENCR_WEP)
- encr_type = WPS_ENCR_WEP;
- else if (encr_type & WPS_ENCR_NONE)
- encr_type = WPS_ENCR_NONE;
- }
-
- wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type);
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)",
+ wps->wps->ap_encr_type);
wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, encr_type);
+ wpabuf_put_be16(msg, wps->wps->ap_encr_type);
return 0;
}
static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
{
- wpa_printf(MSG_DEBUG, "WPS: * Network Key");
+ if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) &&
+ wps->wps->network_key_len == 0) {
+ char hex[65];
+ u8 psk[32];
+ /* Generate a random per-device PSK */
+ if (random_get_bytes(psk, sizeof(psk)) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+ psk, sizeof(psk));
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%u)",
+ (unsigned int) wps->new_psk_len * 2);
+ wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk));
+ wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+ wpabuf_put_be16(msg, sizeof(psk) * 2);
+ wpabuf_put_data(msg, hex, sizeof(psk) * 2);
+ if (wps->wps->registrar) {
+ wps_cb_new_psk(wps->wps->registrar,
+ wps->peer_dev.mac_addr,
+ wps->p2p_dev_addr, psk, sizeof(psk));
+ }
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%u)",
+ (unsigned int) wps->wps->network_key_len);
wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
wpabuf_put_be16(msg, wps->wps->network_key_len);
wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
@@ -310,6 +307,9 @@
static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
{
+ const u8 *start, *end;
+ int ret;
+
if (wps->wps->ap_settings) {
wpa_printf(MSG_DEBUG, "WPS: * AP Settings (pre-configured)");
wpabuf_put_data(plain, wps->wps->ap_settings,
@@ -317,11 +317,19 @@
return 0;
}
- return wps_build_cred_ssid(wps, plain) ||
+ wpa_printf(MSG_DEBUG, "WPS: * AP Settings based on current configuration");
+ start = wpabuf_put(plain, 0);
+ ret = wps_build_cred_ssid(wps, plain) ||
wps_build_cred_mac_addr(wps, plain) ||
wps_build_cred_auth_type(wps, plain) ||
wps_build_cred_encr_type(wps, plain) ||
wps_build_cred_network_key(wps, plain);
+ end = wpabuf_put(plain, 0);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings",
+ start, end - start);
+
+ return ret;
}
@@ -514,6 +522,24 @@
return -1;
}
+ if (wps->peer_pubkey_hash_set) {
+ u8 hash[WPS_HASH_LEN];
+ sha256_vector(1, &pk, &pk_len, hash);
+ if (os_memcmp_const(hash, wps->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
+ pk, pk_len);
+ wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
+ "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
+ wps->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+ return -1;
+ }
+ }
+
wpabuf_free(wps->dh_pubkey_r);
wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_r == NULL)
@@ -579,7 +605,7 @@
len[3] = wpabuf_len(wps->dh_pubkey_r);
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
- if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+ if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
"not match with the pre-committed value");
wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
@@ -619,7 +645,7 @@
len[3] = wpabuf_len(wps->dh_pubkey_r);
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
- if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+ if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
"not match with the pre-committed value");
wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
@@ -670,7 +696,6 @@
#endif /* CONFIG_WPS_STRICT */
}
-#ifdef CONFIG_WPS2
if (!(wps->cred.encr_type &
(WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
if (wps->cred.encr_type & WPS_ENCR_WEP) {
@@ -684,7 +709,6 @@
"invalid encr_type 0x%x", wps->cred.encr_type);
return -1;
}
-#endif /* CONFIG_WPS2 */
if (wps->wps->cred_cb) {
wps->cred.cred_attr = cred - 4;
@@ -771,7 +795,6 @@
#endif /* CONFIG_WPS_STRICT */
}
-#ifdef CONFIG_WPS2
if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
{
if (cred.encr_type & WPS_ENCR_WEP) {
@@ -785,7 +808,6 @@
"invalid encr_type 0x%x", cred.encr_type);
return -1;
}
-#endif /* CONFIG_WPS2 */
#ifdef CONFIG_WPS_STRICT
if (wps2) {
@@ -802,7 +824,6 @@
}
#endif /* CONFIG_WPS_STRICT */
-#ifdef CONFIG_WPS2
if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
{
wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
@@ -816,7 +837,6 @@
"WPAPSK+WPA2PSK");
cred.auth_type |= WPS_AUTH_WPA2PSK;
}
-#endif /* CONFIG_WPS2 */
if (wps->wps->cred_cb) {
cred.cred_attr = wpabuf_head(attrs);
@@ -864,9 +884,15 @@
wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
"ID from %u to %u", wps->dev_pw_id, id);
+ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Workaround - ignore PBC-to-PIN change");
+ return 0;
+ }
+
if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
- os_free(wps->dev_password);
+ bin_clear_free(wps->dev_password, wps->dev_password_len);
wps->dev_pw_id = wps->alt_dev_pw_id;
wps->dev_password = wps->alt_dev_password;
wps->dev_password_len = wps->alt_dev_password_len;
@@ -923,6 +949,38 @@
return WPS_CONTINUE;
}
+#ifdef CONFIG_WPS_NFC
+ if (wps->peer_pubkey_hash_set) {
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
+ "Encrypted Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
+ "Settings attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted,
+ eattr.key_wrap_auth) ||
+ wps_process_creds(wps, eattr.cred, eattr.cred_len,
+ eattr.num_cred, attr->version2 != NULL)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = WPS_MSG_DONE;
+ return WPS_CONTINUE;
+ }
+#endif /* CONFIG_WPS_NFC */
+
wps->state = SEND_M3;
return WPS_CONTINUE;
}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 5694997..078ff72 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -185,10 +185,8 @@
dl_list_del(&ap->list);
wps_er_ap_free(ap);
- if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) {
- eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
+ if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing))
wps_er_deinit_finish(er, NULL);
- }
}
@@ -581,12 +579,15 @@
wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
ap->udn = xml_get_first_item(data, "UDN");
- wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
- pos = os_strstr(ap->udn, "uuid:");
- if (pos) {
- pos += 5;
- if (uuid_str2bin(pos, ap->uuid) < 0)
- wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN");
+ if (ap->udn) {
+ wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
+ pos = os_strstr(ap->udn, "uuid:");
+ if (pos) {
+ pos += 5;
+ if (uuid_str2bin(pos, ap->uuid) < 0)
+ wpa_printf(MSG_DEBUG,
+ "WPS ER: Invalid UUID in UDN");
+ }
}
ap->upc = xml_get_first_item(data, "UPC");
@@ -1347,9 +1348,19 @@
struct wps_er *er = eloop_data;
void (*deinit_done_cb)(void *ctx);
void *deinit_done_ctx;
+ struct wps_er_ap *ap, *tmp;
wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit");
+ dl_list_for_each_safe(ap, tmp, &er->ap_unsubscribing, struct wps_er_ap,
+ list) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP entry for %s (%s) still in ap_unsubscribing list - free it",
+ inet_ntoa(ap->addr), ap->location);
+ dl_list_del(&ap->list);
+ wps_er_ap_free(ap);
+ }
+
+ eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
deinit_done_cb = er->deinit_done_cb;
deinit_done_ctx = er->deinit_done_ctx;
os_free(er->ip_addr_text);
@@ -1482,11 +1493,9 @@
static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
{
-#ifdef CONFIG_WPS2
wpabuf_put_be16(msg, ATTR_UUID_R);
wpabuf_put_be16(msg, WPS_UUID_LEN);
wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
-#endif /* CONFIG_WPS2 */
return 0;
}
@@ -1498,9 +1507,7 @@
struct wps_er_ap *ap;
struct wps_registrar *reg = er->wps->registrar;
const u8 *auth_macs;
-#ifdef CONFIG_WPS2
u8 bcast[ETH_ALEN];
-#endif /* CONFIG_WPS2 */
size_t count;
union wps_event_data data;
@@ -1514,13 +1521,11 @@
return;
auth_macs = wps_authorized_macs(reg, &count);
-#ifdef CONFIG_WPS2
if (count == 0) {
os_memset(bcast, 0xff, ETH_ALEN);
auth_macs = bcast;
count = 1;
}
-#endif /* CONFIG_WPS2 */
if (wps_build_version(msg) ||
wps_er_build_selected_registrar(msg, sel_reg) ||
@@ -2032,8 +2037,7 @@
os_memset(&data, 0, sizeof(data));
data.wps = wps;
data.use_cred = cred;
- if (wps_build_version(ret) ||
- wps_build_cred(&data, ret) ||
+ if (wps_build_cred(&data, ret) ||
wps_build_wfa_ext(ret, 0, NULL, 0)) {
wpabuf_free(ret);
return NULL;
@@ -2063,4 +2067,29 @@
return wps_er_config_token_from_cred(er->wps, ap->ap_settings);
}
+
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+ struct wps_context *wps, const u8 *uuid,
+ const u8 *addr, struct wpabuf *pubkey)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return NULL;
+
+ ap = wps_er_ap_get(er, NULL, uuid, addr);
+ if (ap == NULL)
+ return NULL;
+ if (ap->ap_settings == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+ "selected AP");
+ return NULL;
+ }
+
+ os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len);
+ wps->ssid_len = ap->ap_settings->ssid_len;
+
+ return wps_build_nfc_handover_sel(wps, pubkey, addr, 0);
+}
+
#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index dac1250..f7154f8 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -75,6 +75,9 @@
size_t alt_dev_password_len;
u16 alt_dev_pw_id;
+ u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ int peer_pubkey_hash_set;
+
/**
* request_type - Request Type attribute from (Re)AssocReq
*/
@@ -173,6 +176,8 @@
size_t dev_pw_len);
struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr);
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands);
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel);
/* wps_attr_process.c */
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
@@ -207,5 +212,7 @@
const u8 *addr, const u8 *uuid_e);
void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
struct wps_nfc_pw_token *token);
+int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len);
#endif /* WPS_I_H */
diff --git a/src/wps/wps_module_tests.c b/src/wps/wps_module_tests.c
new file mode 100644
index 0000000..6800e86
--- /dev/null
+++ b/src/wps/wps_module_tests.c
@@ -0,0 +1,337 @@
+/*
+ * WPS module tests
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "wps_attr_parse.h"
+
+struct wps_attr_parse_test {
+ const char *data;
+ int result;
+ int extra;
+};
+
+struct wps_attr_parse_test wps_attr_parse_test_cases[] = {
+ /* Empty message */
+ { "", 0, 0 },
+ /* Truncated attribute header */
+ { "10", -1, 0 },
+ { "1010", -1, 0 },
+ { "101000", -1, 0 },
+ /* Attribute overflow */
+ { "10100001", -1, 0 },
+#ifdef CONFIG_WPS_STRICT
+ { "10270000001057000101", -1, 0 },
+ { "1027000010570001010000000000", -1, 0 },
+#else /* CONFIG_WPS_STRICT */
+ /* Network Key workaround */
+ { "10270000001057000101", 0, 1 },
+ { "10230000001057000101", -1, 0 },
+ { "10270000101057000101", -1, 0 },
+ /* Mac OS X 10.6 padding workaround */
+ { "1027000010570001010000000000", 0, 1 },
+ { "1027000010570001010000000000000001000000", -1, 0 },
+#endif /* CONFIG_WPS_STRICT */
+ /* Version */
+ { "104a000110", 0, 0 },
+ { "104a0000", -1, 0 },
+ /* Message Type */
+ { "1022000101", 0, 0 },
+ { "10220000", -1, 0 },
+ /* Enrollee Nonce */
+ { "101a001000112233445566778899aabbccddeeff", 0, 0 },
+ { "101a00111122334455667788990011223344556677", -1, 0 },
+ /* Registrar Nonce */
+ { "1039001000112233445566778899aabbccddeeff", 0, 0 },
+ { "103900111122334455667788990011223344556677", -1, 0 },
+ /* UUID-E */
+ { "1047001000112233445566778899aabbccddeeff", 0, 0 },
+ { "10470000", -1, 0 },
+ { "104700111122334455667788990011223344556677", -1, 0 },
+ /* UUID-R */
+ { "1048001000112233445566778899aabbccddeeff", 0, 0 },
+ { "10480000", -1, 0 },
+ { "104800111122334455667788990011223344556677", -1, 0 },
+ /* Auth Type Flags */
+ { "100400021122", 0, 0 },
+ { "10040001ff", -1, 0 },
+ /* Encr Type Flags */
+ { "101000021122", 0, 0 },
+ { "10100001ff", -1, 0 },
+ /* Connection Type Flags */
+ { "100d0001ff", 0, 0 },
+ { "100d0002ffff", -1, 0 },
+ /* Config Methods */
+ { "10080002ffff", 0, 0 },
+ { "10080001ff", -1, 0 },
+ /* Selected Registrar Config Methods */
+ { "10530002ffff", 0, 0 },
+ { "10530001ff", -1, 0 },
+ /* Primary Device Type */
+ { "105400081122334455667788", 0, 0 },
+ { "105400111122334455667788990011223344556677", -1, 0 },
+ /* RF Bands */
+ { "103c0001ff", 0, 0 },
+ { "103c0002ffff", -1, 0 },
+ /* Association State */
+ { "10020002ffff", 0, 0 },
+ { "10020001ff", -1, 0 },
+ /* Config Error */
+ { "100900020001", 0, 0 },
+ { "10090001ff", -1, 0 },
+ /* Device Password ID */
+ { "101200020004", 0, 0 },
+ { "10120001ff", -1, 0 },
+ /* OOB Device Password */
+ { "102c001611223344556677889900112233445566778899000007", 0, 0 },
+ { "102c0036112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344", 0, 0 },
+ { "102c0001ff", -1, 0 },
+ { "102c003711223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455", -1, 0 },
+ { "102c002511223344556677889900112233445566778899001122334455667788990011223344556677", -1, 0 },
+ /* OS Version */
+ { "102d000411223344", 0, 0 },
+ { "102d00111122334455667788990011223344556677", -1, 0 },
+ /* WPS State */
+ { "1044000101", 0, 0 },
+ { "10440002ffff", -1, 0 },
+ /* Authenticator */
+ { "100500081122334455667788", 0, 0 },
+ { "10050000", -1, 0 },
+ { "100500111122334455667788990011223344556677", -1, 0 },
+ /* R-Hash1 */
+ { "103d00201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+ { "103d0000", -1, 0 },
+ { "103d0021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+ /* R-Hash2 */
+ { "103e00201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+ { "103e0000", -1, 0 },
+ { "103e0021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+ /* E-Hash1 */
+ { "101400201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+ { "10140000", -1, 0 },
+ { "10140021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+ /* E-Hash2 */
+ { "101500201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+ { "10150000", -1, 0 },
+ { "10150021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+ /* R-SNonce1 */
+ { "103f001011223344556677889900112233445566", 0, 0 },
+ { "103f0000", -1, 0 },
+ { "103f00111122334455667788990011223344556677", -1, 0 },
+ /* R-SNonce2 */
+ { "1040001011223344556677889900112233445566", 0, 0 },
+ { "10400000", -1, 0 },
+ { "104000111122334455667788990011223344556677", -1, 0 },
+ /* E-SNonce1 */
+ { "1016001011223344556677889900112233445566", 0, 0 },
+ { "10160000", -1, 0 },
+ { "101600111122334455667788990011223344556677", -1, 0 },
+ /* E-SNonce2 */
+ { "1017001011223344556677889900112233445566", 0, 0 },
+ { "10170000", -1, 0 },
+ { "101700111122334455667788990011223344556677", -1, 0 },
+ /* Key Wrap Authenticator */
+ { "101e00081122334455667788", 0, 0 },
+ { "101e0000", -1, 0 },
+ { "101e0009112233445566778899", -1, 0 },
+ /* Authentication Type */
+ { "100300020001", 0, 0 },
+ { "10030001ff", -1, 0 },
+ /* Encryption Type */
+ { "100f00020001", 0, 0 },
+ { "100f0001ff", -1, 0 },
+ /* Network Index */
+ { "1026000101", 0, 0 },
+ { "10260002ffff", -1, 0 },
+ /* Network Key Index */
+ { "1028000101", 0, 3 },
+ { "10280002ffff", -1, 0 },
+ /* MAC Address */
+ { "10200006112233445566", 0, 0 },
+ { "10200000", -1, 0 },
+ { "1020000711223344556677", -1, 0 },
+ /* Selected Registrar */
+ { "1041000101", 0, 0 },
+ { "10410002ffff", -1, 0 },
+ /* Request Type */
+ { "103a000101", 0, 0 },
+ { "103a0002ffff", -1, 0 },
+ /* Response Type */
+ { "103b000101", 0, 0 },
+ { "103b0002ffff", -1, 0 },
+ /* Manufacturer */
+ { "10210000", 0, 0 },
+ /* Model Name */
+ { "10230000", 0, 0 },
+ /* Model Number */
+ { "10240000", 0, 0 },
+ /* Serial Number */
+ { "10420000", 0, 0 },
+ /* Device Name */
+ { "10110000", 0, 0 },
+ /* Public Key */
+ { "10320000", 0, 0 },
+ /* Enc Settings */
+ { "10180000", 0, 0 },
+ /* SSID */
+ { "10450000", 0, 0 },
+ /* AP Setup Locked */
+ { "1057000101", 0, 0 },
+ { "10570002ffff", -1, 0 },
+ /* Requested Device Type */
+ { "106a00081122334455667788", 0, 0 },
+ { "106a0000", -1, 0 },
+ { "106a0009112233445566778899", -1, 0 },
+ /* More than maximum Requested Device Type attributes */
+ { "106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788", 0, 4 },
+ /* Secondary Device Type List */
+ { "105500081122334455667788", 0, 0 },
+ { "1055000711223344556677", -1, 0 },
+ { "1055008811223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566", -1, 0 },
+ /* AP Channel */
+ { "100100020001", 0, 0 },
+ { "1001000101", -1, 0 },
+ /* Skip invalid Vendor Extension */
+ { "10490000", 0, 0 },
+ { "1049000100", 0, 0 },
+ { "104900020000", 0, 0 },
+ /* Too long unknown vendor extension */
+ { "10490401"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "112233445566778899001122334455667788990011223344556677889900"
+ "1122334455", -1, 0 },
+ /* Maximum unknown vendor extensions */
+ { "10490003111111104900032222221049000333333310490003444444104900035555551049000366666610490003777777104900038888881049000399999910490003AAAAAA", 0, 5 },
+ /* More than maximum unknown vendor extensions */
+ { "10490003111111104900032222221049000333333310490003444444104900035555551049000366666610490003777777104900038888881049000399999910490003AAAAAA10490003BBBBBB", -1, 0 },
+ /* WFA vendor extensions */
+ { "1049000300372a", 0, 0 },
+ { "1049000400372a00", 0, 0 },
+ { "1049000500372a0001", 0, 0 },
+ { "1049001600372a0001ff0100020101030101040101ff00fe0101", 0, 6 },
+ /* Invalid Version2 length */
+ { "1049000500372a0000", -1, 0 },
+ /* Invalid Network Key Shareable length */
+ { "1049000500372a0200", -1, 0 },
+ /* Invalid Requedt To Enroll length */
+ { "1049000500372a0300", -1, 0 },
+ /* Invalid Settings Delay Time length */
+ { "1049000500372a0400", -1, 0 },
+ /* More than maximum Credential attributes */
+ { "100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000", 0, 2 },
+};
+
+
+static int wps_attr_parse_tests(void)
+{
+ struct wps_parse_attr attr;
+ unsigned int i;
+ int ret = 0;
+
+ wpa_printf(MSG_INFO, "WPS attribute parsing tests");
+
+ for (i = 0; i < ARRAY_SIZE(wps_attr_parse_test_cases); i++) {
+ struct wpabuf *buf;
+ size_t len;
+ struct wps_attr_parse_test *test =
+ &wps_attr_parse_test_cases[i];
+
+ len = os_strlen(test->data) / 2;
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return -1;
+ if (hexstr2bin(test->data, wpabuf_put(buf, len), len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+ if (wps_parse_msg(buf, &attr) != test->result) {
+ wpa_printf(MSG_ERROR, "WPS attribute parsing test %u failed: %s",
+ i, test->data);
+ ret = -1;
+ }
+ switch (test->extra) {
+ case 1:
+ if (!attr.network_key || !attr.ap_setup_locked)
+ ret = -1;
+ break;
+ case 2:
+ if (attr.num_cred != MAX_CRED_COUNT)
+ ret = -1;
+ break;
+ case 3:
+ if (!attr.network_key_idx)
+ ret = -1;
+ break;
+ case 4:
+ if (attr.num_req_dev_type != MAX_REQ_DEV_TYPE_COUNT)
+ ret = -1;
+ break;
+ case 5:
+ if (attr.num_vendor_ext != MAX_WPS_PARSE_VENDOR_EXT)
+ ret = -1;
+ break;
+ case 6:
+ if (!attr.version2 ||
+ !attr.authorized_macs ||
+ !attr.network_key_shareable ||
+ !attr.request_to_enroll ||
+ !attr.settings_delay_time)
+ ret = -1;
+ break;
+ }
+ wpabuf_free(buf);
+ }
+
+ return ret;
+}
+
+
+int wps_module_tests(void)
+{
+ int ret = 0;
+
+ wpa_printf(MSG_INFO, "WPS module tests");
+
+ if (wps_attr_parse_tests() < 0)
+ ret = -1;
+
+ return ret;
+}
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index b7fcd9c..00c8299 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -31,16 +31,18 @@
struct wps_nfc_pw_token {
struct dl_list list;
u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ unsigned int peer_pk_hash_known:1;
u16 pw_id;
u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
size_t dev_pw_len;
+ int pk_hash_provided_oob; /* whether own PK hash was provided OOB */
};
static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
{
dl_list_del(&token->list);
- os_free(token);
+ bin_clear_free(token, sizeof(*token));
}
@@ -82,14 +84,14 @@
#define PIN_LOCKED BIT(0)
#define PIN_EXPIRES BIT(1)
int flags;
- struct os_time expiration;
+ struct os_reltime expiration;
u8 enrollee_addr[ETH_ALEN];
};
static void wps_free_pin(struct wps_uuid_pin *pin)
{
- os_free(pin->pin);
+ bin_clear_free(pin->pin, pin->pin_len);
os_free(pin);
}
@@ -113,7 +115,7 @@
struct wps_pbc_session *next;
u8 addr[ETH_ALEN];
u8 uuid_e[WPS_UUID_LEN];
- struct os_time timestamp;
+ struct os_reltime timestamp;
};
@@ -183,7 +185,9 @@
u8 p2p_dev_addr[ETH_ALEN];
u8 pbc_ignore_uuid[WPS_UUID_LEN];
- struct os_time pbc_ignore_start;
+#ifdef WPS_WORKAROUNDS
+ struct os_reltime pbc_ignore_start;
+#endif /* WPS_WORKAROUNDS */
};
@@ -311,9 +315,9 @@
const u8 *addr, const u8 *uuid_e)
{
struct wps_pbc_session *pbc, *prev = NULL;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
pbc = reg->pbc_sessions;
while (pbc) {
@@ -347,7 +351,8 @@
pbc = pbc->next;
while (pbc) {
- if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+ if (os_reltime_expired(&now, &pbc->timestamp,
+ WPS_PBC_WALK_TIME)) {
prev->next = NULL;
wps_free_pbc_sessions(pbc);
break;
@@ -367,13 +372,8 @@
pbc = reg->pbc_sessions;
while (pbc) {
if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
-#ifdef ANDROID_P2P
- (p2p_dev_addr && !is_zero_ether_addr(pbc->addr) &&
- os_memcmp(pbc->addr, p2p_dev_addr, ETH_ALEN) ==
-#else
(p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
-#endif
0)) {
if (prev)
prev->next = pbc->next;
@@ -400,9 +400,9 @@
int count = 0;
struct wps_pbc_session *pbc;
struct wps_pbc_session *first = NULL;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
@@ -418,9 +418,9 @@
MAC2STR(pbc->addr));
wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
pbc->uuid_e, WPS_UUID_LEN);
- if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
- wpa_printf(MSG_DEBUG, "WPS: PBC walk time has "
- "expired");
+ if (os_reltime_expired(&now, &pbc->timestamp,
+ WPS_PBC_WALK_TIME)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired");
break;
}
if (first &&
@@ -538,7 +538,6 @@
static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
{
*methods |= WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
WPS_CONFIG_VIRT_PUSHBUTTON)
*methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
@@ -556,7 +555,6 @@
*/
*methods |= WPS_CONFIG_PHY_PUSHBUTTON;
}
-#endif /* CONFIG_WPS2 */
}
@@ -568,10 +566,8 @@
return 0;
methods = reg->wps->config_methods;
methods &= ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
if (reg->pbc)
wps_set_pushbutton(&methods, reg->wps->config_methods);
if (reg->sel_reg_config_methods_override >= 0)
@@ -594,10 +590,8 @@
* external Registrars.
*/
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
@@ -617,13 +611,11 @@
{
*count = 0;
-#ifdef CONFIG_WPS2
while (*count < WPS_MAX_AUTHORIZED_MACS) {
if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
break;
(*count)++;
}
-#endif /* CONFIG_WPS2 */
return (const u8 *) reg->authorized_macs_union;
}
@@ -753,7 +745,7 @@
if (timeout) {
p->flags |= PIN_EXPIRES;
- os_get_time(&p->expiration);
+ os_get_reltime(&p->expiration);
p->expiration.sec += timeout;
}
@@ -802,13 +794,13 @@
static void wps_registrar_expire_pins(struct wps_registrar *reg)
{
struct wps_uuid_pin *pin, *prev;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list)
{
if ((pin->flags & PIN_EXPIRES) &&
- os_time_before(&pin->expiration, &now)) {
+ os_reltime_before(&pin->expiration, &now)) {
wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
pin->uuid, WPS_UUID_LEN);
wps_registrar_remove_pin(reg, pin);
@@ -834,7 +826,7 @@
{
if (dev_pw && pin->pin &&
(dev_pw_len != pin->pin_len ||
- os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0))
+ os_memcmp_const(dev_pw, pin->pin, dev_pw_len) != 0))
continue; /* different PIN */
if (pin->wildcard_uuid) {
wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
@@ -1042,7 +1034,9 @@
wps_registrar_remove_pbc_session(registrar,
uuid_e, NULL);
wps_registrar_pbc_completed(registrar);
- os_get_time(®istrar->pbc_ignore_start);
+#ifdef WPS_WORKAROUNDS
+ os_get_reltime(®istrar->pbc_ignore_start);
+#endif /* WPS_WORKAROUNDS */
os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN);
} else {
wps_registrar_pin_completed(registrar);
@@ -1145,9 +1139,9 @@
#ifdef WPS_WORKAROUNDS
if (reg->pbc_ignore_start.sec &&
os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
- struct os_time now, dur;
- os_get_time(&now);
- os_time_sub(&now, ®->pbc_ignore_start, &dur);
+ struct os_reltime now, dur;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, ®->pbc_ignore_start, &dur);
if (dur.sec >= 0 && dur.sec < 5) {
wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation "
"based on Probe Request from the Enrollee "
@@ -1168,8 +1162,8 @@
}
-static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
- const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len)
+int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len)
{
if (reg->new_psk_cb == NULL)
return 0;
@@ -1215,10 +1209,8 @@
if (reg->selected_registrar) {
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
if (reg->pbc)
wps_set_pushbutton(&methods, reg->wps->config_methods);
}
@@ -1351,7 +1343,7 @@
const u8 *pin;
size_t pin_len = 0;
- os_free(wps->dev_password);
+ bin_clear_free(wps->dev_password, wps->dev_password_len);
wps->dev_password = NULL;
if (wps->pbc) {
@@ -1360,10 +1352,23 @@
pin_len = 8;
#ifdef CONFIG_WPS_NFC
} else if (wps->nfc_pw_token) {
+ if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Using NFC connection "
+ "handover and abbreviated WPS handshake "
+ "without Device Password");
+ return 0;
+ }
wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
"Password Token");
pin = wps->nfc_pw_token->dev_pw;
pin_len = wps->nfc_pw_token->dev_pw_len;
+ } else if (wps->dev_pw_id >= 0x10 &&
+ wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+ wps->wps->ap_nfc_dev_pw) {
+ wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token");
+ pin = wpabuf_head(wps->wps->ap_nfc_dev_pw);
+ pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw);
#endif /* CONFIG_WPS_NFC */
} else {
pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
@@ -1379,7 +1384,8 @@
}
if (pin == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
- "the Enrollee");
+ "the Enrollee (context %p registrar %p)",
+ wps->wps, wps->wps->registrar);
wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
&wps->peer_dev);
return -1;
@@ -1593,8 +1599,6 @@
wps->auth_type = WPS_AUTH_WPAPSK;
else if (wps->auth_type & WPS_AUTH_OPEN)
wps->auth_type = WPS_AUTH_OPEN;
- else if (wps->auth_type & WPS_AUTH_SHARED)
- wps->auth_type = WPS_AUTH_SHARED;
else {
wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
wps->auth_type);
@@ -1614,10 +1618,12 @@
return -1;
}
} else {
- if (wps->encr_type & WPS_ENCR_WEP)
- wps->encr_type = WPS_ENCR_WEP;
- else if (wps->encr_type & WPS_ENCR_NONE)
+ if (wps->encr_type & WPS_ENCR_NONE)
wps->encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
+ else if (wps->encr_type & WPS_ENCR_WEP)
+ wps->encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
else {
wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
"type for non-WPA/WPA2 mode");
@@ -1776,6 +1782,7 @@
static struct wpabuf * wps_build_m2(struct wps_data *wps)
{
struct wpabuf *msg;
+ int config_in_m2 = 0;
if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
return NULL;
@@ -1806,14 +1813,41 @@
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
- wps_build_authenticator(wps, msg)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+#ifdef CONFIG_WPS_NFC
+ if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
+ wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+ /*
+ * Use abbreviated handshake since public key hash allowed
+ * Enrollee to validate our public key similarly to how Enrollee
+ * public key was validated. There is no need to validate Device
+ * Password in this case.
+ */
+ struct wpabuf *plain = wpabuf_alloc(500);
+ if (plain == NULL ||
+ wps_build_cred(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain)) {
+ wpabuf_free(msg);
+ wpabuf_free(plain);
+ return NULL;
+ }
+ wpabuf_free(plain);
+ config_in_m2 = 1;
+ }
+#endif /* CONFIG_WPS_NFC */
+
+ if (wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
}
wps->int_reg = 1;
- wps->state = RECV_M3;
+ wps->state = config_in_m2 ? RECV_DONE : RECV_M3;
return msg;
}
@@ -2177,7 +2211,7 @@
len[3] = wpabuf_len(wps->dh_pubkey_r);
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
- if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+ if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
"not match with the pre-committed value");
wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
@@ -2217,7 +2251,7 @@
len[3] = wpabuf_len(wps->dh_pubkey_r);
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
- if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+ if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
"not match with the pre-committed value");
wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
@@ -2527,6 +2561,9 @@
wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+#ifdef CONFIG_WPS_NFC
+ wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+#endif /* CONFIG_WPS_NFC */
(wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
!wps->wps->registrar->pbc)) {
wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
@@ -2536,14 +2573,17 @@
}
#ifdef CONFIG_WPS_NFC
- if (wps->dev_pw_id >= 0x10) {
+ if (wps->dev_pw_id >= 0x10 ||
+ wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
struct wps_nfc_pw_token *token;
const u8 *addr[1];
u8 hash[WPS_HASH_LEN];
+ wpa_printf(MSG_DEBUG, "WPS: Searching for NFC token match for id=%d (ctx %p registrar %p)",
+ wps->dev_pw_id, wps->wps, wps->wps->registrar);
token = wps_get_nfc_pw_token(
&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
- if (token) {
+ if (token && token->peer_pk_hash_known) {
wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
"Password Token");
dl_list_del(&token->list);
@@ -2551,12 +2591,24 @@
addr[0] = attr->public_key;
sha256_vector(1, addr, &attr->public_key_len, hash);
- if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash,
- WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ if (os_memcmp_const(hash,
+ wps->nfc_pw_token->pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
wpa_printf(MSG_ERROR, "WPS: Public Key hash "
"mismatch");
- return WPS_FAILURE;
+ wps->state = SEND_M2D;
+ wps->config_error =
+ WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+ return WPS_CONTINUE;
}
+ } else if (token) {
+ wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+ "Password Token (no peer PK hash)");
+ wps->nfc_pw_token = token;
+ } else if (wps->dev_pw_id >= 0x10 &&
+ wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+ wps->wps->ap_nfc_dev_pw) {
+ wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token");
}
}
#endif /* CONFIG_WPS_NFC */
@@ -3191,7 +3243,9 @@
wps->uuid_e,
wps->p2p_dev_addr);
wps_registrar_pbc_completed(wps->wps->registrar);
- os_get_time(&wps->wps->registrar->pbc_ignore_start);
+#ifdef WPS_WORKAROUNDS
+ os_get_reltime(&wps->wps->registrar->pbc_ignore_start);
+#endif /* WPS_WORKAROUNDS */
os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e,
WPS_UUID_LEN);
} else {
@@ -3383,10 +3437,8 @@
u16 methods;
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
if (reg->pbc) {
reg->sel_reg_dev_password_id_override =
DEV_PW_PUSHBUTTON;
@@ -3447,8 +3499,7 @@
int wps_registrar_config_ap(struct wps_registrar *reg,
struct wps_credential *cred)
{
-#ifdef CONFIG_WPS2
- printf("encr_type=0x%x\n", cred->encr_type);
+ wpa_printf(MSG_DEBUG, "WPS: encr_type=0x%x", cred->encr_type);
if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
WPS_ENCR_AES))) {
if (cred->encr_type & WPS_ENCR_WEP) {
@@ -3475,7 +3526,6 @@
"WPAPSK+WPA2PSK");
cred->auth_type |= WPS_AUTH_WPA2PSK;
}
-#endif /* CONFIG_WPS2 */
if (reg->wps->cred_cb)
return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
@@ -3488,25 +3538,39 @@
int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
const u8 *pubkey_hash, u16 pw_id,
- const u8 *dev_pw, size_t dev_pw_len)
+ const u8 *dev_pw, size_t dev_pw_len,
+ int pk_hash_provided_oob)
{
struct wps_nfc_pw_token *token;
if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
return -1;
+ if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
+ (pubkey_hash == NULL || !pk_hash_provided_oob)) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
+ "addition - missing public key hash");
+ return -1;
+ }
+
wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id);
token = os_zalloc(sizeof(*token));
if (token == NULL)
return -1;
- os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ token->peer_pk_hash_known = pubkey_hash != NULL;
+ if (pubkey_hash)
+ os_memcpy(token->pubkey_hash, pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
token->pw_id = pw_id;
- wpa_snprintf_hex_uppercase((char *) token->dev_pw,
- sizeof(token->dev_pw),
- dev_pw, dev_pw_len);
- token->dev_pw_len = dev_pw_len * 2;
+ token->pk_hash_provided_oob = pk_hash_provided_oob;
+ if (dev_pw) {
+ wpa_snprintf_hex_uppercase((char *) token->dev_pw,
+ sizeof(token->dev_pw),
+ dev_pw, dev_pw_len);
+ token->dev_pw_len = dev_pw_len * 2;
+ }
dl_list_add(®->nfc_pw_tokens, &token->list);
@@ -3535,8 +3599,7 @@
u16 id;
size_t dev_pw_len;
- if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
- WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+ if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
WPS_OOB_DEVICE_PASSWORD_LEN)
return -1;
@@ -3555,7 +3618,7 @@
wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
- dev_pw_len);
+ dev_pw_len, 0);
}
@@ -3565,6 +3628,14 @@
wps_registrar_remove_authorized_mac(reg,
(u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg, 0);
+
+ /*
+ * Free the NFC password token if it was used only for a single protocol
+ * run. The static handover case uses the same password token multiple
+ * times, so do not free that case here.
+ */
+ if (token->peer_pk_hash_known)
+ os_free(token);
}
#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index bea2b33..ae94a9f 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -227,6 +227,8 @@
t = time(NULL);
date = gmtime(&t);
+ if (date == NULL)
+ return;
wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
&weekday_str[date->tm_wday * 4], date->tm_mday,
&month_str[date->tm_mon * 4], date->tm_year + 1900,
@@ -432,23 +434,6 @@
}
-int send_wpabuf(int fd, struct wpabuf *buf)
-{
- wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message",
- (unsigned long) wpabuf_len(buf));
- errno = 0;
- if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) !=
- (int) wpabuf_len(buf)) {
- wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: "
- "errno=%d (%s)",
- errno, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-
static void wpabuf_put_property(struct wpabuf *buf, const char *name,
const char *value)
{
@@ -480,14 +465,14 @@
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *format_tail = "</e:propertyset>\n";
- struct os_time now;
+ struct os_reltime now;
if (dl_list_empty(&sm->subscriptions)) {
/* optimize */
return;
}
- if (os_get_time(&now) == 0) {
+ if (os_get_reltime(&now) == 0) {
if (now.sec != sm->last_event_sec) {
sm->last_event_sec = now.sec;
sm->num_events_in_sec = 1;
@@ -611,7 +596,10 @@
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put(msg, WPS_NONCE_LEN);
- wps_build_wfa_ext(msg, 0, NULL, 0);
+ if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
return msg;
}
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
index 4f1dd8f..2949f14 100644
--- a/src/wps/wps_upnp_ap.c
+++ b/src/wps/wps_upnp_ap.c
@@ -61,11 +61,9 @@
os_memcpy(s->authorized_macs, attr.authorized_macs,
count * ETH_ALEN);
} else if (!attr.version2) {
-#ifdef CONFIG_WPS2
wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
"AuthorizedMACs for WPS 1.0 ER");
os_memset(s->authorized_macs, 0xff, ETH_ALEN);
-#endif /* CONFIG_WPS2 */
}
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
upnp_er_set_selected_timeout, s, reg);
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index 5c39f7e..f289fe6 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -158,7 +158,6 @@
struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
const u8 uuid[UUID_LEN]);
void subscr_addr_delete(struct subscr_addr *a);
-int send_wpabuf(int fd, struct wpabuf *buf);
int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
u8 mac[ETH_ALEN]);
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 416961c..098571c 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -134,6 +134,8 @@
*islast = 0;
iface = dl_list_first(&sm->interfaces,
struct upnp_wps_device_interface, list);
+ if (!iface)
+ return NULL;
uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
msg = wpabuf_alloc(800); /* more than big enough */
if (msg == NULL)
@@ -587,6 +589,8 @@
&sm->interfaces,
struct upnp_wps_device_interface,
list);
+ if (!iface)
+ continue;
data += os_strlen("uuid:");
uuid_bin2str(iface->wps->uuid, uuid_string,
sizeof(uuid_string));
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
index 11386d8..b1cf571 100644
--- a/src/wps/wps_upnp_web.c
+++ b/src/wps/wps_upnp_web.c
@@ -179,15 +179,12 @@
/* format_wps_device_xml -- produce content of "file" wps_device.xml
* (UPNP_WPS_DEVICE_XML_FILE)
*/
-static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
+static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
+ struct upnp_wps_device_sm *sm,
struct wpabuf *buf)
{
const char *s;
char uuid_string[80];
- struct upnp_wps_device_interface *iface;
-
- iface = dl_list_first(&sm->interfaces,
- struct upnp_wps_device_interface, list);
wpabuf_put_str(buf, wps_device_xml_prefix);
@@ -319,13 +316,15 @@
iface = dl_list_first(&sm->interfaces,
struct upnp_wps_device_interface, list);
+ if (iface == NULL) {
+ http_request_deinit(hreq);
+ return;
+ }
/*
* It is not required that filenames be case insensitive but it is
* allowed and cannot hurt here.
*/
- if (filename == NULL)
- filename = "(null)"; /* just in case */
if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
req = GET_DEVICE_XML_FILE;
@@ -393,7 +392,7 @@
switch (req) {
case GET_DEVICE_XML_FILE:
- format_wps_device_xml(sm, buf);
+ format_wps_device_xml(iface, sm, buf);
break;
case GET_SCPD_XML_FILE:
wpabuf_put_str(buf, wps_scpd_xml);
@@ -421,13 +420,14 @@
iface = dl_list_first(&sm->interfaces,
struct upnp_wps_device_interface, list);
- peer = &iface->peer;
wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
- if (iface->ctx->ap_pin == NULL)
+ if (!iface || iface->ctx->ap_pin == NULL)
return HTTP_INTERNAL_SERVER_ERROR;
+ peer = &iface->peer;
+
/*
* Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
* registration over UPnP with the AP acting as an Enrollee. It should
@@ -475,6 +475,8 @@
iface = dl_list_first(&sm->interfaces,
struct upnp_wps_device_interface, list);
+ if (!iface)
+ return HTTP_INTERNAL_SERVER_ERROR;
/*
* PutMessage is used by external UPnP-based Registrar to perform WPS
@@ -948,7 +950,7 @@
wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
end = os_strchr(h, '\n');
- for (; end != NULL; h = end + 1) {
+ while (end) {
/* Option line by option line */
h = end + 1;
end = os_strchr(h, '\n');
@@ -1155,7 +1157,7 @@
wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
end = os_strchr(h, '\n');
- for (; end != NULL; h = end + 1) {
+ while (end) {
/* Option line by option line */
h = end + 1;
end = os_strchr(h, '\n');
@@ -1173,7 +1175,6 @@
.....
}
#endif
- /* SID is only for renewal */
match = "SID:";
match_len = os_strlen(match);
if (os_strncasecmp(h, match, match_len) == 0) {
@@ -1196,6 +1197,20 @@
got_uuid = 1;
continue;
}
+
+ match = "NT:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto send_msg;
+ }
+
+ match = "CALLBACK:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto send_msg;
+ }
}
if (got_uuid) {
@@ -1209,6 +1224,10 @@
sa->domain_and_port : "-null-");
dl_list_del(&s->list);
subscription_destroy(s);
+ } else {
+ wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
+ ret = HTTP_PRECONDITION_FAILED;
+ goto send_msg;
}
} else {
wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
index e366256..1c6a14b 100644
--- a/src/wps/wps_validate.c
+++ b/src/wps/wps_validate.c
@@ -267,7 +267,7 @@
return 0;
}
val = WPA_GET_BE16(config_error);
- if (val > 18) {
+ if (val > 20) {
wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
"attribute value 0x%04x", val);
return -1;
@@ -290,7 +290,7 @@
return 0;
}
val = WPA_GET_BE16(dev_password_id);
- if (val >= 0x0006 && val <= 0x000f) {
+ if (val >= 0x0008 && val <= 0x000f) {
wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
"attribute value 0x%04x", val);
return -1;
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 8515b5a..43c3eed 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -24,24 +24,20 @@
# Set Android log name
L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\"
+# Disable unused parameter warnings
+L_CFLAGS += -Wno-unused-parameter
+
+# Set Android extended P2P functionality
+L_CFLAGS += -DANDROID_P2P
+ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+L_CFLAGS += -DANDROID_P2P_STUB
+endif
+
# Disable roaming in wpa_supplicant
ifdef CONFIG_NO_ROAMING
L_CFLAGS += -DCONFIG_NO_ROAMING
endif
-ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
-L_CFLAGS += -DANDROID_P2P
-L_CFLAGS += -DP2P_CONCURRENT_SEARCH_DELAY=0
-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/wpa_supplicant\"
@@ -51,9 +47,6 @@
L_CFLAGS += -mabi=aapcs-linux
endif
-# To allow non-ASCII characters in SSID
-L_CFLAGS += -DWPA_UNICODE_SSID
-
INCLUDES = $(LOCAL_PATH)
INCLUDES += $(LOCAL_PATH)/src
INCLUDES += $(LOCAL_PATH)/src/common
@@ -71,10 +64,14 @@
INCLUDES += $(LOCAL_PATH)/src/utils
INCLUDES += $(LOCAL_PATH)/src/wps
INCLUDES += external/openssl/include
-INCLUDES += system/security/keystore
+INCLUDES += system/security/keystore/include
ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+INCLUDES += external/libnl/include
+else
INCLUDES += external/libnl-headers
endif
+endif
ifdef CONFIG_FIPS
CONFIG_NO_RANDOM_POOL=
@@ -139,6 +136,10 @@
L_CFLAGS += -DCONFIG_ELOOP_POLL
endif
+ifdef CONFIG_ELOOP_EPOLL
+L_CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
ifdef CONFIG_EAPOL_TEST
L_CFLAGS += -Werror -DEAPOL_TEST
endif
@@ -258,6 +259,7 @@
OBJS += src/p2p/p2p_dev_disc.c
OBJS += src/p2p/p2p_group.c
OBJS += src/ap/p2p_hostapd.c
+OBJS += src/utils/bitfield.c
L_CFLAGS += -DCONFIG_P2P
NEED_GAS=y
NEED_OFFCHANNEL=y
@@ -278,6 +280,7 @@
OBJS += hs20_supplicant.c
L_CFLAGS += -DCONFIG_HS20
CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
endif
ifdef CONFIG_INTERWORKING
@@ -507,7 +510,7 @@
ifdef CONFIG_EAP_PROXY
L_CFLAGS += -DCONFIG_EAP_PROXY
OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c
-include eap_proxy_$(CONFIG_EAP_PROXY).mk
+include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -614,10 +617,6 @@
endif
ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-L_CFLAGS += -DCONFIG_WPS2
-endif
-
# EAP-WSC
L_CFLAGS += -DCONFIG_WPS -DEAP_WSC
OBJS += wps_supplicant.c
@@ -772,6 +771,9 @@
OBJS += src/ap/eap_user_db.c
ifdef CONFIG_IEEE80211N
OBJS += src/ap/ieee802_11_ht.c
+ifdef CONFIG_IEEE80211AC
+OBJS += src/ap/ieee802_11_vht.c
+endif
endif
ifdef CONFIG_WNM
OBJS += src/ap/wnm_ap.c
@@ -787,6 +789,9 @@
ifdef CONFIG_IEEE80211N
L_CFLAGS += -DCONFIG_IEEE80211N
+ifdef CONFIG_IEEE80211AC
+L_CFLAGS += -DCONFIG_IEEE80211AC
+endif
endif
ifdef NEED_AP_MLME
@@ -794,6 +799,7 @@
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_WPS
@@ -952,7 +958,8 @@
OBJS += src/crypto/crypto_gnutls.c
OBJS_p += src/crypto/crypto_gnutls.c
ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_gnutls.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
endif
LIBS += -lgcrypt
LIBS_p += -lgcrypt
@@ -968,7 +975,8 @@
OBJS += src/crypto/crypto_cryptoapi.c
OBJS_p += src/crypto/crypto_cryptoapi.c
ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_cryptoapi.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
endif
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
@@ -983,7 +991,8 @@
OBJS += src/crypto/crypto_nss.c
OBJS_p += src/crypto/crypto_nss.c
ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_nss.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
endif
LIBS += -lnss3
LIBS_p += -lnss3
@@ -1122,7 +1131,7 @@
ifdef NEED_AES_OMAC1
NEED_AES_ENC=y
ifdef CONFIG_OPENSSL_CMAC
-CFLAGS += -DCONFIG_OPENSSL_CMAC
+L_CFLAGS += -DCONFIG_OPENSSL_CMAC
else
AESOBJS += src/crypto/aes-omac1.c
endif
@@ -1171,7 +1180,10 @@
endif
endif
-MD5OBJS = src/crypto/md5.c
+MD5OBJS =
+ifndef CONFIG_FIPS
+MD5OBJS += src/crypto/md5.c
+endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
MD5OBJS += src/crypto/md5-internal.c
@@ -1425,7 +1437,7 @@
endif
ifdef CONFIG_AUTOSCAN_PERIODIC
-CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
+L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
OBJS += autoscan_periodic.c
NEED_AUTOSCAN=y
endif
@@ -1552,12 +1564,20 @@
LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)
endif
LOCAL_SHARED_LIBRARIES := libc libcutils liblog
+ifdef CONFIG_EAP_PROXY
+LOCAL_STATIC_LIBRARIES += $(LIB_STATIC_EAP_PROXY)
+LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY)
+endif
ifeq ($(CONFIG_TLS), openssl)
LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder
endif
ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+LOCAL_SHARED_LIBRARIES += libnl
+else
LOCAL_STATIC_LIBRARIES += libnl_2
endif
+endif
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := $(OBJS)
LOCAL_C_INCLUDES := $(INCLUDES)
@@ -1597,4 +1617,5 @@
LOCAL_SHARED_LIBRARIES := libcutils liblog
LOCAL_COPY_HEADERS_TO := libwpa_client
LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h
+LOCAL_COPY_HEADERS += src/common/qca-vendor.h
include $(BUILD_SHARED_LIBRARY)
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index 3f10e11..5558a5e 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,8 +1,233 @@
ChangeLog for wpa_supplicant
-????-??-?? - v2.1
- * added support for simulataneous authentication of equals (SAE) for
+2014-06-04 - v2.2
+ * added DFS indicator to get_capability freq
+ * added/fixed nl80211 functionality
+ - BSSID/frequency hint for driver-based BSS selection
+ - fix tearing down WDS STA interfaces
+ - support vendor specific driver command
+ (VENDOR <vendor id> <sub command id> [<hex formatted data>])
+ - GO interface teardown optimization
+ - allow beacon interval to be configured for IBSS
+ - add SHA256-based AKM suites to CONNECT/ASSOCIATE commands
+ * removed unused NFC_RX_HANDOVER_REQ and NFC_RX_HANDOVER_SEL control
+ interface commands (the more generic NFC_REPORT_HANDOVER is now used)
+ * fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding;
+ this fixes password with include UTF-8 characters that use
+ three-byte encoding EAP methods that use NtPasswordHash
+ * fixed couple of sequencies where radio work items could get stuck,
+ e.g., when rfkill blocking happens during scanning or when
+ scan-for-auth workaround is used
+ * P2P enhancements/fixes
+ - enable enable U-APSD on GO automatically if the driver indicates
+ support for this
+ - fixed some service discovery cases with broadcast queries not being
+ sent to all stations
+ - fixed Probe Request frame triggering invitation to trigger only a
+ single invitation instance even if multiple Probe Request frames are
+ received
+ - fixed a potential NULL pointer dereference crash when processing an
+ invalid Invitation Request frame
+ - add optional configuration file for the P2P_DEVICE parameters
+ - optimize scan for GO during persistent group invocation
+ - fix possible segmentation fault when PBC overlap is detected while
+ using a separate P2P group interface
+ - improve GO Negotiation robustness by allowing GO Negotiation
+ Confirmation to be retransmitted
+ - do use freed memory on device found event when P2P NFC
+ * added phase1 network parameter options for disabling TLS v1.1 and v1.2
+ to allow workarounds with misbehaving AAA servers
+ (tls_disable_tlsv1_1=1 and tls_disable_tlsv1_2=1)
+ * added support for OCSP stapling to validate AAA server certificate
+ during TLS exchange
+ * Interworking/Hotspot 2.0 enhancements
+ - prefer the last added network in Interworking connection to make the
+ behavior more consistent with likely user expectation
+ - roaming partner configuration (roaming_partner within a cred block)
+ - support Hotspot 2.0 Release 2
+ * "hs20_anqp_get <BSSID> 8" to request OSU Providers list
+ * "hs20_icon_request <BSSID> <icon filename>" to request icon files
+ * "fetch_osu" and "cancel_osu_fetch" to start/stop full OSU provider
+ search (all suitable APs in scan results)
+ * OSEN network for online signup connection
+ * min_{dl,ul}_bandwidth_{home,roaming} cred parameters
+ * max_bss_load cred parameter
+ * req_conn_capab cred parameter
+ * sp_priority cred parameter
+ * ocsp cred parameter
+ * slow down automatic connection attempts on EAP failure to meet
+ required behavior (no more than 10 retries within a 10-minute
+ interval)
+ * sample implementation of online signup client (both SPP and
+ OMA-DM protocols) (hs20/client/*)
+ - fixed GAS indication for additional comeback delay with status
+ code 95
+ - extend ANQP_GET to accept Hotspot 2.0 subtypes
+ ANQP_GET <addr> <info id>[,<info id>]...
+ [,hs20:<subtype>][...,hs20:<subtype>]
+ - add control interface events CRED-ADDED <id>,
+ CRED-MODIFIED <id> <field>, CRED-REMOVED <id>
+ - add "GET_CRED <id> <field>" command
+ - enable FT for the connection automatically if the AP advertises
+ support for this
+ - fix a case where auto_interworking=1 could end up stopping scanning
+ * fixed TDLS interoperability issues with supported operating class in
+ some deployed stations
+ * internal TLS implementation enhancements/fixes
+ - add SHA256-based cipher suites
+ - add DHE-RSA cipher suites
+ - fix X.509 validation of PKCS#1 signature to check for extra data
+ * fixed PTK derivation for CCMP-256 and GCMP-256
+ * added "reattach" command for fast reassociate-back-to-same-BSS
+ * allow PMF to be enabled for AP mode operation with the ieee80211w
+ parameter
+ * added "get_capability tdls" command
+ * added option to set config blobs through control interface with
+ "SET blob <name> <hexdump>"
+ * D-Bus interface extensions/fixes
+ - make p2p_no_group_iface configurable
+ - declare ServiceDiscoveryRequest method properly
+ - export peer's device address as a property
+ - make reassociate command behave like the control interface one,
+ i.e., to allow connection from disconnected state
+ * added optional "freq=<channel ranges>" parameter to SET pno
+ * added optional "freq=<channel ranges>" parameter to SELECT_NETWORK
+ * fixed OBSS scan result processing for 20/40 MHz co-ex report
+ * remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled
+ whenever CONFIG_WPS=y is set
+ * fixed regression in parsing of WNM Sleep Mode exit key data
+ * fixed potential segmentation fault and memory leaks in WNM neighbor
+ report processing
+ * EAP-pwd fixes
+ - fragmentation of PWD-Confirm-Resp
+ - fix memory leak when fragmentation is used
+ - fix possible segmentation fault on EAP method deinit if an invalid
+ group is negotiated
+ * added MACsec/IEEE Std 802.1X-2010 PAE implementation (currently
+ available only with the macsec_qca driver wrapper)
+ * fixed EAP-SIM counter-too-small message
+ * added 'dup_network <id_s> <id_d> <name>' command; this can be used to
+ clone the psk field without having toextract it from wpa_supplicant
+ * fixed GSM authentication on USIM
+ * added support for usin epoll in eloop (CONFIG_ELOOP_EPOLL=y)
+ * fixed some concurrent virtual interface cases with dedicated P2P
+ management interface to not catch events from removed interface (this
+ could result in the management interface getting disabled)
+ * fixed a memory leak in SAE random number generation
+ * fixed off-by-one bounds checking in printf_encode()
+ - this could result in some control interface ATTACH command cases
+ terminating wpa_supplicant
+ * fixed EAPOL-Key exchange when GCMP is used with SHA256-based AKM
+ * various bug fixes
+
+2014-02-04 - v2.1
+ * added support for simultaneous authentication of equals (SAE) for
stronger password-based authentication with WPA2-Personal
+ * improved P2P negotiation and group formation robustness
+ - avoid unnecessary Dialog Token value changes during retries
+ - avoid more concurrent scanning cases during full group formation
+ sequence
+ - do not use potentially obsolete scan result data from driver
+ cache for peer discovery/updates
+ - avoid undesired re-starting of GO negotiation based on Probe
+ Request frames
+ - increase GO Negotiation and Invitation timeouts to address busy
+ environments and peers that take long time to react to messages,
+ e.g., due to power saving
+ - P2P Device interface type
+ * improved P2P channel selection (use more peer information and allow
+ more local options)
+ * added support for optional per-device PSK assignment by P2P GO
+ (wpa_cli p2p_set per_sta_psk <0/1>)
+ * added P2P_REMOVE_CLIENT for removing a client from P2P groups
+ (including persistent groups); this can be used to securely remove
+ a client from a group if per-device PSKs are used
+ * added more configuration flexibility for allowed P2P GO/client
+ channels (p2p_no_go_freq list and p2p_add_cli_chan=0/1)
+ * added nl80211 functionality
+ - VHT configuration for nl80211
+ - MFP (IEEE 802.11w) information for nl80211 command API
+ - support split wiphy dump
+ - FT (IEEE 802.11r) with driver-based SME
+ - use advertised number of supported concurrent channels
+ - QoS Mapping configuration
+ * improved TDLS negotiation robustness
+ * added more TDLS peer parameters to be configured to the driver
+ * optimized connection time by allowing recently received scan results
+ to be used instead of having to run through a new scan
+ * fixed ctrl_iface BSS command iteration with RANGE argument and no
+ exact matches; also fixed argument parsing for some cases with
+ multiple arguments
+ * added 'SCAN TYPE=ONLY' ctrl_iface command to request manual scan
+ without executing roaming/network re-selection on scan results
+ * added Session-Id derivation for EAP peer methods
+ * added fully automated regression testing with mac80211_hwsim
+ * changed configuration parser to reject invalid integer values
+ * allow AP/Enrollee to be specified with BSSID instead of UUID for
+ WPS ER operations
+ * disable network block temporarily on repeated connection failures
+ * changed the default driver interface from wext to nl80211 if both are
+ included in the build
+ * remove duplicate networks if WPS provisioning is run multiple times
+ * remove duplicate networks when Interworking network selection uses the
+ same network
+ * added global freq_list configuration to allow scan frequencies to be
+ limited for all cases instead of just for a specific network block
+ * added support for BSS Transition Management
+ * added option to use "IFNAME=<ifname> " prefix to use the global
+ control interface connection to perform per-interface commands;
+ similarly, allow global control interface to be used as a monitor
+ interface to receive events from all interfaces
+ * fixed OKC-based PMKSA cache entry clearing
+ * fixed TKIP group key configuration with FT
+ * added support for using OCSP stapling to validate server certificate
+ (ocsp=1 as optional and ocsp=2 as mandatory)
+ * added EAP-EKE peer
+ * added peer restart detection for IBSS RSN
+ * added domain_suffix_match (and domain_suffix_match2 for Phase 2
+ EAP-TLS) to specify additional constraint for the server certificate
+ domain name
+ * added support for external SIM/USIM processing in EAP-SIM, EAP-AKA,
+ and EAP-AKA' (CTRL-REQ-SIM and CTRL-RSP-SIM commands over control
+ interface)
+ * added global bgscan configuration option as a default for all network
+ blocks that do not specify their own bgscan parameters
+ * added D-Bus methods for TDLS
+ * added more control to scan requests
+ - "SCAN freq=<freq list>" can be used to specify which channels are
+ scanned (comma-separated frequency ranges in MHz)
+ - "SCAN passive=1" can be used to request a passive scan (no Probe
+ Request frames are sent)
+ - "SCAN use_id" can be used to request a scan id to be returned and
+ included in event messages related to this specific scan operation
+ - "SCAN only_new=1" can be used to request the driver/cfg80211 to
+ report only BSS entries that have been updated during this scan
+ round
+ - these optional arguments to the SCAN command can be combined with
+ each other
+ * modified behavior on externally triggered scans
+ - avoid concurrent operations requiring full control of the radio when
+ an externally triggered scan is detected
+ - do not use results for internal roaming decision
+ * added a new cred block parameter 'temporary' to allow credential
+ blocks to be stored separately even if wpa_supplicant configuration
+ file is used to maintain other network information
+ * added "radio work" framework to schedule exclusive radio operations
+ for off-channel functionality
+ - reduce issues with concurrent operations that try to control which
+ channel is used
+ - allow external programs to request exclusive radio control in a way
+ that avoids conflicts with wpa_supplicant
+ * added support for using Protected Dual of Public Action frames for
+ GAS/ANQP exchanges when associated with PMF
+ * added support for WPS+NFC updates and P2P+NFC
+ - improved protocol for WPS
+ - P2P group formation/join based on NFC connection handover
+ - new IPv4 address assignment for P2P groups (ip_addr_* configuration
+ parameters on the GO) to replace DHCP
+ - option to fetch and report alternative carrier records for external
+ NFC operations
+ * various bug fixes
2013-01-12 - v2.0
* removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 5698619..8f7c23f 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -10,11 +10,17 @@
export BINDIR ?= /usr/local/sbin/
PKG_CONFIG ?= pkg-config
-CFLAGS += -I../src
-CFLAGS += -I../src/utils
+CFLAGS += -I$(abspath ../src)
+CFLAGS += -I$(abspath ../src/utils)
-include .config
+ifdef CONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CONFIG_WPS_TESTING=y
+CONFIG_TDLS_TESTING=y
+endif
+
BINALL=wpa_supplicant wpa_cli
ifndef CONFIG_NO_WPA_PASSPHRASE
@@ -100,10 +106,10 @@
LDFLAGS += -rdynamic
CFLAGS += -funwind-tables
ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DWPA_TRACE_BFD
-LIBS += -lbfd
-LIBS_p += -lbfd
-LIBS_c += -lbfd
+CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD
+LIBS += -lbfd -ldl -liberty -lz
+LIBS_p += -lbfd -ldl -liberty -lz
+LIBS_c += -lbfd -ldl -liberty -lz
endif
endif
@@ -113,15 +119,32 @@
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_p += -lrt
+endif
+
ifdef CONFIG_ELOOP_POLL
CFLAGS += -DCONFIG_ELOOP_POLL
endif
+ifdef CONFIG_ELOOP_EPOLL
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
ifdef CONFIG_EAPOL_TEST
CFLAGS += -Werror -DEAPOL_TEST
endif
+ifdef CONFIG_CODE_COVERAGE
+CFLAGS += -O0 -fprofile-arcs -ftest-coverage
+LIBS += -lgcov
+LIBS_c += -lgcov
+LIBS_p += -lgcov
+endif
+
ifdef CONFIG_HT_OVERRIDES
CFLAGS += -DCONFIG_HT_OVERRIDES
endif
@@ -237,6 +260,7 @@
OBJS += ../src/p2p/p2p_dev_disc.o
OBJS += ../src/p2p/p2p_group.o
OBJS += ../src/ap/p2p_hostapd.o
+OBJS += ../src/utils/bitfield.o
CFLAGS += -DCONFIG_P2P
NEED_GAS=y
NEED_OFFCHANNEL=y
@@ -257,6 +281,7 @@
OBJS += hs20_supplicant.o
CFLAGS += -DCONFIG_HS20
CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
endif
ifdef CONFIG_INTERWORKING
@@ -485,7 +510,7 @@
ifdef CONFIG_EAP_PROXY
CFLAGS += -DCONFIG_EAP_PROXY
OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o
-include eap_proxy_$(CONFIG_EAP_PROXY).mk
+include eap_proxy_$(CONFIG_EAP_PROXY).mak
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -592,10 +617,6 @@
endif
ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-CFLAGS += -DCONFIG_WPS2
-endif
-
# EAP-WSC
CFLAGS += -DCONFIG_WPS -DEAP_WSC
OBJS += wps_supplicant.o
@@ -722,6 +743,19 @@
endif
endif
+ifdef CONFIG_MACSEC
+CFLAGS += -DCONFIG_MACSEC
+NEED_AES_ENCBLOCK=y
+NEED_AES_UNWRAP=y
+NEED_AES_WRAP=y
+NEED_AES_OMAC1=y
+OBJS += wpas_kay.o
+OBJS += ../src/pae/ieee802_1x_cp.o
+OBJS += ../src/pae/ieee802_1x_kay.o
+OBJS += ../src/pae/ieee802_1x_key.o
+OBJS += ../src/pae/ieee802_1x_secy_ops.o
+endif
+
ifdef CONFIG_AP
NEED_80211_COMMON=y
NEED_EAP_COMMON=y
@@ -750,6 +784,9 @@
OBJS += ../src/ap/eap_user_db.o
ifdef CONFIG_IEEE80211N
OBJS += ../src/ap/ieee802_11_ht.o
+ifdef CONFIG_IEEE80211AC
+OBJS += ../src/ap/ieee802_11_vht.o
+endif
endif
ifdef CONFIG_WNM
OBJS += ../src/ap/wnm_ap.o
@@ -765,6 +802,9 @@
ifdef CONFIG_IEEE80211N
CFLAGS += -DCONFIG_IEEE80211N
+ifdef CONFIG_IEEE80211AC
+CFLAGS += -DCONFIG_IEEE80211AC
+endif
endif
ifdef NEED_AP_MLME
@@ -772,6 +812,7 @@
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_WPS
@@ -930,7 +971,8 @@
OBJS += ../src/crypto/crypto_gnutls.o
OBJS_p += ../src/crypto/crypto_gnutls.o
ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_gnutls.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
endif
LIBS += -lgcrypt
LIBS_p += -lgcrypt
@@ -946,7 +988,8 @@
OBJS += ../src/crypto/crypto_cryptoapi.o
OBJS_p += ../src/crypto/crypto_cryptoapi.o
ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_cryptoapi.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
endif
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
@@ -961,7 +1004,8 @@
OBJS += ../src/crypto/crypto_nss.o
OBJS_p += ../src/crypto/crypto_nss.o
ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_nss.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
endif
LIBS += -lnss3
LIBS_p += -lnss3
@@ -1231,6 +1275,11 @@
ifeq ($(CONFIG_CTRL_IFACE), udp)
CFLAGS += -DCONFIG_CTRL_IFACE_UDP
endif
+ifeq ($(CONFIG_CTRL_IFACE), udp6)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+endif
ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
endif
@@ -1239,6 +1288,12 @@
CFLAGS += -DCONFIG_CTRL_IFACE_UDP
CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
endif
+ifeq ($(CONFIG_CTRL_IFACE), udp6-remote)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+endif
OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o
endif
@@ -1436,6 +1491,19 @@
CFLAGS += -DCONFIG_OFFCHANNEL
endif
+ifdef CONFIG_MODULE_TESTS
+CFLAGS += -DCONFIG_MODULE_TESTS
+OBJS += wpas_module_tests.o
+OBJS += ../src/utils/utils_module_tests.o
+OBJS += ../src/common/common_module_tests.o
+ifdef CONFIG_WPS
+OBJS += ../src/wps/wps_module_tests.o
+endif
+ifndef CONFIG_P2P
+OBJS += ../src/utils/bitfield.o
+endif
+endif
+
OBJS += ../src/drivers/driver_common.o
OBJS_priv += ../src/drivers/driver_common.o
@@ -1601,9 +1669,15 @@
$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
-D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
+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
%.service: %.service.in
sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
@@ -1655,11 +1729,18 @@
fips:
$(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)"
+lcov-html: wpa_supplicant.gcda
+ lcov -c -d .. > lcov.info
+ genhtml lcov.info --output-directory lcov-html
+
clean:
$(MAKE) -C ../src clean
$(MAKE) -C dbus clean
- rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
+ rm -f core *~ *.o *.d *.gcno *.gcda *.gcov
+ rm -f eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
rm -f wpa_priv
rm -f nfc_pw_token
+ rm -f lcov.info
+ rm -rf lcov-html
-include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index 78df89e..653848e 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -1,7 +1,7 @@
WPA Supplicant
==============
-Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
@@ -413,7 +413,7 @@
[-G<group>] \
-i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \
[-b<br_ifname> [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \
- [-p<driver_param>] [-b<br_ifname>] ...]
+ [-p<driver_param>] [-b<br_ifname>] [-m<P2P Device config file>] ...
options:
-b = optional bridge interface name
@@ -438,6 +438,7 @@
-w = wait for interface to be added, if needed
-W = wait for a control interface monitor before starting
-N = start describing new interface
+ -m = Configuration file for the P2P Device
drivers:
nl80211 = Linux nl80211/cfg80211
@@ -949,3 +950,105 @@
wpa_priv can control multiple interface with one process, but it is
also possible to run multiple wpa_priv processes at the same time, if
desired.
+
+
+Linux capabilities instead of privileged process
+------------------------------------------------
+
+wpa_supplicant performs operations that need special permissions, e.g.,
+to control the network connection. Traditionally this has been achieved
+by running wpa_supplicant as a privileged process with effective user id
+0 (root). Linux capabilities can be used to provide restricted set of
+capabilities to match the functions needed by wpa_supplicant. The
+minimum set of capabilities needed for the operations is CAP_NET_ADMIN
+and CAP_NET_RAW.
+
+setcap(8) can be used to set file capabilities. For example:
+
+sudo setcap cap_net_raw,cap_net_admin+ep wpa_supplicant
+
+Please note that this would give anyone being able to run that
+wpa_supplicant binary access to the additional capabilities. This can
+further be limited by file owner/group and mode bits. For example:
+
+sudo chown wpas wpa_supplicant
+sudo chmod 0100 wpa_supplicant
+
+This combination of setcap, chown, and chmod commands would allow wpas
+user to execute wpa_supplicant with additional network admin/raw
+capabilities.
+
+Common way style of creating a control interface socket in
+/var/run/wpa_supplicant could not be done by this user, but this
+directory could be created before starting the wpa_supplicant and set to
+suitable mode to allow wpa_supplicant to create sockets
+there. Alternatively, other directory or abstract socket namespace could
+be used for the control interface.
+
+
+External requests for radio control
+-----------------------------------
+
+External programs can request wpa_supplicant to not start offchannel
+operations during other tasks that may need exclusive control of the
+radio. The RADIO_WORK control interface command can be used for this.
+
+"RADIO_WORK add <name> [freq=<MHz>] [timeout=<seconds>]" command can be
+used to reserve a slot for radio access. If freq is specified, other
+radio work items on the same channel may be completed in
+parallel. Otherwise, all other radio work items are blocked during
+execution. Timeout is set to 10 seconds by default to avoid blocking
+wpa_supplicant operations for excessive time. If a longer (or shorter)
+safety timeout is needed, that can be specified with the optional
+timeout parameter. This command returns an identifier for the radio work
+item.
+
+Once the radio work item has been started, "EXT-RADIO-WORK-START <id>"
+event message is indicated that the external processing can start. Once
+the operation has been completed, "RADIO_WORK done <id>" is used to
+indicate that to wpa_supplicant. This allows other radio works to be
+performed. If this command is forgotten (e.g., due to the external
+program terminating), wpa_supplicant will time out the radio owrk item
+and send "EXT-RADIO-WORK-TIMEOUT <id>" event ot indicate that this has
+happened. "RADIO_WORK done <id>" can also be used to cancel items that
+have not yet been started.
+
+For example, in wpa_cli interactive mode:
+
+> radio_work add test
+1
+<3>EXT-RADIO-WORK-START 1
+> radio_work show
+ext:test@wlan0:0:1:2.487797
+> radio_work done 1
+OK
+> radio_work show
+
+
+> radio_work done 3
+OK
+> radio_work show
+ext:test freq=2412 timeout=30@wlan0:2412:1:28.583483
+<3>EXT-RADIO-WORK-TIMEOUT 2
+
+
+> radio_work add test2 freq=2412 timeout=60
+5
+<3>EXT-RADIO-WORK-START 5
+> radio_work add test3
+6
+> radio_work add test4
+7
+> radio_work show
+ext:test2 freq=2412 timeout=60@wlan0:2412:1:9.751844
+ext:test3@wlan0:0:0:5.071812
+ext:test4@wlan0:0:0:3.143870
+> radio_work done 6
+OK
+> radio_work show
+ext:test2 freq=2412 timeout=60@wlan0:2412:1:16.287869
+ext:test4@wlan0:0:0:9.679895
+> radio_work done 5
+OK
+<3>EXT-RADIO-WORK-START 7
+<3>EXT-RADIO-WORK-TIMEOUT 7
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index 5669c55..58c2475 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -109,6 +109,8 @@
#
# credential fields:
#
+# temporary: Whether this credential is temporary and not to be saved
+#
# priority: Priority group
# By default, all networks and credentials get the same priority group
# (0). This field can be used to give higher priority for credentials
@@ -166,9 +168,25 @@
# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
# format
#
-# domain: Home service provider FQDN
+# domain_suffix_match: Constraint for server domain name
+# If set, this FQDN is used as a suffix match requirement for the AAA
+# 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.
+#
+# domain: Home service provider FQDN(s)
# This is used to compare against the Domain Name List to figure out
-# whether the AP is operated by the Home SP.
+# whether the AP is operated by the Home SP. Multiple domain entries can
+# be used to configure alternative FQDNs that will be considered home
+# networks.
#
# roaming_consortium: Roaming Consortium OI
# If roaming_consortium_len is non-zero, this field contains the
@@ -195,6 +213,65 @@
# matching with the network. Multiple entries can be used to specify more
# than one SSID.
#
+# roaming_partner: Roaming partner information
+# This optional field can be used to configure preferences between roaming
+# partners. The field is a string in following format:
+# <FQDN>,<0/1 exact match>,<priority>,<* or country code>
+# (non-exact match means any subdomain matches the entry; priority is in
+# 0..255 range with 0 being the highest priority)
+#
+# update_identifier: PPS MO ID
+# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# provisioning_sp: FQDN of the SP that provisioned the credential
+# This optional field can be used to keep track of the SP that provisioned
+# the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
+#
+# sp_priority: Credential priority within a provisioning SP
+# This is the priority of the credential among all credentials
+# provisionined by the same SP (i.e., for entries that have identical
+# provisioning_sp value). The range of this priority is 0-255 with 0
+# being the highest and 255 the lower priority.
+#
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+# These fields can be used to specify minimum download/upload backhaul
+# bandwidth that is preferred for the credential. This constraint is
+# ignored if the AP does not advertise WAN Metrics information or if the
+# limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
+# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
+# (PPS/<X+>/Policy/MaximumBSSLoadValue)
+# This value is used as the maximum channel utilization for network
+# selection purposes for home networks. If the AP does not advertise
+# BSS Load or if the limit would prevent any connection, this constraint
+# will be ignored.
+#
+# req_conn_capab: Required connection capability
+# (PPS/<X+>/Policy/RequiredProtoPortTuple)
+# This value is used to configure set of required protocol/port pairs that
+# a roaming network shall support (include explicitly in Connection
+# Capability ANQP element). This constraint is ignored if the AP does not
+# advertise Connection Capability or if this constraint would prevent any
+# network connection. This policy is not used in home networks.
+# Format: <protocol>[:<comma-separated list of ports]
+# Multiple entries can be used to list multiple requirements.
+# For example, number of common TCP protocols:
+# req_conn_capab=6:22,80,443
+# For example, IPSec/IKE:
+# req_conn_capab=17:500
+# req_conn_capab=50
+#
+# 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
+#
+# sim_num: Identifier for which SIM to use in multi-SIM devices
+#
# for example:
#
#cred={
@@ -203,6 +280,7 @@
# password="password"
# ca_cert="/etc/wpa_supplicant/ca.pem"
# domain="example.com"
+# domain_suffix_match="example.com"
#}
#
#cred={
@@ -252,6 +330,8 @@
OK
> set_cred 0 priority 1
OK
+> set_cred 0 temporary 1
+OK
Add a SIM credential using a simulated SIM/USIM card for testing:
@@ -267,6 +347,17 @@
Note: the return value of add_cred is used as the first argument to
the following set_cred commands.
+Add a SIM credential using a external SIM/USIM processing:
+
+> set external_sim 1
+OK
+> add_cred
+1
+> set_cred 1 imsi "23456-0000000000"
+OK
+> set_cred 1 eap SIM
+OK
+
Add a WPA2-Enterprise network:
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
index 76f8219..5c1e4f9 100644
--- a/wpa_supplicant/README-P2P
+++ b/wpa_supplicant/README-P2P
@@ -72,7 +72,8 @@
Device Discovery
p2p_find [timeout in seconds] [type=<social|progressive>] \
- [dev_id=<addr>] [delay=<search delay in ms>]
+ [dev_id=<addr>] [dev_type=<device type>] \
+ [delay=<search delay in ms>]
The default behavior is to run a single full scan in the beginning and
then scan only social channels. type=social will scan only social
@@ -87,6 +88,10 @@
delay to be used between search iterations (e.g., to free up radio
resources for concurrent operations).
+The optional dev_type option can be used to specify a single device type
+(primary or secondary) to search for, e.g.,
+"p2p_find dev_type=1-0050F204-1".
+
p2p_listen [timeout in seconds]
Start Listen-only state (become discoverable without searching for
@@ -125,7 +130,7 @@
p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad]
[persistent|persistent=<network id>] [join|auth]
- [go_intent=<0..15>] [freq=<in MHz>] [ht40] [provdisc]
+ [go_intent=<0..15>] [freq=<in MHz>] [ht40] [vht] [provdisc]
Start P2P group formation with a discovered P2P peer. This includes
optional group owner negotiation, group interface setup, provisioning,
@@ -166,7 +171,8 @@
P2P implementations that require this to allow the user to accept the
connection.
-p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] [ht40]
+p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>]
+ [ht40] [vht]
Set up a P2P group owner manually (i.e., without group owner
negotiation with a specific peer). This is also known as autonomous
@@ -224,9 +230,8 @@
peers (note: this can result in long response frames). The pending
requests are sent during device discovery (see p2p_find).
-Only a single pending wildcard query is supported, but there can be
-multiple pending peer device specific queries (each will be sent in
-sequence whenever the peer is found).
+There can be multiple pending peer device specific queries (each will be
+sent in sequence whenever the peer is found).
This command returns an identifier for the pending query (e.g.,
"1f77628") that can be used to cancel the request. Directed requests
@@ -373,7 +378,8 @@
Invitation
p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address]
- [go_dev_addr=address] [freq=<freq in MHz>] [ht40] [pref=<MHz>]
+ [go_dev_addr=address] [freq=<freq in MHz>] [ht40] [vht]
+ [pref=<MHz>]
Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a
persistent group (e.g., persistent=4). If the peer device is the GO of
@@ -416,9 +422,11 @@
Send a P2P Presence Request to the GO (this is only available when
acting as a P2P client). If no duration/interval pairs are given, the
request indicates that this client has no special needs for GO
-presence. the first parameter pair gives the preferred duration and
+presence. The first parameter pair gives the preferred duration and
interval values in microseconds. If the second pair is included, that
-indicates which value would be acceptable.
+indicates which value would be acceptable. This command returns OK
+immediately and the response from the GO is indicated in a
+P2P-PRESENCE-RESPONSE event message.
Parameters
@@ -546,6 +554,13 @@
Set country code (this is included in some P2P messages).
+set p2p_search_delay <delay>
+
+Set p2p_search_delay which adds extra delay in milliseconds between
+concurrent search iterations to make p2p_find friendlier to concurrent
+operations by avoiding it from taking 100% of radio resources. The
+default value is 500 ms.
+
Status
p2p_peers [discovered]
diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS
index 3d07109..b884f67 100644
--- a/wpa_supplicant/README-WPS
+++ b/wpa_supplicant/README-WPS
@@ -60,7 +60,6 @@
CONFIG_DRIVER_NL80211=y
CONFIG_WPS=y
-CONFIG_WPS2=y
If you want to enable WPS external registrar (ER) functionality, you
will also need to add following line:
@@ -366,11 +365,11 @@
token is used to enable enrollment of a new station (that was the source
of the NFC password token).
-"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the
-contents of a Handover Request Message for connection handover. 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_get_handover_req <NDEF> <WPS-CR>" command can be used to build the
+WPS carrier record for a Handover Request Message for connection
+handover. The first argument selects the format of the output data and
+the second argument selects which type of connection handover is
+requested (WPS-CR = Wi-Fi handover as specified in WSC 2.0).
"nfc_get_handover_sel <NDEF> <WPS> [UUID|BSSID]" command can be used to
build the contents of a Handover Select Message for connection handover
@@ -382,17 +381,6 @@
message for the specified AP when wpa_supplicant is operating as a WPS
ER.
-"nfc_rx_handover_req <hexdump of payload>" is used to indicate receipt
-of NFC connection handover request. The payload may include multiple
-carriers the the applicable ones are matched based on the media
-type. The reply data is contents for the Handover Select Message
-(hexdump).
-
-"nfc_rx_handover_sel <hexdump of payload>" is used to indicate receipt
-of NFC connection handover select. The payload may include multiple
-carriers the the applicable ones are matched based on the media
-type.
-
"nfc_report_handover <INIT/RESP> WPS <carrier from handover request>
<carrier from handover select>" can be used as an alternative way for
reporting completed NFC connection handover. The first parameter
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 8b3d6b4..3ed734d 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -20,63 +20,6 @@
# used to fix build issues on such systems (krb5.h not found).
#CFLAGS += -I/usr/include/kerberos
-# Example configuration for various cross-compilation platforms
-
-#### sveasoft (e.g., for Linksys WRT54G) ######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
-#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
-###############################################################################
-
-#### openwrt (e.g., for Linksys WRT54G) #######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
-# -I../WRT54GS/release/src/include
-#LIBS = -lssl
-###############################################################################
-
-
-# Driver interface for Host AP driver
-#CONFIG_DRIVER_HOSTAP=y
-
-# Driver interface for Agere driver
-#CONFIG_DRIVER_HERMES=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
-#CFLAGS += -I../../include/wireless
-
-# Driver interface for madwifi driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_MADWIFI=y
-# Set include directory to the madwifi source tree
-#CFLAGS += -I../../madwifi
-
-# Driver interface for ndiswrapper
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_NDISWRAPPER=y
-
-# Driver interface for Atmel driver
-#CONFIG_DRIVER_ATMEL=y
-
-# Driver interface for old Broadcom driver
-# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
-# Linux wireless extensions and does not need (or even work) with the old
-# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
-#CONFIG_DRIVER_BROADCOM=y
-# Example path for wlioctl.h; change to match your configuration
-#CFLAGS += -I/opt/WRT54GS/release/src/include
-
-# Driver interface for Intel ipw2100/2200 driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_IPW=y
-
-# Driver interface for Ralink driver
-#CONFIG_DRIVER_RALINK=y
-
# Driver interface for generic Linux wireless extensions
# Note: WEXT is deprecated in the current Linux kernel version and no new
# functionality is added to it. nl80211-based interface is the new
@@ -180,7 +123,7 @@
# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used).
# This requires CONFIG_EAP_AKA to be enabled, too.
-#CONFIG_EAP_AKA_PRIME=y
+CONFIG_EAP_AKA_PRIME=y
# Enable USIM simulator (Milenage) for EAP-AKA
#CONFIG_USIM_SIMULATOR=y
@@ -198,8 +141,6 @@
# Wi-Fi Protected Setup (WPS)
CONFIG_WPS=y
-# Enable WSC 2.0 support
-CONFIG_WPS2=y
# Enable WPS external registrar functionality
CONFIG_WPS_ER=y
# Disable credentials for an open network by default when acting as a WPS
@@ -296,7 +237,7 @@
# main_none = Very basic example (development use only)
#CONFIG_MAIN=main
-# Select wrapper for operatins system and C library specific functions
+# Select wrapper for operating system and C library specific functions
# unix = UNIX/POSIX like systems (default)
# win32 = Windows systems
# none = Empty template
@@ -305,12 +246,14 @@
# Select event loop implementation
# eloop = select() loop (default)
# eloop_win = Windows events and WaitForMultipleObject() loop
-# eloop_none = Empty template
CONFIG_ELOOP=eloop
# Should we use poll instead of select? Select is used by default.
#CONFIG_ELOOP_POLL=y
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
# Select layer 2 packet implementation
# linux = Linux packet socket (default)
# pcap = libpcap/libdnet/WinPcap
@@ -325,7 +268,7 @@
# IEEE 802.11w (management frame protection), also known as PMF
# Driver support is also needed for IEEE 802.11w.
-#CONFIG_IEEE80211W=y
+CONFIG_IEEE80211W=y
# Select TLS implementation
# openssl = OpenSSL (default)
@@ -403,7 +346,7 @@
#CONFIG_DYNAMIC_EAP_METHODS=y
# IEEE Std 802.11r-2008 (Fast BSS Transition)
-#CONFIG_IEEE80211R=y
+CONFIG_IEEE80211R=y
# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
#CONFIG_DEBUG_FILE=y
@@ -484,16 +427,16 @@
# Wireless Network Management (IEEE Std 802.11v-2011)
# Note: This is experimental and not complete implementation.
-#CONFIG_WNM=y
+CONFIG_WNM=y
# Interworking (IEEE 802.11u)
# This can be used to enable functionality to improve interworking with
# external networks (GAS/ANQP to learn more about the networks and network
# selection based on available credentials).
-#CONFIG_INTERWORKING=y
+CONFIG_INTERWORKING=y
# Hotspot 2.0
-#CONFIG_HS20=y
+CONFIG_HS20=y
# Disable roaming in wpa_supplicant
CONFIG_NO_ROAMING=y
@@ -510,14 +453,17 @@
# more information on P2P operations.
CONFIG_P2P=y
+# Enable TDLS support
CONFIG_TDLS=y
-#Enable Wifi Display
+# Wi-Fi Direct
+# This can be used to enable Wi-Fi Direct extensions for P2P using an external
+# program to control the additional information exchanges in the messages.
CONFIG_WIFI_DISPLAY=y
# Autoscan
# This can be used to enable automatic scan support in wpa_supplicant.
-# See wpa_supplicant.conf for more information on autoscan usage.
+# See wpa_supplicant.conf for more information on autoscan usage.
#
# Enabling directly a module will enable autoscan support.
# For exponential module:
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index fdbe248..f9aa807 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -15,6 +15,7 @@
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "eapol_supp/eapol_supp_sm.h"
+#include "crypto/dh_group5.h"
#include "ap/hostapd.h"
#include "ap/ap_config.h"
#include "ap/ap_drv_ops.h"
@@ -42,11 +43,43 @@
#endif /* CONFIG_WPS */
+#ifdef CONFIG_IEEE80211N
+static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
+ struct hostapd_config *conf,
+ struct hostapd_hw_modes *mode)
+{
+#ifdef CONFIG_P2P
+ u8 center_chan = 0;
+ u8 channel = conf->channel;
+
+ if (!conf->secondary_channel)
+ goto no_vht;
+
+ center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+ if (!center_chan)
+ goto no_vht;
+
+ /* Use 80 MHz channel */
+ conf->vht_oper_chwidth = 1;
+ conf->vht_oper_centr_freq_seg0_idx = center_chan;
+ return;
+
+no_vht:
+ conf->vht_oper_centr_freq_seg0_idx =
+ channel + conf->secondary_channel * 2;
+#else /* CONFIG_P2P */
+ conf->vht_oper_centr_freq_seg0_idx =
+ conf->channel + conf->secondary_channel * 2;
+#endif /* CONFIG_P2P */
+}
+#endif /* CONFIG_IEEE80211N */
+
+
static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct hostapd_config *conf)
{
- struct hostapd_bss_config *bss = &conf->bss[0];
+ struct hostapd_bss_config *bss = conf->bss[0];
conf->driver = wpa_s->driver;
@@ -114,6 +147,11 @@
HT_CAP_INFO_SHORT_GI40MHZ |
HT_CAP_INFO_RX_STBC_MASK |
HT_CAP_INFO_MAX_AMSDU_SIZE);
+
+ if (mode->vht_capab && ssid->vht) {
+ conf->ieee80211ac = 1;
+ wpas_conf_ap_vht(wpa_s, conf, mode);
+ }
}
}
#endif /* CONFIG_IEEE80211N */
@@ -149,6 +187,16 @@
bss->isolate = !wpa_s->conf->p2p_intra_bss;
bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
+
+ if (ssid->p2p_group) {
+ os_memcpy(bss->ip_addr_go, wpa_s->parent->conf->ip_addr_go, 4);
+ os_memcpy(bss->ip_addr_mask, wpa_s->parent->conf->ip_addr_mask,
+ 4);
+ os_memcpy(bss->ip_addr_start,
+ wpa_s->parent->conf->ip_addr_start, 4);
+ os_memcpy(bss->ip_addr_end, wpa_s->parent->conf->ip_addr_end,
+ 4);
+ }
#endif /* CONFIG_P2P */
if (ssid->ssid_len == 0) {
@@ -244,7 +292,9 @@
if (bss->wpa_group_rekey < 86400 && (bss->wpa & 2) &&
(bss->wpa_group == WPA_CIPHER_CCMP ||
- bss->wpa_group == WPA_CIPHER_GCMP)) {
+ bss->wpa_group == WPA_CIPHER_GCMP ||
+ bss->wpa_group == WPA_CIPHER_CCMP_256 ||
+ bss->wpa_group == WPA_CIPHER_GCMP_256)) {
/*
* Strong ciphers do not need frequent rekeying, so increase
* the default GTK rekeying period to 24 hours.
@@ -252,6 +302,11 @@
bss->wpa_group_rekey = 86400;
}
+#ifdef CONFIG_IEEE80211W
+ if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT)
+ bss->ieee80211w = ssid->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+
#ifdef CONFIG_WPS
/*
* Enable WPS by default for open and WPA/WPA2-Personal network, but
@@ -261,12 +316,10 @@
if (bss->ssid.security_policy != SECURITY_WPA_PSK &&
bss->ssid.security_policy != SECURITY_PLAINTEXT)
goto no_wps;
-#ifdef CONFIG_WPS2
if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
(!(bss->rsn_pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
goto no_wps; /* WPS2 does not allow WPA/TKIP-only
* configuration */
-#endif /* CONFIG_WPS2 */
bss->eap_server = 1;
if (!ssid->ignore_broadcast_ssid)
@@ -320,16 +373,16 @@
#ifdef CONFIG_P2P
struct wpa_supplicant *wpa_s = ctx;
const struct ieee80211_mgmt *mgmt;
- size_t hdr_len;
mgmt = (const struct ieee80211_mgmt *) buf;
- hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
- if (hdr_len > len)
+ if (len < IEEE80211_HDRLEN + 1)
+ return;
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
return;
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
mgmt->u.action.category,
- &mgmt->u.action.u.vs_public_action.action,
- len - hdr_len, freq);
+ buf + IEEE80211_HDRLEN + 1,
+ len - IEEE80211_HDRLEN - 1, freq);
#endif /* CONFIG_P2P */
}
@@ -385,16 +438,14 @@
#ifdef CONFIG_P2P
struct wpa_supplicant *wpa_s = ctx;
const struct ieee80211_mgmt *mgmt;
- size_t hdr_len;
mgmt = (const struct ieee80211_mgmt *) buf;
- hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
- if (hdr_len > len)
+ if (len < IEEE80211_HDRLEN + 1)
return -1;
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
mgmt->u.action.category,
- &mgmt->u.action.u.vs_public_action.action,
- len - hdr_len, freq);
+ buf + IEEE80211_HDRLEN + 1,
+ len - IEEE80211_HDRLEN - 1, freq);
#endif /* CONFIG_P2P */
return 0;
}
@@ -404,23 +455,17 @@
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal)
{
-#ifdef CONFIG_P2P
struct wpa_supplicant *wpa_s = ctx;
return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
ssi_signal);
-#else /* CONFIG_P2P */
- return 0;
-#endif /* CONFIG_P2P */
}
static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
const u8 *uuid_e)
{
-#ifdef CONFIG_P2P
struct wpa_supplicant *wpa_s = ctx;
wpas_p2p_wps_success(wpa_s, mac_addr, 1);
-#endif /* CONFIG_P2P */
}
@@ -458,17 +503,13 @@
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
switch (ssid->mode) {
- case WPAS_MODE_INFRA:
- params.mode = IEEE80211_MODE_INFRA;
- break;
- case WPAS_MODE_IBSS:
- params.mode = IEEE80211_MODE_IBSS;
- break;
case WPAS_MODE_AP:
case WPAS_MODE_P2P_GO:
case WPAS_MODE_P2P_GROUP_FORMATION:
params.mode = IEEE80211_MODE_AP;
break;
+ default:
+ return -1;
}
if (ssid->frequency == 0)
ssid->frequency = 2462; /* default channel 11 */
@@ -479,7 +520,7 @@
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
else
wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
- params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+ params.key_mgmt_suite = wpa_s->key_mgmt;
wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher,
1);
@@ -488,8 +529,7 @@
"cipher.");
return -1;
}
- params.pairwise_suite =
- wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
+ params.pairwise_suite = wpa_s->pairwise_cipher;
params.group_suite = params.pairwise_suite;
#ifdef CONFIG_P2P
@@ -500,6 +540,8 @@
if (wpa_s->parent->set_ap_uapsd)
params.uapsd = wpa_s->parent->ap_uapsd;
+ else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+ params.uapsd = 1; /* mandatory for P2P GO */
else
params.uapsd = -1;
@@ -529,8 +571,8 @@
sizeof(wpa_s->conf->wmm_ac_params));
if (params.uapsd > 0) {
- conf->bss->wmm_enabled = 1;
- conf->bss->wmm_uapsd = 1;
+ conf->bss[0]->wmm_enabled = 1;
+ conf->bss[0]->wmm_uapsd = 1;
}
if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
@@ -541,9 +583,9 @@
#ifdef CONFIG_P2P
if (ssid->mode == WPAS_MODE_P2P_GO)
- conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+ conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER;
else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
- conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+ conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER |
P2P_GROUP_FORMATION;
#endif /* CONFIG_P2P */
@@ -558,7 +600,7 @@
for (i = 0; i < conf->num_bss; i++) {
hapd_iface->bss[i] =
hostapd_alloc_bss_data(hapd_iface, conf,
- &conf->bss[i]);
+ conf->bss[i]);
if (hapd_iface->bss[i] == NULL) {
wpa_supplicant_ap_deinit(wpa_s);
return -1;
@@ -620,11 +662,10 @@
wpa_s->current_ssid = NULL;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_s->assoc_freq = 0;
-#ifdef CONFIG_P2P
- if (wpa_s->ap_iface->bss)
- wpa_s->ap_iface->bss[0]->p2p_group = NULL;
- wpas_p2p_group_deinit(wpa_s);
-#endif /* CONFIG_P2P */
+ wpas_p2p_ap_deinit(wpa_s);
+ wpa_s->ap_iface->driver_ap_teardown =
+ !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
hostapd_interface_deinit(wpa_s->ap_iface);
hostapd_interface_free(wpa_s->ap_iface);
wpa_s->ap_iface = NULL;
@@ -647,6 +688,8 @@
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
+ if (!wpa_s->ap_iface)
+ return;
hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack);
#endif /* NEED_AP_MLME */
}
@@ -909,6 +952,19 @@
return hostapd_wps_nfc_hs_cr(hapd, ndef);
}
+
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return -1;
+ hapd = wpa_s->ap_iface->bss[0];
+ return hostapd_wps_nfc_report_handover(hapd, req, sel);
+}
+
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
@@ -1010,9 +1066,9 @@
#ifdef CONFIG_P2P
if (ssid->mode == WPAS_MODE_P2P_GO)
- iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+ iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER;
else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
- iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+ iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER |
P2P_GROUP_FORMATION;
#endif /* CONFIG_P2P */
@@ -1026,14 +1082,40 @@
}
+int ap_switch_channel(struct wpa_supplicant *wpa_s,
+ struct csa_settings *settings)
+{
+#ifdef NEED_AP_MLME
+ if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+ return -1;
+
+ return hostapd_switch_channel(wpa_s->ap_iface->bss[0], settings);
+#else /* NEED_AP_MLME */
+ return -1;
+#endif /* NEED_AP_MLME */
+}
+
+
+int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
+{
+ struct csa_settings settings;
+ int ret = hostapd_parse_csa_settings(pos, &settings);
+
+ if (ret)
+ return ret;
+
+ return ap_switch_channel(wpa_s, &settings);
+}
+
+
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
- int offset)
+ int offset, int width, int cf1, int cf2)
{
if (!wpa_s->ap_iface)
return;
wpa_s->assoc_freq = freq;
- hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset);
+ hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1);
}
@@ -1076,3 +1158,48 @@
return 0;
}
+
+
+#ifdef CONFIG_WPS_NFC
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+ const struct wpabuf *pw, const u8 *pubkey_hash)
+{
+ struct hostapd_data *hapd;
+ struct wps_context *wps;
+
+ if (!wpa_s->ap_iface)
+ return -1;
+ hapd = wpa_s->ap_iface->bss[0];
+ wps = hapd->wps;
+
+ if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL ||
+ wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known");
+ return -1;
+ }
+
+ dh5_free(wps->dh_ctx);
+ wpabuf_free(wps->dh_pubkey);
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = wpabuf_dup(
+ wpa_s->parent->conf->wps_nfc_dh_privkey);
+ wps->dh_pubkey = wpabuf_dup(
+ wpa_s->parent->conf->wps_nfc_dh_pubkey);
+ if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+ wps->dh_ctx = NULL;
+ wpabuf_free(wps->dh_pubkey);
+ wps->dh_pubkey = NULL;
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+ return -1;
+ }
+ wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+ if (wps->dh_ctx == NULL)
+ return -1;
+
+ return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash,
+ pw_id,
+ pw ? wpabuf_head(pw) : NULL,
+ pw ? wpabuf_len(pw) : 0, 1);
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 74a0b18..8aa5ffa 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -50,8 +50,11 @@
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr);
void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
+int ap_switch_channel(struct wpa_supplicant *wpa_s,
+ struct csa_settings *settings);
+int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr);
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
- int offset);
+ int offset, int width, int cf1, int cf2);
struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
int ndef);
#ifdef CONFIG_AP
@@ -66,4 +69,10 @@
}
#endif /* CONFIG_AP */
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+ const struct wpabuf *pw, const u8 *pubkey_hash);
+
#endif /* AP_H */
diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c
index 9a9bd52..f74cdbf 100644
--- a/wpa_supplicant/bgscan.c
+++ b/wpa_supplicant/bgscan.c
@@ -31,9 +31,9 @@
};
-int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ const char *name)
{
- const char *name = ssid->bgscan;
const char *params;
size_t nlen;
int i;
@@ -41,7 +41,7 @@
bgscan_deinit(wpa_s);
if (name == NULL)
- return 0;
+ return -1;
params = os_strchr(name, ':');
if (params == NULL) {
diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h
index e9d15fc..9131e4e 100644
--- a/wpa_supplicant/bgscan.h
+++ b/wpa_supplicant/bgscan.h
@@ -29,7 +29,8 @@
#ifdef CONFIG_BGSCAN
-int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ const char *name);
void bgscan_deinit(struct wpa_supplicant *wpa_s);
int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
@@ -41,7 +42,7 @@
#else /* CONFIG_BGSCAN */
static inline int bgscan_init(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid)
+ struct wpa_ssid *ssid, const char name)
{
return 0;
}
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index 07d31e4..6a92b73 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -34,7 +34,7 @@
int signal_threshold;
int short_interval; /* use if signal < threshold */
int long_interval; /* use if signal > threshold */
- struct os_time last_bgscan;
+ struct os_reltime last_bgscan;
char *fname;
struct dl_list bss;
int *supp_freqs;
@@ -240,17 +240,14 @@
if (data->supp_freqs == NULL)
return freqs;
- idx = data->probe_idx + 1;
- while (idx != data->probe_idx) {
- if (data->supp_freqs[idx] == 0) {
- if (data->probe_idx == 0)
- break;
- idx = 0;
- }
+ idx = data->probe_idx;
+ do {
if (!in_array(freqs, data->supp_freqs[idx])) {
wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq "
"%u", data->supp_freqs[idx]);
- data->probe_idx = idx;
+ data->probe_idx = idx + 1;
+ if (data->supp_freqs[data->probe_idx] == 0)
+ data->probe_idx = 0;
n = os_realloc_array(freqs, count + 2, sizeof(int));
if (n == NULL)
return freqs;
@@ -262,7 +259,9 @@
}
idx++;
- }
+ if (data->supp_freqs[idx] == 0)
+ idx = 0;
+ } while (idx != data->probe_idx);
return freqs;
}
@@ -311,7 +310,7 @@
eloop_register_timeout(data->scan_interval, 0,
bgscan_learn_timeout, data, NULL);
} else
- os_get_time(&data->last_bgscan);
+ os_get_reltime(&data->last_bgscan);
os_free(freqs);
}
@@ -363,6 +362,9 @@
for (j = 0; j < modes[i].num_channels; j++) {
if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED)
continue;
+ /* some hw modes (e.g. 11b & 11g) contain same freqs */
+ if (in_array(freqs, modes[i].channels[j].freq))
+ continue;
n = os_realloc_array(freqs, count + 2, sizeof(int));
if (n == NULL)
continue;
@@ -419,6 +421,14 @@
data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s);
data->scan_interval = data->short_interval;
+ if (data->signal_threshold) {
+ /* Poll for signal info to set initial scan interval */
+ struct wpa_signal_info siginfo;
+ if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
+ siginfo.current_signal >= data->signal_threshold)
+ data->scan_interval = data->long_interval;
+ }
+
eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
data, NULL);
@@ -428,7 +438,7 @@
* us skip an immediate new scan in cases where the current signal
* level is below the bgscan threshold.
*/
- os_get_time(&data->last_bgscan);
+ os_get_reltime(&data->last_bgscan);
return data;
}
@@ -555,7 +565,7 @@
{
struct bgscan_learn_data *data = priv;
int scan = 0;
- struct os_time now;
+ struct os_reltime now;
if (data->short_interval == data->long_interval ||
data->signal_threshold == 0)
@@ -569,7 +579,7 @@
wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan "
"interval");
data->scan_interval = data->short_interval;
- os_get_time(&now);
+ os_get_reltime(&now);
if (now.sec > data->last_bgscan.sec + 1)
scan = 1;
} else if (data->scan_interval == data->short_interval && above) {
@@ -584,7 +594,7 @@
* Signal dropped further 4 dB. Request a new scan if we have
* not yet scanned in a while.
*/
- os_get_time(&now);
+ os_get_reltime(&now);
if (now.sec > data->last_bgscan.sec + 10)
scan = 1;
}
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index 479f703..a467cc5 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -26,7 +26,7 @@
int max_short_scans; /* maximum times we short-scan before back-off */
int short_interval; /* use if signal < threshold */
int long_interval; /* use if signal > threshold */
- struct os_time last_bgscan;
+ struct os_reltime last_bgscan;
};
@@ -75,7 +75,7 @@
*/
data->short_scan_count--;
}
- os_get_time(&data->last_bgscan);
+ os_get_reltime(&data->last_bgscan);
}
}
@@ -159,7 +159,7 @@
* us skip an immediate new scan in cases where the current signal
* level is below the bgscan threshold.
*/
- os_get_time(&data->last_bgscan);
+ os_get_reltime(&data->last_bgscan);
return data;
}
@@ -211,7 +211,7 @@
{
struct bgscan_simple_data *data = priv;
int scan = 0;
- struct os_time now;
+ struct os_reltime now;
if (data->short_interval == data->long_interval ||
data->signal_threshold == 0)
@@ -225,7 +225,7 @@
wpa_printf(MSG_DEBUG, "bgscan simple: Start using short "
"bgscan interval");
data->scan_interval = data->short_interval;
- os_get_time(&now);
+ os_get_reltime(&now);
if (now.sec > data->last_bgscan.sec + 1 &&
data->short_scan_count <= data->max_short_scans)
/*
@@ -259,7 +259,7 @@
* Signal dropped further 4 dB. Request a new scan if we have
* not yet scanned in a while.
*/
- os_get_time(&now);
+ os_get_reltime(&now);
if (now.sec > data->last_bgscan.sec + 10)
scan = 1;
}
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 67a9f97..f99a8a7 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -98,6 +98,7 @@
ANQP_DUP(hs20_wan_metrics);
ANQP_DUP(hs20_connection_capability);
ANQP_DUP(hs20_operating_class);
+ ANQP_DUP(hs20_osu_providers_list);
#endif /* CONFIG_HS20 */
#undef ANQP_DUP
@@ -166,6 +167,7 @@
wpabuf_free(anqp->hs20_wan_metrics);
wpabuf_free(anqp->hs20_connection_capability);
wpabuf_free(anqp->hs20_operating_class);
+ wpabuf_free(anqp->hs20_osu_providers_list);
#endif /* CONFIG_HS20 */
os_free(anqp);
@@ -224,9 +226,9 @@
}
-static void calculate_update_time(const struct os_time *fetch_time,
+static void calculate_update_time(const struct os_reltime *fetch_time,
unsigned int age_ms,
- struct os_time *update_time)
+ struct os_reltime *update_time)
{
os_time_t usec;
@@ -243,7 +245,7 @@
static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
- struct os_time *fetch_time)
+ struct os_reltime *fetch_time)
{
dst->flags = src->flags;
os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
@@ -326,7 +328,7 @@
static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
const u8 *ssid, size_t ssid_len,
struct wpa_scan_res *res,
- struct os_time *fetch_time)
+ struct os_reltime *fetch_time)
{
struct wpa_bss *bss;
@@ -492,7 +494,7 @@
static struct wpa_bss *
wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
- struct wpa_scan_res *res, struct os_time *fetch_time)
+ struct wpa_scan_res *res, struct os_reltime *fetch_time)
{
u32 changes;
@@ -502,6 +504,22 @@
wpa_bss_copy_res(bss, res, fetch_time);
/* Move the entry to the end of the list */
dl_list_del(&bss->list);
+#ifdef CONFIG_P2P
+ if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+ !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) {
+ /*
+ * This can happen when non-P2P station interface runs a scan
+ * without P2P IE in the Probe Request frame. P2P GO would reply
+ * to that with a Probe Response that does not include P2P IE.
+ * Do not update the IEs in this BSS entry to avoid such loss of
+ * information that may be needed for P2P operations to
+ * determine group information.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for "
+ MACSTR " since that would remove P2P IE information",
+ MAC2STR(bss->bssid));
+ } else
+#endif /* CONFIG_P2P */
if (bss->ie_len + bss->beacon_ie_len >=
res->ie_len + res->beacon_ie_len) {
os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
@@ -571,17 +589,18 @@
*/
void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res,
- struct os_time *fetch_time)
+ struct os_reltime *fetch_time)
{
const u8 *ssid, *p2p;
struct wpa_bss *bss;
if (wpa_s->conf->ignore_old_scan_res) {
- struct os_time update;
+ struct os_reltime update;
calculate_update_time(fetch_time, res->age, &update);
- if (os_time_before(&update, &wpa_s->scan_trigger_time)) {
- struct os_time age;
- os_time_sub(&wpa_s->scan_trigger_time, &update, &age);
+ if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) {
+ struct os_reltime age;
+ os_reltime_sub(&wpa_s->scan_trigger_time, &update,
+ &age);
wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
"table entry that is %u.%06u seconds older "
"than our scan trigger",
@@ -625,8 +644,18 @@
bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
if (bss == NULL)
bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
- else
+ else {
bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
+ if (wpa_s->last_scan_res) {
+ unsigned int i;
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ if (bss == wpa_s->last_scan_res[i]) {
+ /* Already in the list */
+ return;
+ }
+ }
+ }
+ }
if (bss == NULL)
return;
@@ -645,7 +674,8 @@
wpa_s->last_scan_res_size = siz;
}
- wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
+ if (wpa_s->last_scan_res)
+ wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
}
@@ -705,26 +735,10 @@
{
struct wpa_bss *bss, *n;
- wpa_s->last_scan_full = 0;
- os_get_time(&wpa_s->last_scan);
+ os_get_reltime(&wpa_s->last_scan);
if (!new_scan)
return; /* do not expire entries without new scan */
- if (info && !info->aborted && !info->freqs) {
- size_t i;
- if (info->num_ssids == 0) {
- wpa_s->last_scan_full = 1;
- } else {
- for (i = 0; i < info->num_ssids; i++) {
- if (info->ssids[i].ssid == NULL ||
- info->ssids[i].ssid_len == 0) {
- wpa_s->last_scan_full = 1;
- break;
- }
- }
- }
- }
-
dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
if (wpa_bss_in_use(wpa_s, bss))
continue;
@@ -738,10 +752,8 @@
}
}
- wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u "
- "last_scan_full=%d",
- wpa_s->last_scan_res_used, wpa_s->last_scan_res_size,
- wpa_s->last_scan_full);
+ wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u",
+ wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
}
@@ -755,19 +767,19 @@
void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
{
struct wpa_bss *bss, *n;
- struct os_time t;
+ struct os_reltime t;
if (dl_list_empty(&wpa_s->bss))
return;
- os_get_time(&t);
+ os_get_reltime(&t);
t.sec -= age;
dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
if (wpa_bss_in_use(wpa_s, bss))
continue;
- if (os_time_before(&bss->last_update, &t)) {
+ if (os_reltime_before(&bss->last_update, &t)) {
wpa_bss_remove(wpa_s, bss, __func__);
} else
break;
@@ -811,6 +823,8 @@
{
struct wpa_bss *bss, *n;
+ wpa_s->clear_driver_scan_cache = 1;
+
if (wpa_s->bss.next == NULL)
return; /* BSS table not yet initialized */
@@ -874,7 +888,7 @@
if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
continue;
if (found == NULL ||
- os_time_before(&found->last_update, &bss->last_update))
+ os_reltime_before(&found->last_update, &bss->last_update))
found = bss;
}
return found;
@@ -1002,6 +1016,43 @@
/**
+ * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ *
+ * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
+ u32 vendor_type)
+{
+ const u8 *end, *pos;
+
+ if (bss->beacon_ie_len == 0)
+ return NULL;
+
+ pos = (const u8 *) (bss + 1);
+ pos += bss->ie_len;
+ end = pos + bss->beacon_ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+/**
* wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
* @bss: BSS table entry
* @vendor_type: Vendor type (four octets starting the IE payload)
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 2b41948..4a624c5 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -39,6 +39,7 @@
struct wpabuf *hs20_wan_metrics;
struct wpabuf *hs20_connection_capability;
struct wpabuf *hs20_operating_class;
+ struct wpabuf *hs20_osu_providers_list;
#endif /* CONFIG_HS20 */
};
@@ -84,7 +85,7 @@
/** Timestamp of last Beacon/Probe Response frame */
u64 tsf;
/** Time of the last update (i.e., Beacon or Probe Response RX) */
- struct os_time last_update;
+ struct os_reltime last_update;
/** ANQP data */
struct wpa_bss_anqp *anqp;
/** Length of the following IE field in octets (from Probe Response) */
@@ -98,7 +99,7 @@
void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res,
- struct os_time *fetch_time);
+ struct os_reltime *fetch_time);
void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
int new_scan);
int wpa_bss_init(struct wpa_supplicant *wpa_s);
@@ -118,6 +119,8 @@
unsigned int idf, unsigned int idl);
const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
+const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
+ u32 vendor_type);
struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
u32 vendor_type);
struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
@@ -127,4 +130,9 @@
struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss);
+static inline int bss_is_dmg(const struct wpa_bss *bss)
+{
+ return bss->freq > 45000;
+}
+
#endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 69f920d..8cd4a2f 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "utils/uuid.h"
+#include "utils/ip_addr.h"
#include "crypto/sha1.h"
#include "rsn_supp/wpa.h"
#include "eap_peer/eap.h"
@@ -285,7 +286,7 @@
{
#ifdef CONFIG_EXT_PASSWORD
if (os_strncmp(value, "ext:", 4) == 0) {
- os_free(ssid->passphrase);
+ str_clear_free(ssid->passphrase);
ssid->passphrase = NULL;
ssid->psk_set = 0;
os_free(ssid->ext_psk);
@@ -321,7 +322,7 @@
os_memcmp(ssid->passphrase, value, len) == 0)
return 0;
ssid->psk_set = 0;
- os_free(ssid->passphrase);
+ str_clear_free(ssid->passphrase);
ssid->passphrase = dup_binstr(value, len);
if (ssid->passphrase == NULL)
return -1;
@@ -340,7 +341,7 @@
return -1;
}
- os_free(ssid->passphrase);
+ str_clear_free(ssid->passphrase);
ssid->passphrase = NULL;
ssid->psk_set = 1;
@@ -404,6 +405,8 @@
else if (os_strcmp(start, "RSN") == 0 ||
os_strcmp(start, "WPA2") == 0)
val |= WPA_PROTO_RSN;
+ else if (os_strcmp(start, "OSEN") == 0)
+ val |= WPA_PROTO_OSEN;
else {
wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
line, start);
@@ -432,28 +435,41 @@
static char * wpa_config_write_proto(const struct parse_data *data,
struct wpa_ssid *ssid)
{
- int first = 1, ret;
+ int ret;
char *buf, *pos, *end;
- pos = buf = os_zalloc(10);
+ pos = buf = os_zalloc(20);
if (buf == NULL)
return NULL;
- end = buf + 10;
+ end = buf + 20;
if (ssid->proto & WPA_PROTO_WPA) {
- ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
+ ret = os_snprintf(pos, end - pos, "%sWPA",
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return buf;
pos += ret;
- first = 0;
}
if (ssid->proto & WPA_PROTO_RSN) {
- ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
+ ret = os_snprintf(pos, end - pos, "%sRSN",
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return buf;
pos += ret;
- first = 0;
+ }
+
+ if (ssid->proto & WPA_PROTO_OSEN) {
+ ret = os_snprintf(pos, end - pos, "%sOSEN",
+ pos == buf ? "" : " ");
+ if (ret < 0 || ret >= end - pos)
+ return buf;
+ pos += ret;
+ }
+
+ if (pos == buf) {
+ os_free(buf);
+ buf = NULL;
}
return buf;
@@ -515,6 +531,10 @@
else if (os_strcmp(start, "FT-SAE") == 0)
val |= WPA_KEY_MGMT_FT_SAE;
#endif /* CONFIG_SAE */
+#ifdef CONFIG_HS20
+ else if (os_strcmp(start, "OSEN") == 0)
+ val |= WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
@@ -546,10 +566,10 @@
char *buf, *pos, *end;
int ret;
- pos = buf = os_zalloc(50);
+ pos = buf = os_zalloc(100);
if (buf == NULL)
return NULL;
- end = buf + 50;
+ end = buf + 100;
if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
@@ -602,31 +622,66 @@
}
#ifdef CONFIG_IEEE80211R
- if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK)
- pos += os_snprintf(pos, end - pos, "%sFT-PSK",
- pos == buf ? "" : " ");
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ ret = os_snprintf(pos, end - pos, "%sFT-PSK",
+ pos == buf ? "" : " ");
+ if (ret < 0 || ret >= end - pos) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
- if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
- pos += os_snprintf(pos, end - pos, "%sFT-EAP",
- pos == buf ? "" : " ");
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+ ret = os_snprintf(pos, end - pos, "%sFT-EAP",
+ pos == buf ? "" : " ");
+ if (ret < 0 || ret >= end - pos) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
- if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
- pos += os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
- pos == buf ? "" : " ");
+ if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
+ pos == buf ? "" : " ");
+ if (ret < 0 || ret >= end - pos) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
- if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
- pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
- pos == buf ? "" : " ");
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
+ pos == buf ? "" : " ");
+ if (ret < 0 || ret >= end - pos) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS
- if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
- pos += os_snprintf(pos, end - pos, "%sWPS",
- pos == buf ? "" : " ");
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+ ret = os_snprintf(pos, end - pos, "%sWPS",
+ pos == buf ? "" : " ");
+ if (ret < 0 || ret >= end - pos) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
#endif /* CONFIG_WPS */
+ if (pos == buf) {
+ os_free(buf);
+ buf = NULL;
+ }
+
return buf;
}
#endif /* NO_CONFIG_WRITE */
@@ -818,6 +873,11 @@
pos += ret;
}
+ if (pos == buf) {
+ os_free(buf);
+ buf = NULL;
+ }
+
return buf;
}
#endif /* NO_CONFIG_WRITE */
@@ -873,6 +933,10 @@
freqs = wpa_config_parse_int_array(value);
if (freqs == NULL)
return -1;
+ if (freqs[0] == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
os_free(ssid->scan_freq);
ssid->scan_freq = freqs;
@@ -889,6 +953,10 @@
freqs = wpa_config_parse_int_array(value);
if (freqs == NULL)
return -1;
+ if (freqs[0] == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
os_free(ssid->freq_list);
ssid->freq_list = freqs;
@@ -1062,7 +1130,7 @@
if (os_strcmp(value, "NULL") == 0) {
wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
- os_free(ssid->eap.password);
+ bin_clear_free(ssid->eap.password, ssid->eap.password_len);
ssid->eap.password = NULL;
ssid->eap.password_len = 0;
return 0;
@@ -1073,7 +1141,7 @@
char *name = os_strdup(value + 4);
if (name == NULL)
return -1;
- os_free(ssid->eap.password);
+ bin_clear_free(ssid->eap.password, ssid->eap.password_len);
ssid->eap.password = (u8 *) name;
ssid->eap.password_len = os_strlen(name);
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1095,7 +1163,7 @@
wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
(u8 *) tmp, res_len);
- os_free(ssid->eap.password);
+ bin_clear_free(ssid->eap.password, ssid->eap.password_len);
ssid->eap.password = (u8 *) tmp;
ssid->eap.password_len = res_len;
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1124,7 +1192,7 @@
wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
- os_free(ssid->eap.password);
+ bin_clear_free(ssid->eap.password, ssid->eap.password_len);
ssid->eap.password = hash;
ssid->eap.password_len = 16;
ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1194,7 +1262,7 @@
line, (unsigned int) *len);
}
os_memcpy(key, buf, *len);
- os_free(buf);
+ str_clear_free(buf);
res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
if (res >= 0 && (size_t) res < sizeof(title))
wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
@@ -1283,6 +1351,52 @@
#ifdef CONFIG_P2P
+static int wpa_config_parse_go_p2p_dev_addr(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+ os_strcmp(value, "any") == 0) {
+ os_memset(ssid->go_p2p_dev_addr, 0, ETH_ALEN);
+ wpa_printf(MSG_MSGDUMP, "GO P2P Device Address any");
+ return 0;
+ }
+ if (hwaddr_aton(value, ssid->go_p2p_dev_addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid GO P2P Device Address '%s'.",
+ line, value);
+ return -1;
+ }
+ ssid->bssid_set = 1;
+ wpa_printf(MSG_MSGDUMP, "GO P2P Device Address " MACSTR,
+ MAC2STR(ssid->go_p2p_dev_addr));
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *value;
+ int res;
+
+ if (is_zero_ether_addr(ssid->go_p2p_dev_addr))
+ return NULL;
+
+ value = os_malloc(20);
+ if (value == NULL)
+ return NULL;
+ res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr));
+ if (res < 0 || res >= 20) {
+ os_free(value);
+ return NULL;
+ }
+ value[20 - 1] = '\0';
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
@@ -1541,6 +1655,7 @@
{ STRe(dh_file) },
{ STRe(subject_match) },
{ STRe(altsubject_match) },
+ { STRe(domain_suffix_match) },
{ STRe(ca_cert2) },
{ STRe(ca_path2) },
{ STRe(client_cert2) },
@@ -1549,6 +1664,7 @@
{ STRe(dh_file2) },
{ STRe(subject_match2) },
{ STRe(altsubject_match2) },
+ { STRe(domain_suffix_match2) },
{ STRe(phase1) },
{ STRe(phase2) },
{ STRe(pcsc) },
@@ -1565,6 +1681,7 @@
{ INTe(engine) },
{ INTe(engine2) },
{ INT(eapol_flags) },
+ { INTe(sim_num) },
#endif /* IEEE8021X_EAPOL */
{ FUNC_KEY(wep_key0) },
{ FUNC_KEY(wep_key1) },
@@ -1592,6 +1709,7 @@
{ STR(bgscan) },
{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
#ifdef CONFIG_P2P
+ { FUNC(go_p2p_dev_addr) },
{ FUNC(p2p_client_list) },
{ FUNC(psk_list) },
#endif /* CONFIG_P2P */
@@ -1599,6 +1717,8 @@
{ INT_RANGE(disable_ht, 0, 1) },
{ INT_RANGE(disable_ht40, -1, 1) },
{ INT_RANGE(disable_sgi, 0, 1) },
+ { INT_RANGE(disable_ldpc, 0, 1) },
+ { INT_RANGE(ht40_intolerant, 0, 1) },
{ INT_RANGE(disable_max_amsdu, -1, 1) },
{ INT_RANGE(ampdu_factor, -1, 3) },
{ INT_RANGE(ampdu_density, -1, 7) },
@@ -1628,6 +1748,12 @@
{ INT(ap_max_inactivity) },
{ INT(dtim_period) },
{ INT(beacon_int) },
+#ifdef CONFIG_MACSEC
+ { INT_RANGE(macsec_policy, 0, 1) },
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+ { INT(update_identifier) },
+#endif /* CONFIG_HS20 */
};
#undef OFFSET
@@ -1646,7 +1772,7 @@
#undef _FUNC
#undef FUNC
#undef FUNC_KEY
-#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0]))
+#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields)
/**
@@ -1737,29 +1863,31 @@
static void eap_peer_config_free(struct eap_peer_config *eap)
{
os_free(eap->eap_methods);
- os_free(eap->identity);
+ bin_clear_free(eap->identity, eap->identity_len);
os_free(eap->anonymous_identity);
- os_free(eap->password);
+ bin_clear_free(eap->password, eap->password_len);
os_free(eap->ca_cert);
os_free(eap->ca_path);
os_free(eap->client_cert);
os_free(eap->private_key);
- os_free(eap->private_key_passwd);
+ str_clear_free(eap->private_key_passwd);
os_free(eap->dh_file);
os_free(eap->subject_match);
os_free(eap->altsubject_match);
+ os_free(eap->domain_suffix_match);
os_free(eap->ca_cert2);
os_free(eap->ca_path2);
os_free(eap->client_cert2);
os_free(eap->private_key2);
- os_free(eap->private_key2_passwd);
+ str_clear_free(eap->private_key2_passwd);
os_free(eap->dh_file2);
os_free(eap->subject_match2);
os_free(eap->altsubject_match2);
+ os_free(eap->domain_suffix_match2);
os_free(eap->phase1);
os_free(eap->phase2);
os_free(eap->pcsc);
- os_free(eap->pin);
+ str_clear_free(eap->pin);
os_free(eap->engine_id);
os_free(eap->key_id);
os_free(eap->cert_id);
@@ -1767,12 +1895,13 @@
os_free(eap->key2_id);
os_free(eap->cert2_id);
os_free(eap->ca_cert2_id);
- os_free(eap->pin2);
+ str_clear_free(eap->pin2);
os_free(eap->engine2_id);
os_free(eap->otp);
os_free(eap->pending_req_otp);
os_free(eap->pac_file);
- os_free(eap->new_password);
+ bin_clear_free(eap->new_password, eap->new_password_len);
+ str_clear_free(eap->external_sim_resp);
}
#endif /* IEEE8021X_EAPOL */
@@ -1789,7 +1918,8 @@
struct psk_list_entry *psk;
os_free(ssid->ssid);
- os_free(ssid->passphrase);
+ os_memset(ssid->psk, 0, sizeof(ssid->psk));
+ str_clear_free(ssid->passphrase);
os_free(ssid->ext_psk);
#ifdef IEEE8021X_EAPOL
eap_peer_config_free(&ssid->eap);
@@ -1813,24 +1943,51 @@
void wpa_config_free_cred(struct wpa_cred *cred)
{
+ size_t i;
+
os_free(cred->realm);
- os_free(cred->username);
- os_free(cred->password);
+ str_clear_free(cred->username);
+ str_clear_free(cred->password);
os_free(cred->ca_cert);
os_free(cred->client_cert);
os_free(cred->private_key);
- os_free(cred->private_key_passwd);
+ str_clear_free(cred->private_key_passwd);
os_free(cred->imsi);
- os_free(cred->milenage);
+ str_clear_free(cred->milenage);
+ for (i = 0; i < cred->num_domain; i++)
+ os_free(cred->domain[i]);
os_free(cred->domain);
+ os_free(cred->domain_suffix_match);
os_free(cred->eap_method);
os_free(cred->phase1);
os_free(cred->phase2);
os_free(cred->excluded_ssid);
+ os_free(cred->roaming_partner);
+ os_free(cred->provisioning_sp);
+ for (i = 0; i < cred->num_req_conn_capab; i++)
+ os_free(cred->req_conn_capab_port[i]);
+ os_free(cred->req_conn_capab_port);
+ os_free(cred->req_conn_capab_proto);
os_free(cred);
}
+void wpa_config_flush_blobs(struct wpa_config *config)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ struct wpa_config_blob *blob, *prev;
+
+ blob = config->blobs;
+ config->blobs = NULL;
+ while (blob) {
+ prev = blob;
+ blob = blob->next;
+ wpa_config_free_blob(prev);
+ }
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
/**
* wpa_config_free - Free configuration data
* @config: Configuration data from wpa_config_read()
@@ -1840,9 +1997,6 @@
*/
void wpa_config_free(struct wpa_config *config)
{
-#ifndef CONFIG_NO_CONFIG_BLOBS
- struct wpa_config_blob *blob, *prevblob;
-#endif /* CONFIG_NO_CONFIG_BLOBS */
struct wpa_ssid *ssid, *prev = NULL;
struct wpa_cred *cred, *cprev;
@@ -1860,15 +2014,7 @@
wpa_config_free_cred(cprev);
}
-#ifndef CONFIG_NO_CONFIG_BLOBS
- blob = config->blobs;
- prevblob = NULL;
- while (blob) {
- prevblob = blob;
- blob = blob->next;
- wpa_config_free_blob(prevblob);
- }
-#endif /* CONFIG_NO_CONFIG_BLOBS */
+ wpa_config_flush_blobs(config);
wpabuf_free(config->wps_vendor_ext_m1);
os_free(config->ctrl_interface);
@@ -1877,7 +2023,7 @@
os_free(config->pkcs11_engine_path);
os_free(config->pkcs11_module_path);
os_free(config->pcsc_reader);
- os_free(config->pcsc_pin);
+ str_clear_free(config->pcsc_pin);
os_free(config->driver_param);
os_free(config->device_name);
os_free(config->manufacturer);
@@ -1888,6 +2034,7 @@
os_free(config->p2p_ssid_postfix);
os_free(config->pssid);
os_free(config->p2p_pref_chan);
+ os_free(config->p2p_no_go_freq.range);
os_free(config->autoscan);
os_free(config->freq_list);
wpabuf_free(config->wps_nfc_dh_pubkey);
@@ -1896,6 +2043,8 @@
os_free(config->ext_password_backend);
os_free(config->sae_groups);
wpabuf_free(config->ap_vendor_elements);
+ os_free(config->osu_dir);
+ os_free(config->wowlan_triggers);
os_free(config);
}
@@ -2029,11 +2178,13 @@
ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
+ ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM;
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_HT_OVERRIDES
ssid->disable_ht = DEFAULT_DISABLE_HT;
ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
ssid->disable_sgi = DEFAULT_DISABLE_SGI;
+ ssid->disable_ldpc = DEFAULT_DISABLE_LDPC;
ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
@@ -2256,7 +2407,7 @@
wpa_printf(MSG_DEBUG, "Do not allow "
"key_data field to be "
"exposed");
- os_free(res);
+ str_clear_free(res);
return os_strdup("*");
}
@@ -2291,17 +2442,93 @@
}
+static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
+ const char *value)
+{
+ u8 *proto;
+ int **port;
+ int *ports, *nports;
+ const char *pos;
+ unsigned int num_ports;
+
+ proto = os_realloc_array(cred->req_conn_capab_proto,
+ cred->num_req_conn_capab + 1, sizeof(u8));
+ if (proto == NULL)
+ return -1;
+ cred->req_conn_capab_proto = proto;
+
+ port = os_realloc_array(cred->req_conn_capab_port,
+ cred->num_req_conn_capab + 1, sizeof(int *));
+ if (port == NULL)
+ return -1;
+ cred->req_conn_capab_port = port;
+
+ proto[cred->num_req_conn_capab] = atoi(value);
+
+ pos = os_strchr(value, ':');
+ if (pos == NULL) {
+ port[cred->num_req_conn_capab] = NULL;
+ cred->num_req_conn_capab++;
+ return 0;
+ }
+ pos++;
+
+ ports = NULL;
+ num_ports = 0;
+
+ while (*pos) {
+ nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+ if (nports == NULL) {
+ os_free(ports);
+ return -1;
+ }
+ ports = nports;
+ ports[num_ports++] = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+ if (nports == NULL) {
+ os_free(ports);
+ return -1;
+ }
+ ports = nports;
+ ports[num_ports] = -1;
+
+ port[cred->num_req_conn_capab] = ports;
+ cred->num_req_conn_capab++;
+ return 0;
+}
+
+
int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
const char *value, int line)
{
char *val;
size_t len;
+ if (os_strcmp(var, "temporary") == 0) {
+ cred->temporary = atoi(value);
+ return 0;
+ }
+
if (os_strcmp(var, "priority") == 0) {
cred->priority = atoi(value);
return 0;
}
+ if (os_strcmp(var, "sp_priority") == 0) {
+ int prio = atoi(value);
+ if (prio < 0 || prio > 255)
+ return -1;
+ cred->sp_priority = prio;
+ return 0;
+ }
+
if (os_strcmp(var, "pcsc") == 0) {
cred->pcsc = atoi(value);
return 0;
@@ -2326,12 +2553,55 @@
if (os_strcmp(var, "password") == 0 &&
os_strncmp(value, "ext:", 4) == 0) {
- os_free(cred->password);
+ str_clear_free(cred->password);
cred->password = os_strdup(value);
cred->ext_password = 1;
return 0;
}
+ if (os_strcmp(var, "update_identifier") == 0) {
+ cred->update_identifier = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_dl_bandwidth_home") == 0) {
+ cred->min_dl_bandwidth_home = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_ul_bandwidth_home") == 0) {
+ cred->min_ul_bandwidth_home = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) {
+ cred->min_dl_bandwidth_roaming = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) {
+ cred->min_ul_bandwidth_roaming = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "max_bss_load") == 0) {
+ cred->max_bss_load = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "req_conn_capab") == 0)
+ return wpa_config_set_cred_req_conn_capab(cred, value);
+
+ if (os_strcmp(var, "ocsp") == 0) {
+ cred->ocsp = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "sim_num") == 0) {
+ cred->sim_num = atoi(value);
+ return 0;
+ }
+
val = wpa_config_parse_string(value, &len);
if (val == NULL) {
wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
@@ -2346,13 +2616,13 @@
}
if (os_strcmp(var, "username") == 0) {
- os_free(cred->username);
+ str_clear_free(cred->username);
cred->username = val;
return 0;
}
if (os_strcmp(var, "password") == 0) {
- os_free(cred->password);
+ str_clear_free(cred->password);
cred->password = val;
cred->ext_password = 0;
return 0;
@@ -2377,7 +2647,7 @@
}
if (os_strcmp(var, "private_key_passwd") == 0) {
- os_free(cred->private_key_passwd);
+ str_clear_free(cred->private_key_passwd);
cred->private_key_passwd = val;
return 0;
}
@@ -2389,14 +2659,28 @@
}
if (os_strcmp(var, "milenage") == 0) {
- os_free(cred->milenage);
+ str_clear_free(cred->milenage);
cred->milenage = val;
return 0;
}
+ if (os_strcmp(var, "domain_suffix_match") == 0) {
+ os_free(cred->domain_suffix_match);
+ cred->domain_suffix_match = val;
+ return 0;
+ }
+
if (os_strcmp(var, "domain") == 0) {
- os_free(cred->domain);
- cred->domain = val;
+ char **new_domain;
+ new_domain = os_realloc_array(cred->domain,
+ cred->num_domain + 1,
+ sizeof(char *));
+ if (new_domain == NULL) {
+ os_free(val);
+ return -1;
+ }
+ new_domain[cred->num_domain++] = val;
+ cred->domain = new_domain;
return 0;
}
@@ -2426,6 +2710,21 @@
return 0;
}
+ if (os_strcmp(var, "required_roaming_consortium") == 0) {
+ if (len < 3 || len > sizeof(cred->required_roaming_consortium))
+ {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "required_roaming_consortium length %d "
+ "(3..15 expected)", line, (int) len);
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(cred->required_roaming_consortium, val, len);
+ cred->required_roaming_consortium_len = len;
+ os_free(val);
+ return 0;
+ }
+
if (os_strcmp(var, "excluded_ssid") == 0) {
struct excluded_ssid *e;
@@ -2454,6 +2753,69 @@
return 0;
}
+ if (os_strcmp(var, "roaming_partner") == 0) {
+ struct roaming_partner *p;
+ char *pos;
+
+ p = os_realloc_array(cred->roaming_partner,
+ cred->num_roaming_partner + 1,
+ sizeof(struct roaming_partner));
+ if (p == NULL) {
+ os_free(val);
+ return -1;
+ }
+ cred->roaming_partner = p;
+
+ p = &cred->roaming_partner[cred->num_roaming_partner];
+
+ pos = os_strchr(val, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+ if (pos - val - 1 >= (int) sizeof(p->fqdn)) {
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(p->fqdn, val, pos - val);
+
+ p->exact_match = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+
+ p->priority = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+
+ if (os_strlen(pos) >= sizeof(p->country)) {
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(p->country, pos, os_strlen(pos) + 1);
+
+ cred->num_roaming_partner++;
+ os_free(val);
+
+ return 0;
+ }
+
+ if (os_strcmp(var, "provisioning_sp") == 0) {
+ os_free(cred->provisioning_sp);
+ cred->provisioning_sp = val;
+ return 0;
+ }
+
if (line) {
wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
line, var);
@@ -2465,6 +2827,275 @@
}
+static char * alloc_int_str(int val)
+{
+ char *buf;
+
+ buf = os_malloc(20);
+ if (buf == NULL)
+ return NULL;
+ os_snprintf(buf, 20, "%d", val);
+ return buf;
+}
+
+
+static char * alloc_strdup(const char *str)
+{
+ if (str == NULL)
+ return NULL;
+ return os_strdup(str);
+}
+
+
+char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
+{
+ if (os_strcmp(var, "temporary") == 0)
+ return alloc_int_str(cred->temporary);
+
+ if (os_strcmp(var, "priority") == 0)
+ return alloc_int_str(cred->priority);
+
+ if (os_strcmp(var, "sp_priority") == 0)
+ return alloc_int_str(cred->sp_priority);
+
+ if (os_strcmp(var, "pcsc") == 0)
+ return alloc_int_str(cred->pcsc);
+
+ if (os_strcmp(var, "eap") == 0) {
+ if (!cred->eap_method)
+ return NULL;
+ return alloc_strdup(eap_get_name(cred->eap_method[0].vendor,
+ cred->eap_method[0].method));
+ }
+
+ if (os_strcmp(var, "update_identifier") == 0)
+ return alloc_int_str(cred->update_identifier);
+
+ if (os_strcmp(var, "min_dl_bandwidth_home") == 0)
+ return alloc_int_str(cred->min_dl_bandwidth_home);
+
+ if (os_strcmp(var, "min_ul_bandwidth_home") == 0)
+ return alloc_int_str(cred->min_ul_bandwidth_home);
+
+ if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0)
+ return alloc_int_str(cred->min_dl_bandwidth_roaming);
+
+ if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0)
+ return alloc_int_str(cred->min_ul_bandwidth_roaming);
+
+ if (os_strcmp(var, "max_bss_load") == 0)
+ return alloc_int_str(cred->max_bss_load);
+
+ if (os_strcmp(var, "req_conn_capab") == 0) {
+ unsigned int i;
+ char *buf, *end, *pos;
+ int ret;
+
+ if (!cred->num_req_conn_capab)
+ return NULL;
+
+ buf = os_malloc(4000);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = pos + 4000;
+ for (i = 0; i < cred->num_req_conn_capab; i++) {
+ int *ports;
+
+ ret = os_snprintf(pos, end - pos, "%s%u",
+ i > 0 ? "\n" : "",
+ cred->req_conn_capab_proto[i]);
+ if (ret < 0 || ret >= end - pos)
+ return buf;
+ pos += ret;
+
+ ports = cred->req_conn_capab_port[i];
+ if (ports) {
+ int j;
+ for (j = 0; ports[j] != -1; j++) {
+ ret = os_snprintf(pos, end - pos,
+ "%s%d",
+ j > 0 ? "," : ":",
+ ports[j]);
+ if (ret < 0 || ret >= end - pos)
+ return buf;
+ pos += ret;
+ }
+ }
+ }
+
+ return buf;
+ }
+
+ if (os_strcmp(var, "ocsp") == 0)
+ return alloc_int_str(cred->ocsp);
+
+ if (os_strcmp(var, "realm") == 0)
+ return alloc_strdup(cred->realm);
+
+ if (os_strcmp(var, "username") == 0)
+ return alloc_strdup(cred->username);
+
+ if (os_strcmp(var, "password") == 0) {
+ if (!cred->password)
+ return NULL;
+ return alloc_strdup("*");
+ }
+
+ if (os_strcmp(var, "ca_cert") == 0)
+ return alloc_strdup(cred->ca_cert);
+
+ if (os_strcmp(var, "client_cert") == 0)
+ return alloc_strdup(cred->client_cert);
+
+ if (os_strcmp(var, "private_key") == 0)
+ return alloc_strdup(cred->private_key);
+
+ if (os_strcmp(var, "private_key_passwd") == 0) {
+ if (!cred->private_key_passwd)
+ return NULL;
+ return alloc_strdup("*");
+ }
+
+ if (os_strcmp(var, "imsi") == 0)
+ return alloc_strdup(cred->imsi);
+
+ if (os_strcmp(var, "milenage") == 0) {
+ if (!(cred->milenage))
+ return NULL;
+ return alloc_strdup("*");
+ }
+
+ if (os_strcmp(var, "domain_suffix_match") == 0)
+ return alloc_strdup(cred->domain_suffix_match);
+
+ if (os_strcmp(var, "domain") == 0) {
+ unsigned int i;
+ char *buf, *end, *pos;
+ int ret;
+
+ if (!cred->num_domain)
+ return NULL;
+
+ buf = os_malloc(4000);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = pos + 4000;
+
+ for (i = 0; i < cred->num_domain; i++) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ i > 0 ? "\n" : "", cred->domain[i]);
+ if (ret < 0 || ret >= end - pos)
+ return buf;
+ pos += ret;
+ }
+
+ return buf;
+ }
+
+ if (os_strcmp(var, "phase1") == 0)
+ return alloc_strdup(cred->phase1);
+
+ if (os_strcmp(var, "phase2") == 0)
+ return alloc_strdup(cred->phase2);
+
+ if (os_strcmp(var, "roaming_consortium") == 0) {
+ size_t buflen;
+ char *buf;
+
+ if (!cred->roaming_consortium_len)
+ return NULL;
+ buflen = cred->roaming_consortium_len * 2 + 1;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return NULL;
+ wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
+ cred->roaming_consortium_len);
+ return buf;
+ }
+
+ if (os_strcmp(var, "required_roaming_consortium") == 0) {
+ size_t buflen;
+ char *buf;
+
+ if (!cred->required_roaming_consortium_len)
+ return NULL;
+ buflen = cred->required_roaming_consortium_len * 2 + 1;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return NULL;
+ wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
+ cred->required_roaming_consortium_len);
+ return buf;
+ }
+
+ if (os_strcmp(var, "excluded_ssid") == 0) {
+ unsigned int i;
+ char *buf, *end, *pos;
+
+ if (!cred->num_excluded_ssid)
+ return NULL;
+
+ buf = os_malloc(4000);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = pos + 4000;
+
+ for (i = 0; i < cred->num_excluded_ssid; i++) {
+ struct excluded_ssid *e;
+ int ret;
+
+ e = &cred->excluded_ssid[i];
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ i > 0 ? "\n" : "",
+ wpa_ssid_txt(e->ssid, e->ssid_len));
+ if (ret < 0 || ret >= end - pos)
+ return buf;
+ pos += ret;
+ }
+
+ return buf;
+ }
+
+ if (os_strcmp(var, "roaming_partner") == 0) {
+ unsigned int i;
+ char *buf, *end, *pos;
+
+ if (!cred->num_roaming_partner)
+ return NULL;
+
+ buf = os_malloc(4000);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = pos + 4000;
+
+ for (i = 0; i < cred->num_roaming_partner; i++) {
+ struct roaming_partner *p;
+ int ret;
+
+ p = &cred->roaming_partner[i];
+ ret = os_snprintf(pos, end - pos, "%s%s,%d,%u,%s",
+ i > 0 ? "\n" : "",
+ p->fqdn, p->exact_match, p->priority,
+ p->country);
+ if (ret < 0 || ret >= end - pos)
+ return buf;
+ pos += ret;
+ }
+
+ return buf;
+ }
+
+ if (os_strcmp(var, "provisioning_sp") == 0)
+ return alloc_strdup(cred->provisioning_sp);
+
+ return NULL;
+}
+
+
struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
{
struct wpa_cred *cred;
@@ -2499,6 +3130,7 @@
if (cred == NULL)
return NULL;
cred->id = id;
+ cred->sim_num = DEFAULT_USER_SELECTED_SIM;
if (last)
last->next = cred;
else
@@ -2579,7 +3211,7 @@
{
if (blob) {
os_free(blob->name);
- os_free(blob->data);
+ bin_clear_free(blob->data, blob->len);
os_free(blob);
}
}
@@ -2643,6 +3275,7 @@
config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
+ config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
@@ -2653,6 +3286,7 @@
config->wmm_ac_params[1] = ac_bk;
config->wmm_ac_params[2] = ac_vi;
config->wmm_ac_params[3] = ac_vo;
+ config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
if (ctrl_interface)
config->ctrl_interface = os_strdup(ctrl_interface);
@@ -2770,6 +3404,27 @@
}
+static int wpa_config_process_bgscan(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ size_t len;
+ char *tmp;
+ int res;
+
+ tmp = wpa_config_parse_string(pos, &len);
+ if (tmp == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to parse %s",
+ line, data->name);
+ return -1;
+ }
+
+ res = wpa_global_config_parse_str(data, config, line, tmp);
+ os_free(tmp);
+ return res;
+}
+
+
static int wpa_global_config_parse_bin(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
@@ -2808,12 +3463,39 @@
freqs = wpa_config_parse_int_array(value);
if (freqs == NULL)
return -1;
+ if (freqs[0] == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
os_free(config->freq_list);
config->freq_list = freqs;
return 0;
}
+#ifdef CONFIG_P2P
+static int wpa_global_config_parse_ipv4(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ u32 *dst;
+ struct hostapd_ip_addr addr;
+
+ if (hostapd_parse_ip_addr(pos, &addr) < 0)
+ return -1;
+ if (addr.af != AF_INET)
+ return -1;
+
+ dst = (u32 *) (((u8 *) config) + (long) data->param1);
+ os_memcpy(dst, &addr.u.v4.s_addr, 4);
+ wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
+ WPA_GET_BE32((u8 *) dst));
+
+ return 0;
+}
+#endif /* CONFIG_P2P */
+
+
static int wpa_config_process_country(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
@@ -2998,6 +3680,26 @@
wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
return -1;
}
+
+
+static int wpa_config_process_p2p_no_go_freq(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ int ret;
+
+ ret = freq_range_list_parse(&config->p2p_no_go_freq, pos);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items",
+ config->p2p_no_go_freq.num);
+
+ return 0;
+}
+
#endif /* CONFIG_P2P */
@@ -3070,6 +3772,19 @@
}
+#ifdef CONFIG_CTRL_IFACE
+static int wpa_config_process_no_ctrl_interface(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ wpa_printf(MSG_DEBUG, "no_ctrl_interface -> ctrl_interface=NULL");
+ os_free(config->ctrl_interface);
+ config->ctrl_interface = NULL;
+ return 0;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
#ifdef OFFSET
#undef OFFSET
#endif /* OFFSET */
@@ -3085,14 +3800,21 @@
#define STR(f) _STR(f), NULL, NULL
#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, OFFSET(f), NULL, NULL
static const struct global_parse_data global_fields[] = {
#ifdef CONFIG_CTRL_IFACE
{ STR(ctrl_interface), 0 },
+ { FUNC_NO_VAR(no_ctrl_interface), 0 },
{ STR(ctrl_interface_group), 0 } /* deprecated */,
#endif /* CONFIG_CTRL_IFACE */
+#ifdef CONFIG_MACSEC
+ { INT_RANGE(eapol_version, 1, 3), 0 },
+#else /* CONFIG_MACSEC */
{ INT_RANGE(eapol_version, 1, 2), 0 },
+#endif /* CONFIG_MACSEC */
{ INT(ap_scan), 0 },
+ { FUNC(bgscan), 0 },
{ INT(disable_scan_offload), 0 },
{ INT(fast_reauth), 0 },
{ STR(opensc_engine_path), 0 },
@@ -3100,6 +3822,7 @@
{ STR(pkcs11_module_path), 0 },
{ STR(pcsc_reader), 0 },
{ STR(pcsc_pin), 0 },
+ { INT(external_sim), 0 },
{ STR(driver_param), 0 },
{ INT(dot11RSNAConfigPMKLifetime), 0 },
{ INT(dot11RSNAConfigPMKReauthThreshold), 0 },
@@ -3125,18 +3848,28 @@
{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
{ INT(p2p_listen_reg_class), 0 },
{ INT(p2p_listen_channel), 0 },
- { INT(p2p_oper_reg_class), 0 },
- { INT(p2p_oper_channel), 0 },
+ { INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL },
+ { INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL },
{ INT_RANGE(p2p_go_intent, 0, 15), 0 },
{ STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
{ INT(p2p_group_idle), 0 },
+ { INT_RANGE(p2p_passphrase_len, 8, 63),
+ CFG_CHANGED_P2P_PASSPHRASE_LEN },
{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
+ { FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
+ { INT_RANGE(p2p_add_cli_chan, 0, 1), 0 },
+ { INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 },
{ INT(p2p_go_ht40), 0 },
+ { INT(p2p_go_vht), 0 },
{ INT(p2p_disabled), 0 },
{ INT(p2p_no_group_iface), 0 },
{ INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 },
+ { IPV4(ip_addr_go), 0 },
+ { IPV4(ip_addr_mask), 0 },
+ { IPV4(ip_addr_start), 0 },
+ { IPV4(ip_addr_end), 0 },
#endif /* CONFIG_P2P */
{ FUNC(country), CFG_CHANGED_COUNTRY },
{ INT(bss_max_count), 0 },
@@ -3173,6 +3906,9 @@
{ INT(scan_cur_freq), 0 },
{ INT(sched_scan_interval), 0 },
{ INT(tdls_external_control), 0},
+ { STR(osu_dir), 0 },
+ { STR(wowlan_triggers), 0 },
+ { INT(p2p_search_delay), 0},
};
#undef FUNC
@@ -3183,7 +3919,8 @@
#undef STR
#undef STR_RANGE
#undef BIN
-#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
+#undef IPV4
+#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 3fe46e3..52add9d 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -19,12 +19,14 @@
#define DEFAULT_P2P_GO_INTENT 7
#define DEFAULT_P2P_INTRA_BSS 1
#define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60)
+#define DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN 0
#define DEFAULT_BSS_MAX_COUNT 200
#define DEFAULT_BSS_EXPIRATION_AGE 180
#define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
#define DEFAULT_MAX_NUM_STA 128
#define DEFAULT_ACCESS_NETWORK_TYPE 15
#define DEFAULT_SCAN_CUR_FREQ 0
+#define DEFAULT_P2P_SEARCH_DELAY 500
#include "config_ssid.h"
#include "wps/wps.h"
@@ -52,6 +54,11 @@
int id;
/**
+ * temporary - Whether this credential is temporary and not to be saved
+ */
+ int temporary;
+
+ /**
* priority - Priority group
*
* By default, all networks and credentials get the same priority group
@@ -150,12 +157,37 @@
char *milenage;
/**
- * domain - Home service provider FQDN
+ * domain_suffix_match - Constraint for server domain name
+ *
+ * If set, this FQDN is used as a suffix match requirement for the AAA
+ * 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;
+
+ /**
+ * domain - Home service provider FQDN(s)
*
* This is used to compare against the Domain Name List to figure out
- * whether the AP is operated by the Home SP.
+ * whether the AP is operated by the Home SP. Multiple domain entries
+ * can be used to configure alternative FQDNs that will be considered
+ * home networks.
*/
- char *domain;
+ char **domain;
+
+ /**
+ * num_domain - Number of FQDNs in the domain array
+ */
+ size_t num_domain;
/**
* roaming_consortium - Roaming Consortium OI
@@ -175,6 +207,9 @@
*/
size_t roaming_consortium_len;
+ u8 required_roaming_consortium[15];
+ size_t required_roaming_consortium_len;
+
/**
* eap_method - EAP method to use
*
@@ -203,6 +238,66 @@
size_t ssid_len;
} *excluded_ssid;
size_t num_excluded_ssid;
+
+ struct roaming_partner {
+ char fqdn[128];
+ int exact_match;
+ u8 priority;
+ char country[3];
+ } *roaming_partner;
+ size_t num_roaming_partner;
+
+ int update_identifier;
+
+ /**
+ * provisioning_sp - FQDN of the SP that provisioned the credential
+ */
+ char *provisioning_sp;
+
+ /**
+ * sp_priority - Credential priority within a provisioning SP
+ *
+ * This is the priority of the credential among all credentials
+ * provisionined by the same SP (i.e., for entries that have identical
+ * provisioning_sp value). The range of this priority is 0-255 with 0
+ * being the highest and 255 the lower priority.
+ */
+ int sp_priority;
+
+ unsigned int min_dl_bandwidth_home;
+ unsigned int min_ul_bandwidth_home;
+ unsigned int min_dl_bandwidth_roaming;
+ unsigned int min_ul_bandwidth_roaming;
+
+ /**
+ * max_bss_load - Maximum BSS Load Channel Utilization (1..255)
+ * This value is used as the maximum channel utilization for network
+ * selection purposes for home networks. If the AP does not advertise
+ * BSS Load or if the limit would prevent any connection, this
+ * constraint will be ignored.
+ */
+ unsigned int max_bss_load;
+
+ unsigned int num_req_conn_capab;
+ u8 *req_conn_capab_proto;
+ int **req_conn_capab_port;
+
+ /**
+ * 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;
+
+ /**
+ * sim_num - User selected SIM identifier
+ *
+ * This variable is used for identifying which SIM is used if the system
+ * has more than one.
+ */
+ int sim_num;
};
@@ -222,6 +317,7 @@
#define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
#define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
#define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
+#define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
/**
* struct wpa_config - wpa_supplicant configuration data
@@ -301,6 +397,18 @@
int ap_scan;
/**
+ * bgscan - Background scan and roaming parameters or %NULL if none
+ *
+ * This is an optional set of parameters for background scanning and
+ * roaming within a network (ESS). For more detailed information see
+ * ssid block documentation.
+ *
+ * The variable defines default bgscan behavior for all BSS station
+ * networks except for those which have their own bgscan configuration.
+ */
+ char *bgscan;
+
+ /**
* disable_scan_offload - Disable automatic offloading of scan requests
*
* By default, %wpa_supplicant tries to offload scanning if the driver
@@ -425,6 +533,11 @@
char *pcsc_pin;
/**
+ * external_sim - Use external processing for SIM/USIM operations
+ */
+ int external_sim;
+
+ /**
* driver_param - Driver interface parameters
*
* This text string is passed to the selected driver interface with the
@@ -572,7 +685,10 @@
int p2p_intra_bss;
unsigned int num_p2p_pref_chan;
struct p2p_channel *p2p_pref_chan;
+ struct wpa_freq_range_list p2p_no_go_freq;
+ int p2p_add_cli_chan;
int p2p_ignore_shared_freq;
+ int p2p_optimize_listen_chan;
struct wpabuf *wps_vendor_ext_m1;
@@ -601,6 +717,14 @@
int p2p_group_idle;
/**
+ * p2p_passphrase_len - Passphrase length (8..63) for P2P GO
+ *
+ * This parameter controls the length of the random passphrase that is
+ * generated at the GO.
+ */
+ unsigned int p2p_passphrase_len;
+
+ /**
* bss_max_count - Maximum number of BSS entries to keep in memory
*/
unsigned int bss_max_count;
@@ -793,6 +917,16 @@
int p2p_go_ht40;
/**
+ * p2p_go_vht - Default mode for VHT enable when operating as GO
+ *
+ * This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
+ * Note that regulatory constraints and driver capabilities are
+ * consulted anyway, so setting it to 1 can't do real harm.
+ * By default: 0 (disabled)
+ */
+ int p2p_go_vht;
+
+ /**
* p2p_disabled - Whether P2P operations are disabled for this interface
*/
int p2p_disabled;
@@ -884,9 +1018,39 @@
* to specify the TDLS link to get established to the driver. The
* driver requests the TDLS setup to the supplicant only for the
* specified TDLS peers.
- *
*/
int tdls_external_control;
+
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+
+ /**
+ * osu_dir - OSU provider information directory
+ *
+ * If set, allow FETCH_OSU control interface command to be used to fetch
+ * OSU provider information into all APs and store the results in this
+ * directory.
+ */
+ char *osu_dir;
+
+ /**
+ * wowlan_triggers - Wake-on-WLAN triggers
+ *
+ * If set, these wowlan triggers will be configured.
+ */
+ char *wowlan_triggers;
+
+ /**
+ * p2p_search_delay - Extra delay between concurrent search iterations
+ *
+ * Add extra delay (in milliseconds) between search iterations when
+ * there is a concurrent operation to make p2p_find friendlier to
+ * concurrent operations by avoiding it from taking 100% of radio
+ * resources.
+ */
+ unsigned int p2p_search_delay;
};
@@ -918,6 +1082,7 @@
struct wpa_config_blob *blob);
void wpa_config_free_blob(struct wpa_config_blob *blob);
int wpa_config_remove_blob(struct wpa_config *config, const char *name);
+void wpa_config_flush_blobs(struct wpa_config *config);
struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id);
struct wpa_cred * wpa_config_add_cred(struct wpa_config *config);
@@ -925,6 +1090,7 @@
void wpa_config_free_cred(struct wpa_cred *cred);
int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
const char *value, int line);
+char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var);
struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
const char *driver_param);
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index bb0e536..73ad57a 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -219,6 +219,7 @@
if (cred == NULL)
return NULL;
cred->id = id;
+ cred->sim_num = DEFAULT_USER_SELECTED_SIM;
while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
if (os_strcmp(pos, "}") == 0) {
@@ -351,8 +352,8 @@
FILE *f;
char buf[512], *pos;
int errors = 0, line = 0;
- struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
- struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL;
+ struct wpa_ssid *ssid, *tail, *head;
+ struct wpa_cred *cred, *cred_tail, *cred_head;
struct wpa_config *config;
int id = 0;
int cred_id = 0;
@@ -368,8 +369,12 @@
"structure");
return NULL;
}
- head = config->ssid;
- cred_head = config->cred;
+ tail = head = config->ssid;
+ while (tail && tail->next)
+ tail = tail->next;
+ cred_tail = cred_head = config->cred;
+ while (cred_tail && cred_tail->next)
+ cred_tail = cred_tail->next;
wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
f = fopen(name, "r");
@@ -606,6 +611,15 @@
#ifdef CONFIG_P2P
+static void write_go_p2p_dev_addr(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value = wpa_config_get(ssid, "go_p2p_dev_addr");
+ if (value == NULL)
+ return;
+ fprintf(f, "\tgo_p2p_dev_addr=%s\n", value);
+ os_free(value);
+}
+
static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
{
char *value = wpa_config_get(ssid, "p2p_client_list");
@@ -653,6 +667,7 @@
write_auth_alg(f, ssid);
STR(bgscan);
STR(autoscan);
+ STR(scan_freq);
#ifdef IEEE8021X_EAPOL
write_eap(f, ssid);
STR(identity);
@@ -666,6 +681,7 @@
STR(dh_file);
STR(subject_match);
STR(altsubject_match);
+ STR(domain_suffix_match);
STR(ca_cert2);
STR(ca_path2);
STR(client_cert2);
@@ -674,6 +690,7 @@
STR(dh_file2);
STR(subject_match2);
STR(altsubject_match2);
+ STR(domain_suffix_match2);
STR(phase1);
STR(phase2);
STR(pcsc);
@@ -699,6 +716,8 @@
INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
STR(pac_file);
INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
+ INTe(ocsp);
+ INT_DEFe(sim_num, DEFAULT_USER_SELECTED_SIM);
#endif /* IEEE8021X_EAPOL */
INT(mode);
INT(frequency);
@@ -711,11 +730,18 @@
#endif /* CONFIG_IEEE80211W */
STR(id_str);
#ifdef CONFIG_P2P
+ write_go_p2p_dev_addr(f, ssid);
write_p2p_client_list(f, ssid);
write_psk_list(f, ssid);
#endif /* CONFIG_P2P */
INT(dtim_period);
INT(beacon_int);
+#ifdef CONFIG_MACSEC
+ INT(macsec_policy);
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+ INT(update_identifier);
+#endif /* CONFIG_HS20 */
#undef STR
#undef INT
@@ -725,6 +751,8 @@
static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
{
+ size_t i;
+
if (cred->priority)
fprintf(f, "\tpriority=%d\n", cred->priority);
if (cred->pcsc)
@@ -750,10 +778,12 @@
fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
if (cred->milenage)
fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
- if (cred->domain)
- fprintf(f, "\tdomain=\"%s\"\n", cred->domain);
+ for (i = 0; i < cred->num_domain; i++)
+ fprintf(f, "\tdomain=\"%s\"\n", cred->domain[i]);
+ if (cred->domain_suffix_match)
+ fprintf(f, "\tdomain_suffix_match=\"%s\"\n",
+ cred->domain_suffix_match);
if (cred->roaming_consortium_len) {
- size_t i;
fprintf(f, "\troaming_consortium=");
for (i = 0; i < cred->roaming_consortium_len; i++)
fprintf(f, "%02x", cred->roaming_consortium[i]);
@@ -763,14 +793,15 @@
const char *name;
name = eap_get_name(cred->eap_method[0].vendor,
cred->eap_method[0].method);
- fprintf(f, "\teap=%s\n", name);
+ if (name)
+ fprintf(f, "\teap=%s\n", name);
}
if (cred->phase1)
fprintf(f, "\tphase1=\"%s\"\n", cred->phase1);
if (cred->phase2)
fprintf(f, "\tphase2=\"%s\"\n", cred->phase2);
if (cred->excluded_ssid) {
- size_t i, j;
+ size_t j;
for (i = 0; i < cred->num_excluded_ssid; i++) {
struct excluded_ssid *e = &cred->excluded_ssid[i];
fprintf(f, "\texcluded_ssid=");
@@ -779,6 +810,70 @@
fprintf(f, "\n");
}
}
+ if (cred->roaming_partner) {
+ for (i = 0; i < cred->num_roaming_partner; i++) {
+ struct roaming_partner *p = &cred->roaming_partner[i];
+ fprintf(f, "\troaming_partner=\"%s,%d,%u,%s\"\n",
+ p->fqdn, p->exact_match, p->priority,
+ p->country);
+ }
+ }
+ if (cred->update_identifier)
+ fprintf(f, "\tupdate_identifier=%d\n", cred->update_identifier);
+
+ if (cred->provisioning_sp)
+ fprintf(f, "\tprovisioning_sp=\"%s\"\n", cred->provisioning_sp);
+ if (cred->sp_priority)
+ fprintf(f, "\tsp_priority=%d\n", cred->sp_priority);
+
+ if (cred->min_dl_bandwidth_home)
+ fprintf(f, "\tmin_dl_bandwidth_home=%u\n",
+ cred->min_dl_bandwidth_home);
+ if (cred->min_ul_bandwidth_home)
+ fprintf(f, "\tmin_ul_bandwidth_home=%u\n",
+ cred->min_ul_bandwidth_home);
+ if (cred->min_dl_bandwidth_roaming)
+ fprintf(f, "\tmin_dl_bandwidth_roaming=%u\n",
+ cred->min_dl_bandwidth_roaming);
+ if (cred->min_ul_bandwidth_roaming)
+ fprintf(f, "\tmin_ul_bandwidth_roaming=%u\n",
+ cred->min_ul_bandwidth_roaming);
+
+ if (cred->max_bss_load)
+ fprintf(f, "\tmax_bss_load=%u\n",
+ cred->max_bss_load);
+
+ if (cred->ocsp)
+ fprintf(f, "\tocsp=%d\n", cred->ocsp);
+
+ if (cred->num_req_conn_capab) {
+ for (i = 0; i < cred->num_req_conn_capab; i++) {
+ int *ports;
+
+ fprintf(f, "\treq_conn_capab=%u",
+ cred->req_conn_capab_proto[i]);
+ ports = cred->req_conn_capab_port[i];
+ if (ports) {
+ int j;
+ for (j = 0; ports[j] != -1; j++) {
+ fprintf(f, "%s%d", j > 0 ? "," : ":",
+ ports[j]);
+ }
+ }
+ fprintf(f, "\n");
+ }
+ }
+
+ if (cred->required_roaming_consortium_len) {
+ fprintf(f, "\trequired_roaming_consortium=");
+ for (i = 0; i < cred->required_roaming_consortium_len; i++)
+ fprintf(f, "%02x",
+ cred->required_roaming_consortium[i]);
+ fprintf(f, "\n");
+ }
+
+ if (cred->sim_num != DEFAULT_USER_SELECTED_SIM)
+ fprintf(f, "\tsim_num=%d\n", cred->sim_num);
}
@@ -924,6 +1019,9 @@
fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
if (config->p2p_group_idle)
fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+ if (config->p2p_passphrase_len)
+ fprintf(f, "p2p_passphrase_len=%u\n",
+ config->p2p_passphrase_len);
if (config->p2p_pref_chan) {
unsigned int i;
fprintf(f, "p2p_pref_chan=");
@@ -934,8 +1032,23 @@
}
fprintf(f, "\n");
}
+ if (config->p2p_no_go_freq.num) {
+ char *val = freq_range_list_str(&config->p2p_no_go_freq);
+ if (val) {
+ fprintf(f, "p2p_no_go_freq=%s\n", val);
+ os_free(val);
+ }
+ }
+ if (config->p2p_add_cli_chan)
+ fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan);
+ if (config->p2p_optimize_listen_chan !=
+ DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN)
+ fprintf(f, "p2p_optimize_listen_chan=%d\n",
+ config->p2p_optimize_listen_chan);
if (config->p2p_go_ht40)
fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
+ if (config->p2p_go_vht)
+ fprintf(f, "p2p_go_vht=%u\n", config->p2p_go_vht);
if (config->p2p_disabled)
fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled);
if (config->p2p_no_group_iface)
@@ -1049,9 +1162,23 @@
fprintf(f, "sched_scan_interval=%u\n",
config->sched_scan_interval);
+ if (config->external_sim)
+ fprintf(f, "external_sim=%d\n", config->external_sim);
+
if (config->tdls_external_control)
- fprintf(f, "tdls_external_control=%u\n",
+ fprintf(f, "tdls_external_control=%d\n",
config->tdls_external_control);
+
+ if (config->wowlan_triggers)
+ fprintf(f, "wowlan_triggers=%s\n",
+ config->wowlan_triggers);
+
+ if (config->bgscan)
+ fprintf(f, "bgscan=\"%s\"\n", config->bgscan);
+
+ if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY)
+ fprintf(f, "p2p_search_delay=%u\n",
+ config->p2p_search_delay);
}
#endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1079,6 +1206,8 @@
wpa_config_write_global(f, config);
for (cred = config->cred; cred; cred = cred->next) {
+ if (cred->temporary)
+ continue;
fprintf(f, "\ncred={\n");
wpa_config_write_cred(f, cred);
fprintf(f, "}\n");
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index c6ea963..ab474ff 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -30,9 +30,11 @@
#define DEFAULT_DISABLE_HT 0
#define DEFAULT_DISABLE_HT40 0
#define DEFAULT_DISABLE_SGI 0
+#define DEFAULT_DISABLE_LDPC 0
#define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
#define DEFAULT_AMPDU_FACTOR -1 /* no change */
#define DEFAULT_AMPDU_DENSITY -1 /* no change */
+#define DEFAULT_USER_SELECTED_SIM 1
struct psk_list_entry {
struct dl_list list;
@@ -131,6 +133,11 @@
int bssid_set;
/**
+ * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set
+ */
+ u8 go_p2p_dev_addr[ETH_ALEN];
+
+ /**
* psk - WPA pre-shared key (256 bits)
*/
u8 psk[32];
@@ -310,12 +317,13 @@
* 4 = P2P Group Formation (used internally; not in configuration
* files)
*
- * Note: IBSS can only be used with key_mgmt NONE (plaintext and
- * static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In
- * addition, ap_scan has to be set to 2 for IBSS. WPA-None requires
- * following network block options: proto=WPA, key_mgmt=WPA-NONE,
- * pairwise=NONE, group=TKIP (or CCMP, but not both), and psk must also
- * be set (either directly or using ASCII passphrase).
+ * Note: IBSS can only be used with key_mgmt NONE (plaintext and static
+ * WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE
+ * (fixed group key TKIP/CCMP) is available for backwards compatibility,
+ * but its use is deprecated. WPA-None requires following network block
+ * options: proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or
+ * CCMP, but not both), and psk must also be set (either directly or
+ * using ASCII passphrase).
*/
enum wpas_mode {
WPAS_MODE_INFRA = 0,
@@ -394,6 +402,8 @@
int ht40;
+ int vht;
+
/**
* wpa_ptk_rekey - Maximum lifetime for PTK in seconds
*
@@ -491,13 +501,6 @@
*/
int export_keys;
-#ifdef ANDROID_P2P
- /**
- * assoc_retry - Number of times association should be retried.
- */
- int assoc_retry;
-#endif
-
#ifdef CONFIG_HT_OVERRIDES
/**
* disable_ht - Disable HT (IEEE 802.11n) for this network
@@ -524,6 +527,19 @@
int disable_sgi;
/**
+ * disable_ldpc - Disable LDPC for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_ldpc;
+
+ /**
+ * ht40_intolerant - Indicate 40 MHz intolerant for this network
+ */
+ int ht40_intolerant;
+
+ /**
* disable_max_amsdu - Disable MAX A-MSDU
*
* A-MDSU will be 3839 bytes when disabled, or 7935
@@ -610,7 +626,7 @@
/**
* disabled_until - Network block disabled until this time if non-zero
*/
- struct os_time disabled_until;
+ struct os_reltime disabled_until;
/**
* parent_cred - Pointer to parent wpa_cred entry
@@ -620,6 +636,21 @@
* dereferences since it may not be updated in all cases.
*/
void *parent_cred;
+
+#ifdef CONFIG_MACSEC
+ /**
+ * macsec_policy - Determines the policy for MACsec secure session
+ *
+ * 0: MACsec not in use (default)
+ * 1: MACsec enabled - Should secure, accept key server's advice to
+ * determine whether to use a secure session or not.
+ */
+ int macsec_policy;
+#endif /* CONFIG_MACSEC */
+
+#ifdef CONFIG_HS20
+ int update_identifier;
+#endif /* CONFIG_HS20 */
};
#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 1b5e237..199f04f 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -623,6 +623,9 @@
wpa_config_write_reg_dword(hk, TEXT("okc"), config->okc, 0);
wpa_config_write_reg_dword(hk, TEXT("pmf"), config->pmf, 0);
+ wpa_config_write_reg_dword(hk, TEXT("external_sim"),
+ config->external_sim, 0);
+
return 0;
}
@@ -927,6 +930,9 @@
MGMT_FRAME_PROTECTION_DEFAULT);
#endif /* CONFIG_IEEE80211W */
STR(id_str);
+#ifdef CONFIG_HS20
+ INT(update_identifier);
+#endif /* CONFIG_HS20 */
#undef STR
#undef INT
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 6335605..79806d9 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +10,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
+#include "utils/uuid.h"
#include "common/version.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
@@ -39,104 +40,14 @@
#include "blacklist.h"
#include "autoscan.h"
#include "wnm_sta.h"
-
-extern struct wpa_driver_ops *wpa_drivers[];
+#include "offchannel.h"
static int wpa_supplicant_global_iface_list(struct wpa_global *global,
char *buf, int len);
static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
char *buf, int len);
-
-
-static int pno_start(struct wpa_supplicant *wpa_s)
-{
- int ret;
- size_t i, num_ssid;
- struct wpa_ssid *ssid;
- struct wpa_driver_scan_params params;
-
- if (wpa_s->pno)
- return 0;
-
- if ((wpa_s->wpa_state > WPA_SCANNING) &&
- (wpa_s->wpa_state <= WPA_COMPLETED)) {
- wpa_printf(MSG_ERROR, "PNO: In assoc process");
- return -EAGAIN;
- }
-
- if (wpa_s->wpa_state == WPA_SCANNING) {
- wpa_supplicant_cancel_sched_scan(wpa_s);
- wpa_supplicant_cancel_scan(wpa_s);
- }
-
- os_memset(¶ms, 0, sizeof(params));
-
- num_ssid = 0;
- ssid = wpa_s->conf->ssid;
- while (ssid) {
- if (!wpas_network_disabled(wpa_s, ssid))
- num_ssid++;
- ssid = ssid->next;
- }
- if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
- wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
- "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
- num_ssid = WPAS_MAX_SCAN_SSIDS;
- }
-
- if (num_ssid == 0) {
- wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
- return -1;
- }
-
- params.filter_ssids = os_malloc(sizeof(struct wpa_driver_scan_filter) *
- num_ssid);
- if (params.filter_ssids == NULL)
- return -1;
- i = 0;
- ssid = wpa_s->conf->ssid;
- while (ssid) {
- if (!wpas_network_disabled(wpa_s, ssid)) {
- params.ssids[i].ssid = ssid->ssid;
- params.ssids[i].ssid_len = ssid->ssid_len;
- params.num_ssids++;
- os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
- ssid->ssid_len);
- params.filter_ssids[i].ssid_len = ssid->ssid_len;
- params.num_filter_ssids++;
- i++;
- if (i == num_ssid)
- break;
- }
- ssid = ssid->next;
- }
-
- if (wpa_s->conf->filter_rssi)
- params.filter_rssi = wpa_s->conf->filter_rssi;
-
- ret = wpa_drv_sched_scan(wpa_s, ¶ms, 10 * 1000);
- os_free(params.filter_ssids);
- if (ret == 0)
- wpa_s->pno = 1;
- return ret;
-}
-
-
-static int pno_stop(struct wpa_supplicant *wpa_s)
-{
- int ret = 0;
-
- if (wpa_s->pno) {
- wpa_s->pno = 0;
- ret = wpa_drv_stop_sched_scan(wpa_s);
- }
-
- if (wpa_s->wpa_state == WPA_SCANNING)
- wpa_supplicant_req_scan(wpa_s, 0, 0);
-
- return ret;
-}
-
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
+ char *val);
static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
{
@@ -184,7 +95,7 @@
struct wpa_ssid *c;
/*
- * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | “”
+ * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | ""
* SSID_SPEC ::= ssid <SSID_HEX>
* BSSID_SPEC ::= bssid <BSSID_HEX>
*/
@@ -297,6 +208,72 @@
}
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos)
+{
+ char *name = pos;
+ struct wpa_config_blob *blob;
+ size_t len;
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name);
+ blob = os_zalloc(sizeof(*blob));
+ if (blob == NULL)
+ return -1;
+ blob->name = os_strdup(name);
+ blob->data = os_malloc(len / 2);
+ if (blob->name == NULL || blob->data == NULL) {
+ wpa_config_free_blob(blob);
+ return -1;
+ }
+
+ if (hexstr2bin(pos, blob->data, len / 2) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data");
+ wpa_config_free_blob(blob);
+ return -1;
+ }
+ blob->len = len / 2;
+
+ wpa_config_set_blob(wpa_s->conf, blob);
+
+ return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *params;
+ char *pos;
+ int *freqs = NULL;
+ int ret;
+
+ if (atoi(cmd)) {
+ params = os_strchr(cmd, ' ');
+ os_free(wpa_s->manual_sched_scan_freqs);
+ if (params) {
+ params++;
+ pos = os_strstr(params, "freq=");
+ if (pos)
+ freqs = freq_range_to_channel_list(wpa_s,
+ pos + 5);
+ }
+ wpa_s->manual_sched_scan_freqs = freqs;
+ ret = wpas_start_pno(wpa_s);
+ } else {
+ ret = wpas_stop_pno(wpa_s);
+ }
+ return ret;
+}
+
+
static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
char *cmd)
{
@@ -354,17 +331,21 @@
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 */
} else if (os_strcasecmp(cmd, "ampdu") == 0) {
if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
ret = -1;
+#ifdef CONFIG_TDLS
#ifdef CONFIG_TDLS_TESTING
} else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
extern unsigned int tdls_testing;
tdls_testing = strtol(value, NULL, 0);
wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
#endif /* CONFIG_TDLS_TESTING */
-#ifdef CONFIG_TDLS
} else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
int disabled = atoi(value);
wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
@@ -376,10 +357,7 @@
wpa_tdls_enable(wpa_s->wpa, !disabled);
#endif /* CONFIG_TDLS */
} else if (os_strcasecmp(cmd, "pno") == 0) {
- if (atoi(value))
- ret = pno_start(wpa_s);
- else
- ret = pno_stop(wpa_s);
+ ret = wpas_ctrl_pno(wpa_s, value);
} else if (os_strcasecmp(cmd, "radio_disabled") == 0) {
int disabled = atoi(value);
if (wpa_drv_radio_disable(wpa_s, disabled) < 0)
@@ -426,7 +404,11 @@
ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
#ifdef CONFIG_WIFI_DISPLAY
} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
- wifi_display_enable(wpa_s->global, !!atoi(value));
+ int enabled = !!atoi(value);
+ if (enabled && !wpa_s->global->p2p)
+ ret = -1;
+ else
+ wifi_display_enable(wpa_s->global, enabled);
#endif /* CONFIG_WIFI_DISPLAY */
} else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
ret = set_bssid_filter(wpa_s, value);
@@ -434,6 +416,14 @@
ret = set_disallow_aps(wpa_s, value);
} else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
wpa_s->no_keep_alive = !!atoi(value);
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
+ wpa_s->ext_mgmt_frame_handling = !!atoi(value);
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ } else if (os_strcmp(cmd, "blob") == 0) {
+ ret = wpas_ctrl_set_blob(wpa_s, value);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
} else {
value[-1] = '=';
ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -461,8 +451,13 @@
wpa_s->conf->country[1]);
#ifdef CONFIG_WIFI_DISPLAY
} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
- res = os_snprintf(buf, buflen, "%d",
- wpa_s->global->wifi_display);
+ int enabled;
+ if (wpa_s->global->p2p == NULL ||
+ wpa_s->global->p2p_disabled)
+ enabled = 0;
+ else
+ enabled = wpa_s->global->wifi_display;
+ res = os_snprintf(buf, buflen, "%d", enabled);
if (res < 0 || (unsigned int) res >= buflen)
return -1;
return res;
@@ -569,9 +564,8 @@
MAC2STR(peer));
if ((wpa_s->conf->tdls_external_control) &&
- wpa_tdls_is_external_setup(wpa_s->wpa)) {
+ wpa_tdls_is_external_setup(wpa_s->wpa))
return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
- }
wpa_tdls_remove(wpa_s->wpa, peer);
@@ -590,6 +584,13 @@
u8 peer[ETH_ALEN];
int ret;
+ if (os_strcmp(addr, "*") == 0) {
+ /* remove everyone */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *");
+ wpa_tdls_teardown_peers(wpa_s->wpa);
+ return 0;
+ }
+
if (hwaddr_aton(addr, peer)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid "
"address '%s'", addr);
@@ -600,9 +601,8 @@
MAC2STR(peer));
if ((wpa_s->conf->tdls_external_control) &&
- wpa_tdls_is_external_setup(wpa_s->wpa)) {
+ wpa_tdls_is_external_setup(wpa_s->wpa))
return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
- }
if (wpa_tdls_is_external_setup(wpa_s->wpa))
ret = wpa_tdls_teardown_link(
@@ -614,6 +614,22 @@
return ret;
}
+
+static int ctrl_iface_get_capability_tdls(
+ struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+ int ret;
+
+ ret = os_snprintf(buf, buflen, "%s\n",
+ wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ?
+ (wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ?
+ "EXTERNAL" : "INTERNAL") : "UNSUPPORTED");
+ if (ret < 0 || (size_t) ret > buflen)
+ return -1;
+ return ret;
+}
+
#endif /* CONFIG_TDLS */
@@ -656,7 +672,7 @@
u8 *_p2p_dev_addr = NULL;
#endif /* CONFIG_AP */
- if (cmd == NULL || os_strcmp(cmd, "any") == 0 || cmd[0] == '\0') {
+ if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
_bssid = NULL;
#ifdef CONFIG_P2P
} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
@@ -805,7 +821,8 @@
else if (hwaddr_aton(cmd, bssid))
return -1;
- return wpas_wps_start_nfc(wpa_s, _bssid);
+ return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
+ 0, 0);
}
@@ -877,6 +894,15 @@
size_t len;
struct wpabuf *buf;
int ret;
+ char *freq;
+ int forced_freq = 0;
+
+ freq = strstr(pos, " freq=");
+ if (freq) {
+ *freq = '\0';
+ freq += 6;
+ forced_freq = atoi(freq);
+ }
len = os_strlen(pos);
if (len & 0x01)
@@ -891,7 +917,7 @@
return -1;
}
- ret = wpas_wps_nfc_tag_read(wpa_s, buf);
+ ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
wpabuf_free(buf);
return ret;
@@ -900,12 +926,12 @@
static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
char *reply, size_t max_len,
- int cr)
+ int ndef)
{
struct wpabuf *buf;
int res;
- buf = wpas_wps_nfc_handover_req(wpa_s, cr);
+ buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
if (buf == NULL)
return -1;
@@ -920,25 +946,65 @@
}
+#ifdef CONFIG_P2P
+static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s,
+ char *reply, size_t max_len,
+ int ndef)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = wpas_p2p_nfc_handover_req(wpa_s, ndef);
+ if (buf == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request");
+ 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;
+}
+#endif /* CONFIG_P2P */
+
+
static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
char *cmd, char *reply,
size_t max_len)
{
char *pos;
+ int ndef;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
- if (os_strcmp(cmd, "NDEF") != 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") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+ if (!ndef)
+ return -1;
return wpas_ctrl_nfc_get_handover_req_wps(
- wpa_s, reply, max_len, os_strcmp(pos, "WPS-CR") == 0);
+ wpa_s, reply, max_len, ndef);
}
+#ifdef CONFIG_P2P
+ if (os_strcmp(pos, "P2P-CR") == 0) {
+ return wpas_ctrl_nfc_get_handover_req_p2p(
+ wpa_s, reply, max_len, ndef);
+ }
+#endif /* CONFIG_P2P */
+
return -1;
}
@@ -965,6 +1031,30 @@
}
+#ifdef CONFIG_P2P
+static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s,
+ char *reply, size_t max_len,
+ int ndef, int tag)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag);
+ 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;
+}
+#endif /* CONFIG_P2P */
+
+
static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
char *cmd, char *reply,
size_t max_len)
@@ -988,70 +1078,29 @@
if (pos2)
*pos2++ = '\0';
if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+ if (!ndef)
+ return -1;
return wpas_ctrl_nfc_get_handover_sel_wps(
wpa_s, reply, max_len, ndef,
os_strcmp(pos, "WPS-CR") == 0, pos2);
}
+#ifdef CONFIG_P2P
+ if (os_strcmp(pos, "P2P-CR") == 0) {
+ return wpas_ctrl_nfc_get_handover_sel_p2p(
+ wpa_s, reply, max_len, ndef, 0);
+ }
+
+ if (os_strcmp(pos, "P2P-CR-TAG") == 0) {
+ return wpas_ctrl_nfc_get_handover_sel_p2p(
+ wpa_s, reply, max_len, ndef, 1);
+ }
+#endif /* CONFIG_P2P */
+
return -1;
}
-static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
- char *cmd, char *reply,
- size_t max_len)
-{
- size_t len;
- struct wpabuf *buf;
- int ret;
-
- len = os_strlen(cmd);
- if (len & 0x01)
- return -1;
- len /= 2;
-
- buf = wpabuf_alloc(len);
- if (buf == NULL)
- return -1;
- if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
- wpabuf_free(buf);
- return -1;
- }
-
- ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf);
- wpabuf_free(buf);
-
- return ret;
-}
-
-
-static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
- char *cmd)
-{
- size_t len;
- struct wpabuf *buf;
- int ret;
-
- len = os_strlen(cmd);
- if (len & 0x01)
- return -1;
- len /= 2;
-
- buf = wpabuf_alloc(len);
- if (buf == NULL)
- return -1;
- if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
- wpabuf_free(buf);
- return -1;
- }
-
- ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf);
- wpabuf_free(buf);
-
- return ret;
-}
-
-
static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
char *cmd)
{
@@ -1059,39 +1108,62 @@
struct wpabuf *req, *sel;
int ret;
char *pos, *role, *type, *pos2;
+#ifdef CONFIG_P2P
+ char *freq;
+ int forced_freq = 0;
+
+ freq = strstr(cmd, " freq=");
+ if (freq) {
+ *freq = '\0';
+ freq += 6;
+ forced_freq = atoi(freq);
+ }
+#endif /* CONFIG_P2P */
role = cmd;
pos = os_strchr(role, ' ');
- if (pos == NULL)
+ if (pos == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
return -1;
+ }
*pos++ = '\0';
type = pos;
pos = os_strchr(type, ' ');
- if (pos == NULL)
+ if (pos == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report");
return -1;
+ }
*pos++ = '\0';
pos2 = os_strchr(pos, ' ');
- if (pos2 == NULL)
+ if (pos2 == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
return -1;
+ }
*pos2++ = '\0';
len = os_strlen(pos);
- if (len & 0x01)
+ if (len & 0x01) {
+ wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report");
return -1;
+ }
len /= 2;
req = wpabuf_alloc(len);
- if (req == NULL)
+ if (req == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message");
return -1;
+ }
if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+ wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report");
wpabuf_free(req);
return -1;
}
len = os_strlen(pos2);
if (len & 0x01) {
+ wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report");
wpabuf_free(req);
return -1;
}
@@ -1099,17 +1171,38 @@
sel = wpabuf_alloc(len);
if (sel == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message");
wpabuf_free(req);
return -1;
}
if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+ wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report");
wpabuf_free(req);
wpabuf_free(sel);
return -1;
}
+ wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d",
+ role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel));
+
if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
+#ifdef CONFIG_AP
+ } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0)
+ {
+ ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
+ if (ret < 0)
+ ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+ } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
+ {
+ ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0);
+ } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
+ {
+ ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel,
+ forced_freq);
+#endif /* CONFIG_P2P */
} else {
wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
"reported: role=%s type=%s", role, type);
@@ -1118,6 +1211,9 @@
wpabuf_free(req);
wpabuf_free(sel);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
+
return ret;
}
@@ -1426,7 +1522,12 @@
{
char *pos, *end, tmp[30];
int res, verbose, wps, ret;
+#ifdef CONFIG_HS20
+ const u8 *hs20;
+#endif /* CONFIG_HS20 */
+ if (os_strcmp(params, "-DRIVER") == 0)
+ return wpa_drv_status(wpa_s, buf, buflen);
verbose = os_strcmp(params, "-VERBOSE") == 0;
wps = os_strcmp(params, "-WPS") == 0;
pos = buf;
@@ -1562,10 +1663,16 @@
#ifdef CONFIG_HS20
if (wpa_s->current_bss &&
- wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) &&
+ (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+ HS20_IE_VENDOR_TYPE)) &&
wpa_s->wpa_proto == WPA_PROTO_RSN &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
- ret = os_snprintf(pos, end - pos, "hs20=1\n");
+ int release = 1;
+ if (hs20[1] >= 5) {
+ u8 rel_num = (hs20[6] & 0xf0) >> 4;
+ release = rel_num + 1;
+ }
+ ret = os_snprintf(pos, end - pos, "hs20=%d\n", release);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
@@ -1576,17 +1683,43 @@
char *type;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ size_t i;
+
if (wpa_s->current_ssid->parent_cred != cred)
continue;
- if (!cred->domain)
- continue;
+ if (cred->provisioning_sp) {
+ ret = os_snprintf(pos, end - pos,
+ "provisioning_sp=%s\n",
+ cred->provisioning_sp);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (!cred->domain)
+ goto no_domain;
+
+ i = 0;
+ if (wpa_s->current_bss && wpa_s->current_bss->anqp) {
+ struct wpabuf *names =
+ wpa_s->current_bss->anqp->domain_name;
+ for (i = 0; names && i < cred->num_domain; i++)
+ {
+ if (domain_name_list_contains(
+ names, cred->domain[i], 1))
+ break;
+ }
+ if (i == cred->num_domain)
+ i = 0; /* show first entry by default */
+ }
ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
- cred->domain);
+ cred->domain[i]);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
+ no_domain:
if (wpa_s->current_bss == NULL ||
wpa_s->current_bss->anqp == NULL)
res = -1;
@@ -1623,22 +1756,41 @@
if (res >= 0)
pos += res;
+#ifdef CONFIG_WPS
+ {
+ char uuid_str[100];
+ uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str));
+ ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_WPS */
+
#ifdef ANDROID
- wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
- "id=%d state=%d BSSID=" MACSTR " SSID=%s",
- wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
- wpa_s->wpa_state,
- MAC2STR(wpa_s->bssid),
- wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
- wpa_ssid_txt(wpa_s->current_ssid->ssid,
- wpa_s->current_ssid->ssid_len) : "");
- if (wpa_s->wpa_state == WPA_COMPLETED) {
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- connection to "
- MACSTR " completed %s [id=%d id_str=%s]",
- MAC2STR(wpa_s->bssid), "(auth)",
- ssid ? ssid->id : -1,
- ssid && ssid->id_str ? ssid->id_str : "");
+ /*
+ * Allow using the STATUS command with default behavior, say for debug,
+ * i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE
+ * events with STATUS-NO_EVENTS.
+ */
+ if (os_strcmp(params, "-NO_EVENTS")) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
+ "id=%d state=%d BSSID=" MACSTR " SSID=%s",
+ wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
+ wpa_s->wpa_state,
+ MAC2STR(wpa_s->bssid),
+ wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
+ wpa_ssid_txt(wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len) : "");
+ if (wpa_s->wpa_state == WPA_COMPLETED) {
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
+ "- connection to " MACSTR
+ " completed %s [id=%d id_str=%s]",
+ MAC2STR(wpa_s->bssid), "(auth)",
+ ssid ? ssid->id : -1,
+ ssid && ssid->id_str ? ssid->id_str : "");
+ }
}
#endif /* ANDROID */
@@ -1723,19 +1875,16 @@
* skipped when processing scan results.
*/
ret = wpa_blacklist_add(wpa_s, bssid);
- if (ret != 0)
+ if (ret < 0)
return -1;
ret = wpa_blacklist_add(wpa_s, bssid);
- if (ret != 0)
+ if (ret < 0)
return -1;
os_memcpy(buf, "OK\n", 3);
return 3;
}
-extern int wpa_debug_level;
-extern int wpa_debug_timestamp;
-
static const char * debug_level_str(int level)
{
switch (level) {
@@ -1900,7 +2049,8 @@
const u8 *ie, size_t ie_len)
{
struct wpa_ie_data data;
- int first, ret;
+ char *start;
+ int ret;
ret = os_snprintf(pos, end - pos, "[%s-", proto);
if (ret < 0 || ret >= end - pos)
@@ -1915,62 +2065,58 @@
return pos;
}
- first = 1;
+ start = pos;
if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
- ret = os_snprintf(pos, end - pos, "%sEAP", first ? "" : "+");
+ ret = os_snprintf(pos, end - pos, "%sEAP",
+ pos == start ? "" : "+");
if (ret < 0 || ret >= end - pos)
return pos;
pos += ret;
- first = 0;
}
if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
- ret = os_snprintf(pos, end - pos, "%sPSK", first ? "" : "+");
+ ret = os_snprintf(pos, end - pos, "%sPSK",
+ pos == start ? "" : "+");
if (ret < 0 || ret >= end - pos)
return pos;
pos += ret;
- first = 0;
}
if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
- ret = os_snprintf(pos, end - pos, "%sNone", first ? "" : "+");
+ ret = os_snprintf(pos, end - pos, "%sNone",
+ pos == start ? "" : "+");
if (ret < 0 || ret >= end - pos)
return pos;
pos += ret;
- first = 0;
}
#ifdef CONFIG_IEEE80211R
if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
ret = os_snprintf(pos, end - pos, "%sFT/EAP",
- first ? "" : "+");
+ pos == start ? "" : "+");
if (ret < 0 || ret >= end - pos)
return pos;
pos += ret;
- first = 0;
}
if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
ret = os_snprintf(pos, end - pos, "%sFT/PSK",
- first ? "" : "+");
+ pos == start ? "" : "+");
if (ret < 0 || ret >= end - pos)
return pos;
pos += ret;
- first = 0;
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
- first ? "" : "+");
+ pos == start ? "" : "+");
if (ret < 0 || ret >= end - pos)
return pos;
pos += ret;
- first = 0;
}
if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
- first ? "" : "+");
+ pos == start ? "" : "+");
if (ret < 0 || ret >= end - pos)
return pos;
pos += ret;
- first = 0;
}
#endif /* CONFIG_IEEE80211W */
@@ -2004,10 +2150,8 @@
return pos;
if (wps_is_selected_pbc_registrar(wps_ie))
txt = "[WPS-PBC]";
-#ifdef CONFIG_WPS2
else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
txt = "[WPS-AUTH]";
-#endif /* CONFIG_WPS2 */
else if (wps_is_selected_pin_registrar(wps_ie))
txt = "[WPS-PIN]";
else
@@ -2046,6 +2190,8 @@
const u8 *ie, *ie2, *p2p;
p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+ if (!p2p)
+ p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE);
if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN &&
os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) ==
0)
@@ -2072,17 +2218,43 @@
return -1;
pos += ret;
}
- if (bss->caps & IEEE80211_CAP_IBSS) {
- ret = os_snprintf(pos, end - pos, "[IBSS]");
+ if (bss_is_dmg(bss)) {
+ const char *s;
+ ret = os_snprintf(pos, end - pos, "[DMG]");
if (ret < 0 || ret >= end - pos)
return -1;
pos += ret;
- }
- if (bss->caps & IEEE80211_CAP_ESS) {
- ret = os_snprintf(pos, end - pos, "[ESS]");
+ switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
+ case IEEE80211_CAP_DMG_IBSS:
+ s = "[IBSS]";
+ break;
+ case IEEE80211_CAP_DMG_AP:
+ s = "[ESS]";
+ break;
+ case IEEE80211_CAP_DMG_PBSS:
+ s = "[PBSS]";
+ break;
+ default:
+ s = "";
+ break;
+ }
+ ret = os_snprintf(pos, end - pos, "%s", s);
if (ret < 0 || ret >= end - pos)
return -1;
pos += ret;
+ } else {
+ if (bss->caps & IEEE80211_CAP_IBSS) {
+ ret = os_snprintf(pos, end - pos, "[IBSS]");
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+ }
+ if (bss->caps & IEEE80211_CAP_ESS) {
+ ret = os_snprintf(pos, end - pos, "[ESS]");
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+ }
}
if (p2p) {
ret = os_snprintf(pos, end - pos, "[P2P]");
@@ -2146,9 +2318,10 @@
{
int id;
struct wpa_ssid *ssid;
+ char *pos;
/* cmd: "<network id>" or "any" */
- if (os_strcmp(cmd, "any") == 0) {
+ if (os_strncmp(cmd, "any", 3) == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
ssid = NULL;
} else {
@@ -2168,6 +2341,16 @@
}
}
+ pos = os_strstr(cmd, " freq=");
+ if (pos) {
+ int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
+ if (freqs) {
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = freqs;
+ }
+ }
+
wpa_supplicant_select_network(wpa_s, ssid);
return 0;
@@ -2352,6 +2535,39 @@
}
+static int wpa_supplicant_ctrl_iface_update_network(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ char *name, char *value)
+{
+ if (wpa_config_set(ssid, name, value, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
+ "variable '%s'", name);
+ return -1;
+ }
+
+ if (os_strcmp(name, "bssid") != 0 &&
+ os_strcmp(name, "priority") != 0)
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+
+ if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
+ /*
+ * Invalidate the EAP session cache if anything in the current
+ * or previously used configuration changes.
+ */
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ }
+
+ if ((os_strcmp(name, "psk") == 0 &&
+ value[0] == '"' && ssid->ssid_len) ||
+ (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
+ wpa_config_update_psk(ssid);
+ else if (os_strcmp(name, "priority") == 0)
+ wpa_config_update_prio_list(wpa_s->conf);
+
+ return 0;
+}
+
+
static int wpa_supplicant_ctrl_iface_set_network(
struct wpa_supplicant *wpa_s, char *cmd)
{
@@ -2383,32 +2599,8 @@
return -1;
}
- if (wpa_config_set(ssid, name, value, 0) < 0) {
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
- "variable '%s'", name);
- return -1;
- }
-
- if (os_strcmp(name, "bssid") != 0 &&
- os_strcmp(name, "priority") != 0)
- wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-
- if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
- /*
- * Invalidate the EAP session cache if anything in the current
- * or previously used configuration changes.
- */
- eapol_sm_invalidate_cached_session(wpa_s->eapol);
- }
-
- if ((os_strcmp(name, "psk") == 0 &&
- value[0] == '"' && ssid->ssid_len) ||
- (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
- wpa_config_update_psk(ssid);
- else if (os_strcmp(name, "priority") == 0)
- wpa_config_update_prio_list(wpa_s->conf);
-
- return 0;
+ return wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name,
+ value);
}
@@ -2456,6 +2648,59 @@
}
+static int wpa_supplicant_ctrl_iface_dup_network(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ struct wpa_ssid *ssid_s, *ssid_d;
+ char *name, *id, *value;
+ int id_s, id_d, ret;
+
+ /* cmd: "<src network id> <dst network id> <variable name>" */
+ id = os_strchr(cmd, ' ');
+ if (id == NULL)
+ return -1;
+ *id++ = '\0';
+
+ name = os_strchr(id, ' ');
+ if (name == NULL)
+ return -1;
+ *name++ = '\0';
+
+ id_s = atoi(cmd);
+ id_d = atoi(id);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'",
+ id_s, id_d, name);
+
+ ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
+ if (ssid_s == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "network id=%d", id_s);
+ return -1;
+ }
+
+ ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
+ if (ssid_d == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "network id=%d", id_s);
+ return -1;
+ }
+
+ value = wpa_config_get(ssid_s, name);
+ if (value == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
+ "variable '%s'", name);
+ return -1;
+ }
+
+ ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name,
+ value);
+
+ os_free(value);
+
+ return ret;
+}
+
+
static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
@@ -2476,7 +2721,7 @@
ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n",
cred->id, cred->realm ? cred->realm : "",
cred->username ? cred->username : "",
- cred->domain ? cred->domain : "",
+ cred->domain ? cred->domain[0] : "",
cred->imsi ? cred->imsi : "");
if (ret < 0 || ret >= end - pos)
return pos - buf;
@@ -2501,6 +2746,8 @@
if (cred == NULL)
return -1;
+ wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id);
+
ret = os_snprintf(buf, buflen, "%d\n", cred->id);
if (ret < 0 || (size_t) ret >= buflen)
return -1;
@@ -2513,12 +2760,21 @@
{
struct wpa_ssid *ssid;
char str[20];
+ int id;
- if (cred == NULL || wpa_config_remove_cred(wpa_s->conf, cred->id) < 0) {
+ if (cred == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
return -1;
}
+ id = cred->id;
+ if (wpa_config_remove_cred(wpa_s->conf, id) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id);
+
/* Remove any network entry created based on the removed credential */
ssid = wpa_s->conf->ssid;
while (ssid) {
@@ -2542,7 +2798,8 @@
int id;
struct wpa_cred *cred, *prev;
- /* cmd: "<cred id>", "all", or "sp_fqdn=<FQDN>" */
+ /* cmd: "<cred id>", "all", "sp_fqdn=<FQDN>", or
+ * "provisioning_sp=<FQDN> */
if (os_strcmp(cmd, "all") == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
cred = wpa_s->conf->cred;
@@ -2561,8 +2818,29 @@
while (cred) {
prev = cred;
cred = cred->next;
- if (prev->domain &&
- os_strcmp(prev->domain, cmd + 8) == 0)
+ if (prev->domain) {
+ size_t i;
+ for (i = 0; i < prev->num_domain; i++) {
+ if (os_strcmp(prev->domain[i], cmd + 8)
+ != 0)
+ continue;
+ wpas_ctrl_remove_cred(wpa_s, prev);
+ break;
+ }
+ }
+ }
+ return 0;
+ }
+
+ if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'",
+ cmd + 16);
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ prev = cred;
+ cred = cred->next;
+ if (prev->provisioning_sp &&
+ os_strcmp(prev->provisioning_sp, cmd + 16) == 0)
wpas_ctrl_remove_cred(wpa_s, prev);
}
return 0;
@@ -2613,10 +2891,57 @@
return -1;
}
+ wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name);
+
return 0;
}
+static int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ int id;
+ size_t res;
+ struct wpa_cred *cred;
+ char *name, *value;
+
+ /* cmd: "<cred id> <variable name>" */
+ name = os_strchr(cmd, ' ');
+ if (name == NULL)
+ return -1;
+ *name++ = '\0';
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'",
+ id, name);
+
+ cred = wpa_config_get_cred(wpa_s->conf, id);
+ if (cred == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
+ id);
+ return -1;
+ }
+
+ value = wpa_config_get_cred_no_key(cred, name);
+ if (value == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'",
+ name);
+ return -1;
+ }
+
+ res = os_strlcpy(buf, value, buflen);
+ if (res >= buflen) {
+ os_free(value);
+ return -1;
+ }
+
+ os_free(value);
+
+ return res;
+}
+
+
#ifndef CONFIG_NO_CONFIG_WRITE
static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
{
@@ -2642,13 +2967,32 @@
#endif /* CONFIG_NO_CONFIG_WRITE */
+struct cipher_info {
+ unsigned int capa;
+ const char *name;
+ int group_only;
+};
+
+static const struct cipher_info ciphers[] = {
+ { WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 },
+ { WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 },
+ { WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 },
+ { WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 },
+ { WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 },
+ { WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 },
+ { WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 },
+ { WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 }
+};
+
+
static int ctrl_iface_get_capability_pairwise(int res, char *strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
- int ret, first = 1;
+ int ret;
char *pos, *end;
size_t len;
+ unsigned int i;
pos = buf;
end = pos + buflen;
@@ -2662,36 +3006,15 @@
return len;
}
- if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) {
- ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
- }
-
- if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
- ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
- }
-
- if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
- ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
- }
-
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
- ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
+ for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
+ if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ pos == buf ? "" : " ",
+ ciphers[i].name);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
}
return pos - buf;
@@ -2702,9 +3025,10 @@
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
- int ret, first = 1;
+ int ret;
char *pos, *end;
size_t len;
+ unsigned int i;
pos = buf;
end = pos + buflen;
@@ -2718,45 +3042,15 @@
return len;
}
- if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) {
- ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
- }
-
- if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
- ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
- }
-
- if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
- ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
- }
-
- if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP104) {
- ret = os_snprintf(pos, end - pos, "%sWEP104",
- first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
- }
-
- if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP40) {
- ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : " ");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- first = 0;
+ for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
+ if (capa->enc & ciphers[i].capa) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ pos == buf ? "" : " ",
+ ciphers[i].name);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
}
return pos - buf;
@@ -2820,7 +3114,7 @@
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
- int ret, first = 1;
+ int ret;
char *pos, *end;
size_t len;
@@ -2838,20 +3132,20 @@
if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
- ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
+ ret = os_snprintf(pos, end - pos, "%sRSN",
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- first = 0;
}
if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
- ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
+ ret = os_snprintf(pos, end - pos, "%sWPA",
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- first = 0;
}
return pos - buf;
@@ -2862,7 +3156,7 @@
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
- int ret, first = 1;
+ int ret;
char *pos, *end;
size_t len;
@@ -2879,28 +3173,27 @@
}
if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
- ret = os_snprintf(pos, end - pos, "%sOPEN", first ? "" : " ");
+ ret = os_snprintf(pos, end - pos, "%sOPEN",
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- first = 0;
}
if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
ret = os_snprintf(pos, end - pos, "%sSHARED",
- first ? "" : " ");
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- first = 0;
}
if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
- ret = os_snprintf(pos, end - pos, "%sLEAP", first ? "" : " ");
+ ret = os_snprintf(pos, end - pos, "%sLEAP",
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- first = 0;
}
return pos - buf;
@@ -2911,7 +3204,7 @@
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
- int ret, first = 1;
+ int ret;
char *pos, *end;
size_t len;
@@ -2928,19 +3221,19 @@
}
if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
- ret = os_snprintf(pos, end - pos, "%sIBSS", first ? "" : " ");
+ ret = os_snprintf(pos, end - pos, "%sIBSS",
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- first = 0;
}
if (capa->flags & WPA_DRIVER_FLAGS_AP) {
- ret = os_snprintf(pos, end - pos, "%sAP", first ? "" : " ");
+ ret = os_snprintf(pos, end - pos, "%sAP",
+ pos == buf ? "" : " ");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- first = 0;
}
return pos - buf;
@@ -3033,10 +3326,13 @@
for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
continue;
- ret = os_snprintf(pos, end - pos, " %d = %d MHz%s\n",
+ ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n",
chnl[i].chan, chnl[i].freq,
chnl[i].flag & HOSTAPD_CHAN_NO_IBSS ?
- " (NO_IBSS)" : "");
+ " (NO_IBSS)" : "",
+ chnl[i].flag & HOSTAPD_CHAN_RADAR ?
+ " (DFS)" : "");
+
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
@@ -3111,6 +3407,11 @@
if (os_strcmp(field, "freq") == 0)
return ctrl_iface_get_capability_freq(wpa_s, buf, buflen);
+#ifdef CONFIG_TDLS
+ if (os_strcmp(field, "tdls") == 0)
+ return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
+#endif /* CONFIG_TDLS */
+
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
field);
@@ -3232,9 +3533,9 @@
}
if (mask & WPA_BSS_MASK_AGE) {
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
ret = os_snprintf(pos, end - pos, "age=%d\n",
(int) (now.sec - bss->last_update.sec));
if (ret < 0 || ret >= end - pos)
@@ -3283,19 +3584,46 @@
return 0;
pos += ret;
}
- if (bss->caps & IEEE80211_CAP_IBSS) {
- ret = os_snprintf(pos, end - pos, "[IBSS]");
+ if (bss_is_dmg(bss)) {
+ const char *s;
+ ret = os_snprintf(pos, end - pos, "[DMG]");
if (ret < 0 || ret >= end - pos)
return 0;
pos += ret;
- }
- if (bss->caps & IEEE80211_CAP_ESS) {
- ret = os_snprintf(pos, end - pos, "[ESS]");
+ switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
+ case IEEE80211_CAP_DMG_IBSS:
+ s = "[IBSS]";
+ break;
+ case IEEE80211_CAP_DMG_AP:
+ s = "[ESS]";
+ break;
+ case IEEE80211_CAP_DMG_PBSS:
+ s = "[PBSS]";
+ break;
+ default:
+ s = "";
+ break;
+ }
+ ret = os_snprintf(pos, end - pos, "%s", s);
if (ret < 0 || ret >= end - pos)
return 0;
pos += ret;
+ } else {
+ if (bss->caps & IEEE80211_CAP_IBSS) {
+ ret = os_snprintf(pos, end - pos, "[IBSS]");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+ if (bss->caps & IEEE80211_CAP_ESS) {
+ ret = os_snprintf(pos, end - pos, "[ESS]");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
}
- if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) {
+ if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+ wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
ret = os_snprintf(pos, end - pos, "[P2P]");
if (ret < 0 || ret >= end - pos)
return 0;
@@ -3352,8 +3680,10 @@
WFD_IE_VENDOR_TYPE);
if (wfd) {
ret = os_snprintf(pos, end - pos, "wfd_subelems=");
- if (ret < 0 || ret >= end - pos)
+ if (ret < 0 || ret >= end - pos) {
+ wpabuf_free(wfd);
return 0;
+ }
pos += ret;
pos += wpa_snprintf_hex(pos, end - pos,
@@ -3392,6 +3722,10 @@
anqp->hs20_wan_metrics);
pos = anqp_add_hex(pos, end, "hs20_connection_capability",
anqp->hs20_connection_capability);
+ pos = anqp_add_hex(pos, end, "hs20_operating_class",
+ anqp->hs20_operating_class);
+ pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
+ anqp->hs20_osu_providers_list);
#endif /* CONFIG_HS20 */
}
#endif /* CONFIG_INTERWORKING */
@@ -3586,6 +3920,7 @@
}
+#ifdef CONFIG_TESTING_OPTIONS
static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
{
wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
@@ -3607,6 +3942,7 @@
MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
wpa_sm_drop_sa(wpa_s->wpa);
}
+#endif /* CONFIG_TESTING_OPTIONS */
static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
@@ -3659,9 +3995,15 @@
unsigned int timeout = atoi(cmd);
enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+ u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
char *pos;
unsigned int search_delay;
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Reject P2P_FIND since interface is disabled");
+ return -1;
+ }
if (os_strstr(cmd, "type=social"))
type = P2P_FIND_ONLY_SOCIAL;
else if (os_strstr(cmd, "type=progressive"))
@@ -3675,6 +4017,14 @@
_dev_id = dev_id;
}
+ pos = os_strstr(cmd, "dev_type=");
+ if (pos) {
+ pos += 9;
+ if (wps_dev_type_str2bin(pos, dev_type) < 0)
+ return -1;
+ _dev_type = dev_type;
+ }
+
pos = os_strstr(cmd, "delay=");
if (pos) {
pos += 6;
@@ -3682,8 +4032,8 @@
} else
search_delay = wpas_p2p_search_delay(wpa_s);
- return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id,
- search_delay);
+ return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
+ _dev_id, search_delay);
}
@@ -3703,12 +4053,12 @@
int go_intent = -1;
int freq = 0;
int pd;
- int ht40;
+ int ht40, vht;
/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad]
* [persistent|persistent=<network id>]
* [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
- * [ht40] */
+ * [ht40] [vht] */
if (hwaddr_aton(cmd, addr))
return -1;
@@ -3736,7 +4086,9 @@
auth = os_strstr(pos, " auth") != NULL;
automatic = os_strstr(pos, " auto") != NULL;
pd = os_strstr(pos, " provdisc") != NULL;
- ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+ vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
+ ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+ vht;
pos2 = os_strstr(pos, " go_intent=");
if (pos2) {
@@ -3777,7 +4129,7 @@
new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
persistent_group, automatic, join,
auth, go_intent, freq, persistent_id, pd,
- ht40);
+ ht40, vht);
if (new_pin == -2) {
os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
return 25;
@@ -3803,6 +4155,11 @@
static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
{
unsigned int timeout = atoi(cmd);
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Reject P2P_LISTEN since interface is disabled");
+ return -1;
+ }
return wpas_p2p_listen(wpa_s, timeout);
}
@@ -4141,7 +4498,7 @@
struct wpa_ssid *ssid;
u8 *_peer = NULL, peer[ETH_ALEN];
int freq = 0, pref_freq = 0;
- int ht40;
+ int ht40, vht;
id = atoi(cmd);
pos = os_strstr(cmd, " peer=");
@@ -4175,9 +4532,12 @@
return -1;
}
- ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+ vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
+ ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+ vht;
- return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, pref_freq);
+ return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht,
+ pref_freq);
}
@@ -4224,7 +4584,8 @@
static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
- char *cmd, int freq, int ht40)
+ char *cmd, int freq, int ht40,
+ int vht)
{
int id;
struct wpa_ssid *ssid;
@@ -4238,31 +4599,34 @@
return -1;
}
- return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, ht40, NULL);
+ return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
+ NULL, 0);
}
static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
{
- int freq = 0, ht40;
+ int freq = 0, ht40, vht;
char *pos;
pos = os_strstr(cmd, "freq=");
if (pos)
freq = atoi(pos + 5);
- ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+ vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht;
+ ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+ vht;
if (os_strncmp(cmd, "persistent=", 11) == 0)
return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq,
- ht40);
+ ht40, vht);
if (os_strcmp(cmd, "persistent") == 0 ||
os_strncmp(cmd, "persistent ", 11) == 0)
- return wpas_p2p_group_add(wpa_s, 1, freq, ht40);
+ return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht);
if (os_strncmp(cmd, "freq=", 5) == 0)
- return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
+ return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
if (ht40)
- return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
+ return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
cmd);
@@ -4358,6 +4722,22 @@
return pos - buf;
pos += res;
+ if (info->vendor_elems) {
+ res = os_snprintf(pos, end - pos, "vendor_elems=");
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ pos += wpa_snprintf_hex(pos, end - pos,
+ wpabuf_head(info->vendor_elems),
+ wpabuf_len(info->vendor_elems));
+
+ res = os_snprintf(pos, end - pos, "\n");
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
return pos - buf;
}
@@ -4365,48 +4745,21 @@
static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
const char *param)
{
- struct wpa_freq_range *freq = NULL, *n;
- unsigned int count = 0, i;
- const char *pos, *pos2, *pos3;
+ unsigned int i;
if (wpa_s->global->p2p == NULL)
return -1;
- /*
- * param includes comma separated frequency range.
- * For example: 2412-2432,2462,5000-6000
- */
- pos = param;
- 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++;
- }
+ if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0)
+ return -1;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) {
+ struct wpa_freq_range *freq;
+ freq = &wpa_s->global->p2p_disallow_freq.range[i];
wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u",
- freq[i].min, freq[i].max);
+ freq->min, freq->max);
}
- os_free(wpa_s->global->p2p_disallow_freq);
- wpa_s->global->p2p_disallow_freq = freq;
- wpa_s->global->num_p2p_disallow_freq = count;
wpas_p2p_update_channel_list(wpa_s);
return 0;
}
@@ -4437,7 +4790,7 @@
if (os_strcmp(cmd, "listen_channel") == 0) {
return p2p_set_listen_channel(wpa_s->global->p2p, 81,
- atoi(param));
+ atoi(param), 1);
}
if (os_strcmp(cmd, "ssid_postfix") == 0) {
@@ -4602,6 +4955,16 @@
return 0;
}
+#ifdef CONFIG_WPS_NFC
+ if (os_strcmp(cmd, "nfc_tag") == 0)
+ return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param));
+#endif /* CONFIG_WPS_NFC */
+
+ if (os_strcmp(cmd, "disable_ip_addr_req") == 0) {
+ wpa_s->p2p_disable_ip_addr_req = !!atoi(param);
+ return 0;
+ }
+
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
cmd);
@@ -4689,7 +5052,68 @@
#endif /* CONFIG_P2P */
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val)
+{
+ struct wpa_freq_range_list ranges;
+ int *freqs = NULL;
+ struct hostapd_hw_modes *mode;
+ u16 i;
+
+ if (wpa_s->hw.modes == NULL)
+ return NULL;
+
+ os_memset(&ranges, 0, sizeof(ranges));
+ if (freq_range_list_parse(&ranges, val) < 0)
+ return NULL;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ int j;
+
+ mode = &wpa_s->hw.modes[i];
+ for (j = 0; j < mode->num_channels; j++) {
+ unsigned int freq;
+
+ if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ freq = mode->channels[j].freq;
+ if (!freq_range_list_includes(&ranges, freq))
+ continue;
+
+ int_array_add_unique(&freqs, freq);
+ }
+ }
+
+ os_free(ranges.range);
+ return freqs;
+}
+
+
#ifdef CONFIG_INTERWORKING
+
+static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param)
+{
+ int auto_sel = 0;
+ int *freqs = NULL;
+
+ if (param) {
+ char *pos;
+
+ auto_sel = os_strstr(param, "auto") != NULL;
+
+ pos = os_strstr(param, "freq=");
+ if (pos) {
+ freqs = freq_range_to_channel_list(wpa_s, pos + 5);
+ if (freqs == NULL)
+ return -1;
+ }
+
+ }
+
+ return interworking_select(wpa_s, auto_sel, freqs);
+}
+
+
static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
{
u8 bssid[ETH_ALEN];
@@ -4719,15 +5143,27 @@
#define MAX_ANQP_INFO_ID 100
u16 id[MAX_ANQP_INFO_ID];
size_t num_id = 0;
+ u32 subtypes = 0;
used = hwaddr_aton2(dst, dst_addr);
if (used < 0)
return -1;
pos = dst + used;
while (num_id < MAX_ANQP_INFO_ID) {
- id[num_id] = atoi(pos);
- if (id[num_id])
- num_id++;
+ if (os_strncmp(pos, "hs20:", 5) == 0) {
+#ifdef CONFIG_HS20
+ int num = atoi(pos + 5);
+ if (num <= 0 || num > 31)
+ return -1;
+ subtypes |= BIT(num);
+#else /* CONFIG_HS20 */
+ return -1;
+#endif /* CONFIG_HS20 */
+ } else {
+ id[num_id] = atoi(pos);
+ if (id[num_id])
+ num_id++;
+ }
pos = os_strchr(pos + 1, ',');
if (pos == NULL)
break;
@@ -4737,7 +5173,7 @@
if (num_id == 0)
return -1;
- return anqp_send_req(wpa_s, dst_addr, id, num_id);
+ return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes);
}
@@ -4813,9 +5249,8 @@
int used;
char *pos;
size_t resp_len, start, requested_len;
-
- if (!wpa_s->last_gas_resp)
- return -1;
+ struct wpabuf *resp;
+ int ret;
used = hwaddr_aton2(cmd, addr);
if (used < 0)
@@ -4826,11 +5261,18 @@
pos++;
dialog_token = atoi(pos);
- if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 ||
- dialog_token != wpa_s->last_gas_dialog_token)
+ if (wpa_s->last_gas_resp &&
+ os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
+ dialog_token == wpa_s->last_gas_dialog_token)
+ resp = wpa_s->last_gas_resp;
+ else if (wpa_s->prev_gas_resp &&
+ os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
+ dialog_token == wpa_s->prev_gas_dialog_token)
+ resp = wpa_s->prev_gas_resp;
+ else
return -1;
- resp_len = wpabuf_len(wpa_s->last_gas_resp);
+ resp_len = wpabuf_len(resp);
start = 0;
requested_len = resp_len;
@@ -4851,9 +5293,24 @@
if (requested_len * 2 + 1 > buflen)
return os_snprintf(buf, buflen, "FAIL-Too long response");
- return wpa_snprintf_hex(buf, buflen,
- wpabuf_head_u8(wpa_s->last_gas_resp) + start,
- requested_len);
+ ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start,
+ requested_len);
+
+ if (start + requested_len == resp_len) {
+ /*
+ * Free memory by dropping the response after it has been
+ * fetched.
+ */
+ if (resp == wpa_s->prev_gas_resp) {
+ wpabuf_free(wpa_s->prev_gas_resp);
+ wpa_s->prev_gas_resp = NULL;
+ } else {
+ wpabuf_free(wpa_s->last_gas_resp);
+ wpa_s->last_gas_resp = NULL;
+ }
+ }
+
+ return ret;
}
#endif /* CONFIG_INTERWORKING */
@@ -4941,7 +5398,7 @@
if (len == 0 && cred && cred->realm)
return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
- if (len % 1)
+ if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
@@ -4960,6 +5417,26 @@
return ret;
}
+
+static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ char *icon;
+
+ used = hwaddr_aton2(cmd, dst_addr);
+ if (used < 0)
+ return -1;
+
+ while (cmd[used] == ' ')
+ used++;
+ icon = &cmd[used];
+
+ wpa_s->fetch_osu_icon_in_progress = 0;
+ return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
+ (u8 *) icon, os_strlen(icon));
+}
+
#endif /* CONFIG_HS20 */
@@ -5177,11 +5654,68 @@
p2p_set_country(p2p, country);
}
}
- ret = sprintf(buf, "%s\n", "OK");
+ ret = os_snprintf(buf, buflen, "%s\n", "OK");
}
return ret;
}
-#endif
+#endif /* ANDROID */
+
+
+static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos;
+ u8 *data = NULL;
+ unsigned int vendor_id, subcmd;
+ struct wpabuf *reply;
+ size_t data_len = 0;
+
+ /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+ vendor_id = strtoul(cmd, &pos, 16);
+ if (!isblank(*pos))
+ return -EINVAL;
+
+ subcmd = strtoul(pos, &pos, 10);
+
+ if (*pos != '\0') {
+ if (!isblank(*pos++))
+ return -EINVAL;
+ data_len = os_strlen(pos);
+ }
+
+ if (data_len) {
+ data_len /= 2;
+ data = os_malloc(data_len);
+ if (!data)
+ return -1;
+
+ if (hexstr2bin(pos, data, data_len)) {
+ wpa_printf(MSG_DEBUG,
+ "Vendor command: wrong parameter format");
+ os_free(data);
+ return -EINVAL;
+ }
+ }
+
+ reply = wpabuf_alloc((buflen - 1) / 2);
+ if (!reply) {
+ os_free(data);
+ return -1;
+ }
+
+ ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
+ reply);
+
+ if (ret == 0)
+ ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
+ wpabuf_len(reply));
+
+ wpabuf_free(reply);
+ os_free(data);
+
+ return ret;
+}
static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
@@ -5189,24 +5723,36 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
#ifdef CONFIG_P2P
+ wpas_p2p_cancel(wpa_s);
wpas_p2p_stop_find(wpa_s);
p2p_ctrl_flush(wpa_s);
wpas_p2p_group_remove(wpa_s, "*");
+ wpas_p2p_service_flush(wpa_s);
+ wpa_s->global->p2p_disabled = 0;
+ wpa_s->global->p2p_per_sta_psk = 0;
+ wpa_s->conf->num_sec_device_types = 0;
+ wpa_s->p2p_disable_ip_addr_req = 0;
+ os_free(wpa_s->global->p2p_go_avoid_freq.range);
+ wpa_s->global->p2p_go_avoid_freq.range = NULL;
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS_TESTING
wps_version_number = 0x20;
wps_testing_dummy_cred = 0;
+ wps_corrupt_pkhash = 0;
#endif /* CONFIG_WPS_TESTING */
#ifdef CONFIG_WPS
+ wpa_s->wps_fragment_size = 0;
wpas_wps_cancel(wpa_s);
#endif /* CONFIG_WPS */
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+#ifdef CONFIG_TDLS
#ifdef CONFIG_TDLS_TESTING
extern unsigned int tdls_testing;
tdls_testing = 0;
#endif /* CONFIG_TDLS_TESTING */
-#ifdef CONFIG_TDLS
wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
wpa_tdls_enable(wpa_s->wpa, 1);
#endif /* CONFIG_TDLS */
@@ -5233,6 +5779,678 @@
wpa_s->extra_blacklist_count = 0;
wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
+ wpa_config_flush_blobs(wpa_s->conf);
+ wpa_s->conf->auto_interworking = 0;
+ wpa_s->conf->okc = 0;
+
+ wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
+ wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
+ wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60);
+ eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+
+ radio_remove_works(wpa_s, NULL, 1);
+
+ wpa_s->next_ssid = NULL;
+
+#ifdef CONFIG_INTERWORKING
+ hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_INTERWORKING */
+
+ wpa_s->ext_mgmt_frame_handling = 0;
+}
+
+
+static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ struct wpa_radio_work *work;
+ char *pos, *end;
+ struct os_reltime now, diff;
+
+ pos = buf;
+ end = buf + buflen;
+
+ os_get_reltime(&now);
+
+ dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+ {
+ int ret;
+
+ os_reltime_sub(&now, &work->time, &diff);
+ ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n",
+ work->type, work->wpa_s->ifname, work->freq,
+ work->started, diff.sec, diff.usec);
+ if (ret < 0 || ret >= end - pos)
+ break;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_radio_work *work = eloop_ctx;
+ struct wpa_external_work *ework = work->ctx;
+
+ wpa_dbg(work->wpa_s, MSG_DEBUG,
+ "Timing out external radio work %u (%s)",
+ ework->id, work->type);
+ wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id);
+ radio_work_done(work);
+ os_free(ework);
+}
+
+
+static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_external_work *ework = work->ctx;
+
+ if (deinit) {
+ if (work->started)
+ eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+ work, NULL);
+
+ os_free(ework);
+ return;
+ }
+
+ wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
+ ework->id, ework->type);
+ wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id);
+ if (!ework->timeout)
+ ework->timeout = 10;
+ eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout,
+ work, NULL);
+}
+
+
+static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ struct wpa_external_work *ework;
+ char *pos, *pos2;
+ size_t type_len;
+ int ret;
+ unsigned int freq = 0;
+
+ /* format: <name> [freq=<MHz>] [timeout=<seconds>] */
+
+ ework = os_zalloc(sizeof(*ework));
+ if (ework == NULL)
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos) {
+ type_len = pos - cmd;
+ pos++;
+
+ pos2 = os_strstr(pos, "freq=");
+ if (pos2)
+ freq = atoi(pos2 + 5);
+
+ pos2 = os_strstr(pos, "timeout=");
+ if (pos2)
+ ework->timeout = atoi(pos2 + 8);
+ } else {
+ type_len = os_strlen(cmd);
+ }
+ if (4 + type_len >= sizeof(ework->type))
+ type_len = sizeof(ework->type) - 4 - 1;
+ os_strlcpy(ework->type, "ext:", sizeof(ework->type));
+ os_memcpy(ework->type + 4, cmd, type_len);
+ ework->type[4 + type_len] = '\0';
+
+ wpa_s->ext_work_id++;
+ if (wpa_s->ext_work_id == 0)
+ wpa_s->ext_work_id++;
+ ework->id = wpa_s->ext_work_id;
+
+ if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb,
+ ework) < 0) {
+ os_free(ework);
+ return -1;
+ }
+
+ ret = os_snprintf(buf, buflen, "%u", ework->id);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+ return ret;
+}
+
+
+static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ struct wpa_radio_work *work;
+ unsigned int id = atoi(cmd);
+
+ dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+ {
+ struct wpa_external_work *ework;
+
+ if (os_strncmp(work->type, "ext:", 4) != 0)
+ continue;
+ ework = work->ctx;
+ if (id && ework->id != id)
+ continue;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Completed external radio work %u (%s)",
+ ework->id, ework->type);
+ eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL);
+ radio_work_done(work);
+ os_free(ework);
+ return 3; /* "OK\n" */
+ }
+
+ return -1;
+}
+
+
+static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ if (os_strcmp(cmd, "show") == 0)
+ return wpas_ctrl_radio_work_show(wpa_s, buf, buflen);
+ if (os_strncmp(cmd, "add ", 4) == 0)
+ return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen);
+ if (os_strncmp(cmd, "done ", 5) == 0)
+ return wpas_ctrl_radio_work_done(wpa_s, cmd + 4);
+ return -1;
+}
+
+
+void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_radio_work *work, *tmp;
+
+ if (!wpa_s || !wpa_s->radio)
+ return;
+
+ dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
+ struct wpa_radio_work, list) {
+ struct wpa_external_work *ework;
+
+ if (os_strncmp(work->type, "ext:", 4) != 0)
+ continue;
+ ework = work->ctx;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Flushing%s external radio work %u (%s)",
+ work->started ? " started" : "", ework->id,
+ ework->type);
+ if (work->started)
+ eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+ work, NULL);
+ radio_work_done(work);
+ os_free(ework);
+ }
+}
+
+
+static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ eapol_sm_notify_ctrl_response(wpa_s->eapol);
+}
+
+
+static int set_scan_freqs(struct wpa_supplicant *wpa_s, char *val)
+{
+ int *freqs = NULL;
+
+ freqs = freq_range_to_channel_list(wpa_s, val);
+ if (freqs == NULL)
+ return -1;
+
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = freqs;
+
+ return 0;
+}
+
+
+static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value)
+{
+ const char *pos = value;
+
+ while (pos) {
+ if (*pos == ' ' || *pos == '\0')
+ break;
+ if (wpa_s->scan_id_count == MAX_SCAN_ID)
+ return -1;
+ wpa_s->scan_id[wpa_s->scan_id_count++] = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos)
+ pos++;
+ }
+
+ return 0;
+}
+
+
+static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
+ char *reply, int reply_size, int *reply_len)
+{
+ char *pos;
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ *reply_len = -1;
+ return;
+ }
+
+ wpa_s->manual_scan_passive = 0;
+ wpa_s->manual_scan_use_id = 0;
+ wpa_s->manual_scan_only_new = 0;
+ wpa_s->scan_id_count = 0;
+
+ if (params) {
+ if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
+ wpa_s->scan_res_handler = scan_only_handler;
+
+ pos = os_strstr(params, "freq=");
+ if (pos && set_scan_freqs(wpa_s, pos + 5) < 0) {
+ *reply_len = -1;
+ return;
+ }
+
+ pos = os_strstr(params, "passive=");
+ if (pos)
+ wpa_s->manual_scan_passive = !!atoi(pos + 8);
+
+ pos = os_strstr(params, "use_id=");
+ if (pos)
+ wpa_s->manual_scan_use_id = atoi(pos + 7);
+
+ pos = os_strstr(params, "only_new=1");
+ if (pos)
+ wpa_s->manual_scan_only_new = 1;
+
+ pos = os_strstr(params, "scan_id=");
+ if (pos && scan_id_list_parse(wpa_s, pos + 8) < 0) {
+ *reply_len = -1;
+ return;
+ }
+ } else {
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = NULL;
+ if (wpa_s->scan_res_handler == scan_only_handler)
+ wpa_s->scan_res_handler = NULL;
+ }
+
+ if (!wpa_s->sched_scanning && !wpa_s->scanning &&
+ ((wpa_s->wpa_state <= WPA_SCANNING) ||
+ (wpa_s->wpa_state == WPA_COMPLETED))) {
+ wpa_s->normal_scans = 0;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_s->manual_scan_use_id) {
+ wpa_s->manual_scan_id++;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
+ wpa_s->manual_scan_id);
+ *reply_len = os_snprintf(reply, reply_size, "%u\n",
+ wpa_s->manual_scan_id);
+ }
+ } else if (wpa_s->sched_scanning) {
+ wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_s->manual_scan_use_id) {
+ wpa_s->manual_scan_id++;
+ *reply_len = os_snprintf(reply, reply_size, "%u\n",
+ wpa_s->manual_scan_id);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
+ wpa_s->manual_scan_id);
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
+ *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+ }
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+static void wpas_ctrl_iface_mgmt_tx_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result)
+{
+ wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR
+ " src=" MACSTR " bssid=" MACSTR " result=%s",
+ freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ result == OFFCHANNEL_SEND_ACTION_SUCCESS ?
+ "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ?
+ "NO_ACK" : "FAILED"));
+}
+
+
+static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos, *param;
+ size_t len;
+ u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN];
+ int res, used;
+ int freq = 0, no_cck = 0, wait_time = 0;
+
+ /* <DA> <BSSID> [freq=<MHz>] [wait_time=<ms>] [no_cck=1]
+ * <action=Action frame payload> */
+
+ wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
+
+ pos = cmd;
+ used = hwaddr_aton2(pos, da);
+ if (used < 0)
+ return -1;
+ pos += used;
+ while (*pos == ' ')
+ pos++;
+ used = hwaddr_aton2(pos, bssid);
+ if (used < 0)
+ return -1;
+ pos += used;
+
+ param = os_strstr(pos, " freq=");
+ if (param) {
+ param += 6;
+ freq = atoi(param);
+ }
+
+ param = os_strstr(pos, " no_cck=");
+ if (param) {
+ param += 8;
+ no_cck = atoi(param);
+ }
+
+ param = os_strstr(pos, " wait_time=");
+ if (param) {
+ param += 11;
+ wait_time = atoi(param);
+ }
+
+ param = os_strstr(pos, " action=");
+ if (param == NULL)
+ return -1;
+ param += 8;
+
+ len = os_strlen(param);
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(param, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid,
+ buf, len, wait_time,
+ wpas_ctrl_iface_mgmt_tx_cb, no_cck);
+ os_free(buf);
+ return res;
+}
+
+
+static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting");
+ offchannel_send_action_done(wpa_s);
+}
+
+
+static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos, *param;
+ union wpa_event_data event;
+ enum wpa_event_type ev;
+
+ /* <event name> [parameters..] */
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd);
+
+ pos = cmd;
+ param = os_strchr(pos, ' ');
+ if (param)
+ *param++ = '\0';
+
+ os_memset(&event, 0, sizeof(event));
+
+ if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) {
+ ev = EVENT_INTERFACE_ENABLED;
+ } else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) {
+ ev = EVENT_INTERFACE_DISABLED;
+ } else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) {
+ ev = EVENT_AVOID_FREQUENCIES;
+ if (param == NULL)
+ param = "";
+ if (freq_range_list_parse(&event.freq_range, param) < 0)
+ return -1;
+ wpa_supplicant_event(wpa_s, ev, &event);
+ os_free(event.freq_range.range);
+ return 0;
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s",
+ cmd);
+ return -1;
+ }
+
+ wpa_supplicant_event(wpa_s, ev, &event);
+
+ return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s)
+{
+ unsigned int i;
+ char buf[30];
+
+ wpa_printf(MSG_DEBUG, "Update vendor elements");
+
+ for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+ if (wpa_s->vendor_elem[i]) {
+ os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+ wpa_hexdump_buf(MSG_DEBUG, buf, wpa_s->vendor_elem[i]);
+ }
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->parent == wpa_s &&
+ wpa_s->global->p2p &&
+ !wpa_s->global->p2p_disabled)
+ p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
+#endif /* CONFIG_P2P */
+}
+
+
+static struct wpa_supplicant *
+wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s,
+ enum wpa_vendor_elem_frame frame)
+{
+ switch (frame) {
+#ifdef CONFIG_P2P
+ case VENDOR_ELEM_PROBE_REQ_P2P:
+ case VENDOR_ELEM_PROBE_RESP_P2P:
+ case VENDOR_ELEM_PROBE_RESP_P2P_GO:
+ case VENDOR_ELEM_BEACON_P2P_GO:
+ case VENDOR_ELEM_P2P_PD_REQ:
+ case VENDOR_ELEM_P2P_PD_RESP:
+ case VENDOR_ELEM_P2P_GO_NEG_REQ:
+ case VENDOR_ELEM_P2P_GO_NEG_RESP:
+ case VENDOR_ELEM_P2P_GO_NEG_CONF:
+ case VENDOR_ELEM_P2P_INV_REQ:
+ case VENDOR_ELEM_P2P_INV_RESP:
+ case VENDOR_ELEM_P2P_ASSOC_REQ:
+ return wpa_s->parent;
+#endif /* CONFIG_P2P */
+ default:
+ return wpa_s;
+ }
+}
+
+
+static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos = cmd;
+ int frame;
+ size_t len;
+ struct wpabuf *buf;
+ struct ieee802_11_elems elems;
+
+ frame = atoi(pos);
+ if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+ return -1;
+ wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ pos++;
+
+ len = os_strlen(pos);
+ if (len == 0)
+ return 0;
+ if (len & 1)
+ 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;
+ }
+
+ if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) ==
+ ParseFailed) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ if (wpa_s->vendor_elem[frame] == NULL) {
+ wpa_s->vendor_elem[frame] = buf;
+ wpas_ctrl_vendor_elem_update(wpa_s);
+ return 0;
+ }
+
+ if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ wpabuf_put_buf(wpa_s->vendor_elem[frame], buf);
+ wpabuf_free(buf);
+ wpas_ctrl_vendor_elem_update(wpa_s);
+
+ return 0;
+}
+
+
+static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ int frame = atoi(cmd);
+
+ if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+ return -1;
+ wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+ if (wpa_s->vendor_elem[frame] == NULL)
+ return 0;
+
+ return wpa_snprintf_hex(buf, buflen,
+ wpabuf_head_u8(wpa_s->vendor_elem[frame]),
+ wpabuf_len(wpa_s->vendor_elem[frame]));
+}
+
+
+static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos = cmd;
+ int frame;
+ size_t len;
+ u8 *buf;
+ struct ieee802_11_elems elems;
+ u8 *ie, *end;
+
+ frame = atoi(pos);
+ if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+ return -1;
+ wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ pos++;
+
+ if (*pos == '*') {
+ wpabuf_free(wpa_s->vendor_elem[frame]);
+ wpa_s->vendor_elem[frame] = NULL;
+ wpas_ctrl_vendor_elem_update(wpa_s);
+ return 0;
+ }
+
+ if (wpa_s->vendor_elem[frame] == NULL)
+ return -1;
+
+ len = os_strlen(pos);
+ if (len == 0)
+ return 0;
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(pos, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) {
+ os_free(buf);
+ return -1;
+ }
+
+ ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
+ end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
+
+ for (; ie + 1 < end; ie += 2 + ie[1]) {
+ if (ie + len > end)
+ break;
+ if (os_memcmp(ie, buf, len) != 0)
+ continue;
+
+ if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
+ wpabuf_free(wpa_s->vendor_elem[frame]);
+ wpa_s->vendor_elem[frame] = NULL;
+ } else {
+ os_memmove(ie, ie + len,
+ wpabuf_len(wpa_s->vendor_elem[frame]) - len);
+ wpa_s->vendor_elem[frame]->used -= len;
+ }
+ os_free(buf);
+ wpas_ctrl_vendor_elem_update(wpa_s);
+ return 0;
+ }
+
+ os_free(buf);
+
+ return -1;
}
@@ -5241,22 +6459,27 @@
{
char *reply;
const int reply_size = 4096;
- int ctrl_rsp = 0;
int reply_len;
if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
- os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
- os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
- os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0 ||
- os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) {
+ os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+ if (wpa_debug_show_keys)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Control interface command '%s'", buf);
+ else
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Control interface command '%s [REMOVED]'",
+ os_strncmp(buf, WPA_CTRL_RSP,
+ os_strlen(WPA_CTRL_RSP)) == 0 ?
+ WPA_CTRL_RSP : "SET_NETWORK");
+ } else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
+ os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
(const u8 *) buf, os_strlen(buf));
} else {
int level = MSG_DEBUG;
if (os_strcmp(buf, "PING") == 0)
level = MSG_EXCESSIVE;
- wpa_hexdump_ascii(level, "RX ctrl_iface",
- (const u8 *) buf, os_strlen(buf));
wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
}
@@ -5312,6 +6535,14 @@
reply_len = -1;
else
wpas_request_connection(wpa_s);
+ } else if (os_strcmp(buf, "REATTACH") == 0) {
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED ||
+ !wpa_s->current_ssid)
+ reply_len = -1;
+ else {
+ wpa_s->reattach = 1;
+ wpas_request_connection(wpa_s);
+ }
} else if (os_strcmp(buf, "RECONNECT") == 0) {
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
reply_len = -1;
@@ -5380,12 +6611,6 @@
} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
reply_len = wpas_ctrl_nfc_get_handover_sel(
wpa_s, buf + 21, reply, reply_size);
- } else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) {
- reply_len = wpas_ctrl_nfc_rx_handover_req(
- wpa_s, buf + 20, reply, reply_size);
- } else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) {
- if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20))
- reply_len = -1;
} else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
reply_len = -1;
@@ -5468,7 +6693,7 @@
if (wpas_p2p_group_remove(wpa_s, buf + 17))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
- if (wpas_p2p_group_add(wpa_s, 0, 0, 0))
+ if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0))
reply_len = -1;
} else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
if (p2p_ctrl_group_add(wpa_s, buf + 14))
@@ -5550,9 +6775,11 @@
reply_len = -1;
} else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
interworking_stop_fetch_anqp(wpa_s);
- } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) {
- if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") !=
- NULL) < 0)
+ } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) {
+ if (ctrl_interworking_select(wpa_s, NULL) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) {
+ if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
@@ -5574,14 +6801,28 @@
} else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
+ if (hs20_icon_request(wpa_s, buf + 18) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "FETCH_OSU") == 0) {
+ if (hs20_fetch_osu(wpa_s) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
+ hs20_cancel_fetch_osu(wpa_s);
#endif /* CONFIG_HS20 */
} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
{
if (wpa_supplicant_ctrl_iface_ctrl_rsp(
wpa_s, buf + os_strlen(WPA_CTRL_RSP)))
reply_len = -1;
- else
- ctrl_rsp = 1;
+ else {
+ /*
+ * Notify response from timeout to allow the control
+ * interface response to be sent first.
+ */
+ eloop_register_timeout(0, 0, wpas_ctrl_eapol_response,
+ wpa_s, NULL);
+ }
} else if (os_strcmp(buf, "RECONFIGURE") == 0) {
if (wpa_supplicant_reload_configuration(wpa_s))
reply_len = -1;
@@ -5609,34 +6850,10 @@
wpa_supplicant_cancel_scan(wpa_s);
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
- } else if (os_strcmp(buf, "SCAN") == 0 ||
- os_strncmp(buf, "SCAN ", 5) == 0) {
- if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
- reply_len = -1;
- else {
- if (os_strlen(buf) > 4 &&
- os_strncasecmp(buf + 5, "TYPE=ONLY", 9) == 0)
- wpa_s->scan_res_handler = scan_only_handler;
- if (!wpa_s->sched_scanning && !wpa_s->scanning &&
- ((wpa_s->wpa_state <= WPA_SCANNING) ||
- (wpa_s->wpa_state == WPA_COMPLETED))) {
- wpa_s->normal_scans = 0;
- wpa_s->scan_req = MANUAL_SCAN_REQ;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
- } else if (wpa_s->sched_scanning) {
- wpa_printf(MSG_DEBUG, "Stop ongoing "
- "sched_scan to allow requested "
- "full scan to proceed");
- wpa_supplicant_cancel_sched_scan(wpa_s);
- wpa_s->scan_req = MANUAL_SCAN_REQ;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
- } else {
- wpa_printf(MSG_DEBUG, "Ongoing scan action - "
- "reject new request");
- reply_len = os_snprintf(reply, reply_size,
- "FAIL-BUSY\n");
- }
- }
+ } else if (os_strcmp(buf, "SCAN") == 0) {
+ wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
+ } else if (os_strncmp(buf, "SCAN ", 5) == 0) {
+ wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);
} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_scan_results(
wpa_s, reply, reply_size);
@@ -5661,6 +6878,9 @@
} else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
reply_len = wpa_supplicant_ctrl_iface_get_network(
wpa_s, buf + 12, reply, reply_size);
+ } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+ if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12))
+ reply_len = -1;
} else if (os_strcmp(buf, "LIST_CREDS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_list_creds(
wpa_s, reply, reply_size);
@@ -5673,6 +6893,10 @@
} else if (os_strncmp(buf, "SET_CRED ", 9) == 0) {
if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9))
reply_len = -1;
+ } else if (os_strncmp(buf, "GET_CRED ", 9) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9,
+ reply,
+ reply_size);
#ifndef CONFIG_NO_CONFIG_WRITE
} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
@@ -5711,13 +6935,18 @@
} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
reply_len = -1;
+ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+ if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
+ reply_len = -1;
#endif /* CONFIG_AP */
} else if (os_strcmp(buf, "SUSPEND") == 0) {
wpas_notify_suspend(wpa_s->global);
} else if (os_strcmp(buf, "RESUME") == 0) {
wpas_notify_resume(wpa_s->global);
+#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcmp(buf, "DROP_SA") == 0) {
wpa_supplicant_ctrl_iface_drop_sa(wpa_s);
+#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "ROAM ", 5) == 0) {
if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
reply_len = -1;
@@ -5760,7 +6989,10 @@
} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
reply_size);
-#endif
+#endif /* ANDROID */
+ } else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
+ reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply,
+ reply_size);
} else if (os_strcmp(buf, "REAUTHENTICATE") == 0) {
pmksa_cache_clear_current(wpa_s->wpa);
eapol_sm_request_reauth(wpa_s->eapol);
@@ -5774,6 +7006,28 @@
#endif /* CONFIG_WNM */
} else if (os_strcmp(buf, "FLUSH") == 0) {
wpa_supplicant_ctrl_iface_flush(wpa_s);
+ } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
+ reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply,
+ reply_size);
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
+ if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) {
+ wpas_ctrl_iface_mgmt_tx_done(wpa_s);
+ } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) {
+ if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0)
+ reply_len = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
+ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
+ if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) {
+ reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) {
+ if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
+ reply_len = -1;
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -5784,9 +7038,6 @@
reply_len = 5;
}
- if (ctrl_rsp)
- eapol_sm_notify_ctrl_response(wpa_s->eapol);
-
*resp_len = reply_len;
return reply;
}
@@ -5993,10 +7244,7 @@
{
#ifdef CONFIG_P2P
static const char * cmd[] = {
-#ifdef ANDROID_P2P
"LIST_NETWORKS",
- "SAVE_CONFIG",
-#endif
"P2P_FIND",
"P2P_STOP_FIND",
"P2P_LISTEN",
@@ -6011,12 +7259,11 @@
NULL
};
static const char * prefix[] = {
-#ifdef ANDROID_P2P
+#ifdef ANDROID
"DRIVER ",
+#endif /* ANDROID */
"GET_NETWORK ",
"REMOVE_NETWORK ",
- "SET ",
-#endif
"P2P_FIND ",
"P2P_CONNECT ",
"P2P_LISTEN ",
@@ -6037,6 +7284,9 @@
"P2P_PRESENCE_REQ ",
"P2P_EXT_LISTEN ",
"P2P_REMOVE_CLIENT ",
+ "NFC_GET_HANDOVER_SEL ",
+ "NFC_GET_HANDOVER_REQ ",
+ "NFC_REPORT_HANDOVER ",
NULL
};
int found = 0;
@@ -6113,6 +7363,9 @@
}
#endif /* CONFIG_WIFI_DISPLAY */
+ /* Restore cmd to its original value to allow redirection */
+ value[-1] = ' ';
+
return -1;
}
@@ -6120,7 +7373,7 @@
#ifndef CONFIG_NO_CONFIG_WRITE
static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
{
- int ret = 0;
+ int ret = 0, saved = 0;
struct wpa_supplicant *wpa_s;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
@@ -6134,9 +7387,16 @@
ret = 1;
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated");
+ saved++;
}
}
+ if (!saved && !ret) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated");
+ ret = 1;
+ }
+
return ret;
}
#endif /* CONFIG_NO_CONFIG_WRITE */
@@ -6249,8 +7509,19 @@
} else if (os_strcmp(buf, "RESUME") == 0) {
wpas_notify_resume(global);
} else if (os_strncmp(buf, "SET ", 4) == 0) {
- if (wpas_global_ctrl_iface_set(global, buf + 4))
+ if (wpas_global_ctrl_iface_set(global, buf + 4)) {
+#ifdef CONFIG_P2P
+ if (global->p2p_init_wpa_s) {
+ os_free(reply);
+ /* Check if P2P redirection would work for this
+ * command. */
+ return wpa_supplicant_ctrl_iface_process(
+ global->p2p_init_wpa_s,
+ buf, resp_len);
+ }
+#endif /* CONFIG_P2P */
reply_len = -1;
+ }
#ifndef CONFIG_NO_CONFIG_WRITE
} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
if (wpas_global_ctrl_iface_save_config(global))
@@ -6259,6 +7530,12 @@
} else if (os_strcmp(buf, "STATUS") == 0) {
reply_len = wpas_global_ctrl_iface_status(global, reply,
reply_size);
+#ifdef CONFIG_MODULE_TESTS
+ } else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
+ int wpas_module_tests(void);
+ if (wpas_module_tests() < 0)
+ reply_len = -1;
+#endif /* CONFIG_MODULE_TESTS */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index a329ef3..d54cc07 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -32,7 +32,7 @@
char *buf, size_t *resp_len);
/**
- * wpa_supplicant_ctrl_iface_process - Process global ctrl_iface command
+ * wpa_supplicant_global_ctrl_iface_process - Process global ctrl_iface command
* @global: Pointer to global data from wpa_supplicant_init()
* @buf: Received command buffer (nul terminated string)
* @resp_len: Variable to be set to the response length
@@ -113,6 +113,8 @@
void wpa_supplicant_global_ctrl_iface_deinit(
struct ctrl_iface_global_priv *priv);
+void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s);
+
#else /* CONFIG_CTRL_IFACE */
static inline struct ctrl_iface_priv *
@@ -148,6 +150,10 @@
{
}
+static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+}
+
#endif /* CONFIG_CTRL_IFACE */
#endif /* CTRL_IFACE_H */
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 8c09ba1..9d0674d 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -30,7 +30,11 @@
*/
struct wpa_ctrl_dst {
struct wpa_ctrl_dst *next;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 addr;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in addr;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t addrlen;
int debug_level;
int errors;
@@ -51,38 +55,68 @@
static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen)
{
struct wpa_ctrl_dst *dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_UDP_IPV6 */
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
return -1;
- os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in));
+ os_memcpy(&dst->addr, from, sizeof(*from));
dst->addrlen = fromlen;
dst->debug_level = MSG_INFO;
dst->next = priv->ctrl_dst;
priv->ctrl_dst = dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
+ inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)),
+ ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
inet_ntoa(from->sin_addr), ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
return 0;
}
static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen)
{
struct wpa_ctrl_dst *dst, *prev = NULL;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst = priv->ctrl_dst;
while (dst) {
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ if (from->sin6_port == dst->addr.sin6_port &&
+ !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
+ sizeof(from->sin6_addr))) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s:%d",
+ inet_ntop(AF_INET6, &from->sin6_addr, addr,
+ sizeof(*from)),
+ ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
from->sin_port == dst->addr.sin_port) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
"%s:%d", inet_ntoa(from->sin_addr),
ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (prev == NULL)
priv->ctrl_dst = dst->next;
else
@@ -98,21 +132,38 @@
static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen,
char *level)
{
struct wpa_ctrl_dst *dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
dst = priv->ctrl_dst;
while (dst) {
+#if CONFIG_CTRL_IFACE_UDP_IPV6
+ if (from->sin6_port == dst->addr.sin6_port &&
+ !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
+ sizeof(from->sin6_addr))) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level %s:%d",
+ inet_ntop(AF_INET6, &from->sin6_addr, addr,
+ sizeof(*from)),
+ ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
from->sin_port == dst->addr.sin_port) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
"level %s:%d", inet_ntoa(from->sin_addr),
ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst->debug_level = atoi(level);
return 0;
}
@@ -150,7 +201,14 @@
struct ctrl_iface_priv *priv = sock_ctx;
char buf[256], *pos;
int res;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 from;
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in from;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen = sizeof(from);
char *reply = NULL;
size_t reply_len = 0;
@@ -165,6 +223,13 @@
}
#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
+ if (os_strcmp(addr, "::1")) {
+ wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
+ addr);
+ }
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
/*
* The OS networking stack is expected to drop this kind of
@@ -176,6 +241,7 @@
"source %s", inet_ntoa(from.sin_addr));
return;
}
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
buf[res] = '\0';
@@ -269,8 +335,14 @@
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv;
- struct sockaddr_in addr;
int port = WPA_CTRL_IFACE_PORT;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 addr;
+ int domain = PF_INET6;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+ struct sockaddr_in addr;
+ int domain = PF_INET;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
@@ -282,21 +354,34 @@
if (wpa_s->conf->ctrl_interface == NULL)
return priv;
- priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ priv->sock = socket(domain, SOCK_DGRAM, 0);
if (priv->sock < 0) {
perror("socket(PF_INET)");
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ addr.sin6_family = AF_INET6;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ addr.sin6_addr = in6addr_any;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+ inet_pton(AF_INET6, "::1", &addr.sin6_addr);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
addr.sin_family = AF_INET;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
addr.sin_addr.s_addr = INADDR_ANY;
#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
addr.sin_addr.s_addr = htonl((127 << 24) | 1);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
try_again:
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ addr.sin6_port = htons(port);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
addr.sin_port = htons(port);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
port--;
if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
@@ -362,6 +447,9 @@
int idx;
char *sbuf;
int llen;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst = priv->ctrl_dst;
if (priv->sock < 0 || dst == NULL)
@@ -381,9 +469,16 @@
while (dst) {
next = dst->next;
if (level >= dst->debug_level) {
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
+ inet_ntop(AF_INET6, &dst->addr.sin6_addr,
+ addr, sizeof(dst->addr)),
+ ntohs(dst->addr.sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
inet_ntoa(dst->addr.sin_addr),
ntohs(dst->addr.sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (sendto(priv->sock, sbuf, llen + len, 0,
(struct sockaddr *) &dst->addr,
sizeof(dst->addr)) < 0) {
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index e35d2c3..40082e2 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / UNIX domain socket -based control interface
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -57,10 +57,17 @@
};
-static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock,
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+ const char *ifname, int sock,
struct dl_list *ctrl_dst,
int level, const char *buf,
- size_t len);
+ size_t len,
+ struct ctrl_iface_priv *priv,
+ struct ctrl_iface_global_priv *gp);
+static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv);
+static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
+ struct ctrl_iface_global_priv *priv);
static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
@@ -68,6 +75,7 @@
socklen_t fromlen)
{
struct wpa_ctrl_dst *dst;
+ char addr_txt[200];
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
@@ -76,9 +84,10 @@
dst->addrlen = fromlen;
dst->debug_level = MSG_INFO;
dl_list_add(ctrl_dst, &dst->list);
- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
- (u8 *) from->sun_path,
- fromlen - offsetof(struct sockaddr_un, sun_path));
+ printf_encode(addr_txt, sizeof(addr_txt),
+ (u8 *) from->sun_path,
+ fromlen - offsetof(struct sockaddr_un, sun_path));
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s", addr_txt);
return 0;
}
@@ -94,10 +103,13 @@
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));
+ char addr_txt[200];
+ printf_encode(addr_txt, sizeof(addr_txt),
+ (u8 *) from->sun_path,
+ fromlen -
+ offsetof(struct sockaddr_un, sun_path));
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s",
+ addr_txt);
dl_list_del(&dst->list);
os_free(dst);
return 0;
@@ -121,11 +133,13 @@
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));
+ char addr_txt[200];
dst->debug_level = atoi(level);
+ printf_encode(addr_txt, sizeof(addr_txt),
+ (u8 *) from->sun_path, fromlen -
+ offsetof(struct sockaddr_un, sun_path));
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level to %d for %s",
+ dst->debug_level, addr_txt);
return 0;
}
}
@@ -143,7 +157,7 @@
int res;
struct sockaddr_un from;
socklen_t fromlen = sizeof(from);
- char *reply = NULL;
+ char *reply = NULL, *reply_buf = NULL;
size_t reply_len = 0;
int new_attached = 0;
@@ -177,21 +191,49 @@
else
reply_len = 2;
} else {
- reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
- &reply_len);
+ reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
+ &reply_len);
+ reply = reply_buf;
+ }
+
+ if (!reply && reply_len == 1) {
+ reply = "FAIL\n";
+ reply_len = 5;
+ } else if (!reply && reply_len == 2) {
+ reply = "OK\n";
+ reply_len = 3;
}
if (reply) {
- sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
- fromlen);
- os_free(reply);
- } else if (reply_len == 1) {
- sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
- fromlen);
- } else if (reply_len == 2) {
- sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
- fromlen);
+ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+ fromlen) < 0) {
+ int _errno = errno;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "ctrl_iface sendto failed: %d - %s",
+ _errno, strerror(_errno));
+ if (_errno == ENOBUFS || _errno == EAGAIN) {
+ /*
+ * The socket send buffer could be full. This
+ * may happen if client programs are not
+ * receiving their pending messages. Close and
+ * reopen the socket as a workaround to avoid
+ * getting stuck being unable to send any new
+ * responses.
+ */
+ sock = wpas_ctrl_iface_reinit(wpa_s, priv);
+ if (sock < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reinitialize ctrl_iface socket");
+ }
+ }
+ if (new_attached) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to send response to ATTACH - detaching");
+ new_attached = 0;
+ wpa_supplicant_ctrl_iface_detach(
+ &priv->ctrl_dst, &from, fromlen);
+ }
+ }
}
+ os_free(reply_buf);
if (new_attached)
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
@@ -202,7 +244,7 @@
{
char *buf;
size_t len;
- char *pbuf, *dir = NULL, *gid_str = NULL;
+ char *pbuf, *dir = NULL;
int res;
if (wpa_s->conf->ctrl_interface == NULL)
@@ -212,12 +254,11 @@
if (pbuf == NULL)
return NULL;
if (os_strncmp(pbuf, "DIR=", 4) == 0) {
+ char *gid_str;
dir = pbuf + 4;
gid_str = os_strstr(dir, " GROUP=");
- if (gid_str) {
+ if (gid_str)
*gid_str = '\0';
- gid_str += 7;
- }
} else
dir = pbuf;
@@ -262,26 +303,27 @@
if (global != 2 && wpa_s->global->ctrl_iface) {
struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
if (!dl_list_empty(&priv->ctrl_dst)) {
- wpa_supplicant_ctrl_iface_send(global ? NULL :
+ wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL :
wpa_s->ifname,
priv->sock,
&priv->ctrl_dst,
- level, txt, len);
+ level, txt, len, NULL,
+ priv);
}
}
if (wpa_s->ctrl_iface == NULL)
return;
- wpa_supplicant_ctrl_iface_send(NULL, wpa_s->ctrl_iface->sock,
+ wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
&wpa_s->ctrl_iface->ctrl_dst,
- level, txt, len);
+ level, txt, len, wpa_s->ctrl_iface,
+ NULL);
}
-struct ctrl_iface_priv *
-wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv)
{
- struct ctrl_iface_priv *priv;
struct sockaddr_un addr;
char *fname = NULL;
gid_t gid = 0;
@@ -291,16 +333,6 @@
char *endp;
int flags;
- priv = os_zalloc(sizeof(*priv));
- if (priv == NULL)
- return NULL;
- dl_list_init(&priv->ctrl_dst);
- priv->wpa_s = wpa_s;
- priv->sock = -1;
-
- if (wpa_s->conf->ctrl_interface == NULL)
- return priv;
-
buf = os_strdup(wpa_s->conf->ctrl_interface);
if (buf == NULL)
goto fail;
@@ -476,18 +508,61 @@
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
os_free(buf);
- return priv;
+ return 0;
fail:
- if (priv->sock >= 0)
+ if (priv->sock >= 0) {
close(priv->sock);
- os_free(priv);
+ priv->sock = -1;
+ }
if (fname) {
unlink(fname);
os_free(fname);
}
os_free(buf);
- return NULL;
+ return -1;
+}
+
+
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+ struct ctrl_iface_priv *priv;
+
+ priv = os_zalloc(sizeof(*priv));
+ if (priv == NULL)
+ return NULL;
+ dl_list_init(&priv->ctrl_dst);
+ priv->wpa_s = wpa_s;
+ priv->sock = -1;
+
+ if (wpa_s->conf->ctrl_interface == NULL)
+ return priv;
+
+ if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) {
+ os_free(priv);
+ return NULL;
+ }
+
+ return priv;
+}
+
+
+static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv)
+{
+ int res;
+
+ if (priv->sock <= 0)
+ return -1;
+
+ eloop_unregister_read_sock(priv->sock);
+ close(priv->sock);
+ priv->sock = -1;
+ res = wpas_ctrl_iface_open_sock(wpa_s, priv);
+ if (res < 0)
+ return -1;
+ return priv->sock;
}
@@ -497,7 +572,7 @@
if (priv->sock > -1) {
char *fname;
- char *buf, *dir = NULL, *gid_str = NULL;
+ char *buf, *dir = NULL;
eloop_unregister_read_sock(priv->sock);
if (!dl_list_empty(&priv->ctrl_dst)) {
/*
@@ -517,16 +592,17 @@
os_free(fname);
}
+ if (priv->wpa_s->conf->ctrl_interface == NULL)
+ goto free_dst;
buf = os_strdup(priv->wpa_s->conf->ctrl_interface);
if (buf == NULL)
goto free_dst;
if (os_strncmp(buf, "DIR=", 4) == 0) {
+ char *gid_str;
dir = buf + 4;
gid_str = os_strstr(dir, " GROUP=");
- if (gid_str) {
+ if (gid_str)
*gid_str = '\0';
- gid_str += 7;
- }
} else
dir = buf;
@@ -563,10 +639,13 @@
*
* Send a packet to all monitor programs attached to the control interface.
*/
-static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock,
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+ const char *ifname, int sock,
struct dl_list *ctrl_dst,
int level, const char *buf,
- size_t len)
+ size_t len,
+ struct ctrl_iface_priv *priv,
+ struct ctrl_iface_global_priv *gp)
{
struct wpa_ctrl_dst *dst, *next;
char levelstr[10];
@@ -602,31 +681,58 @@
msg.msg_iov = io;
msg.msg_iovlen = idx;
- idx = 0;
dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
- if (level >= dst->debug_level) {
- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
- (u8 *) dst->addr.sun_path, dst->addrlen -
- offsetof(struct sockaddr_un, sun_path));
- msg.msg_name = (void *) &dst->addr;
- msg.msg_namelen = dst->addrlen;
- if (sendmsg(sock, &msg, MSG_DONTWAIT) < 0) {
- int _errno = errno;
- wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
- "%d - %s",
- idx, errno, strerror(errno));
- dst->errors++;
- if (dst->errors > 1000 ||
- (_errno != ENOBUFS && dst->errors > 10) ||
- _errno == ENOENT) {
- wpa_supplicant_ctrl_iface_detach(
- ctrl_dst, &dst->addr,
- dst->addrlen);
- }
- } else
- dst->errors = 0;
+ int _errno;
+ char addr_txt[200];
+
+ if (level < dst->debug_level)
+ continue;
+
+ printf_encode(addr_txt, sizeof(addr_txt),
+ (u8 *) dst->addr.sun_path, dst->addrlen -
+ offsetof(struct sockaddr_un, sun_path));
+ msg.msg_name = (void *) &dst->addr;
+ msg.msg_namelen = dst->addrlen;
+ if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s",
+ addr_txt);
+ dst->errors = 0;
+ continue;
}
- idx++;
+
+ _errno = errno;
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s",
+ addr_txt, errno, strerror(errno));
+ dst->errors++;
+
+ if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) {
+ wpa_printf(MSG_INFO, "CTRL_IFACE: Detach monitor %s that cannot receive messages",
+ addr_txt);
+ wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr,
+ dst->addrlen);
+ }
+
+ if (_errno == ENOBUFS || _errno == EAGAIN) {
+ /*
+ * The socket send buffer could be full. This may happen
+ * if client programs are not receiving their pending
+ * messages. Close and reopen the socket as a workaround
+ * to avoid getting stuck being unable to send any new
+ * responses.
+ */
+ if (priv)
+ sock = wpas_ctrl_iface_reinit(wpa_s, priv);
+ else if (gp)
+ sock = wpas_ctrl_iface_global_reinit(
+ wpa_s->global, gp);
+ else
+ break;
+ if (sock < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Failed to reinitialize ctrl_iface socket");
+ break;
+ }
+ }
}
}
@@ -656,18 +762,30 @@
/* handle ATTACH signal of first monitor interface */
if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
&from, fromlen)) {
- sendto(priv->sock, "OK\n", 3, 0,
- (struct sockaddr *) &from, fromlen);
+ if (sendto(priv->sock, "OK\n", 3, 0,
+ (struct sockaddr *) &from, fromlen) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+ strerror(errno));
+ }
/* OK to continue */
return;
} else {
- sendto(priv->sock, "FAIL\n", 5, 0,
- (struct sockaddr *) &from, fromlen);
+ if (sendto(priv->sock, "FAIL\n", 5, 0,
+ (struct sockaddr *) &from, fromlen) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+ strerror(errno));
+ }
}
} else {
/* return FAIL for all other signals */
- sendto(priv->sock, "FAIL\n", 5, 0,
- (struct sockaddr *) &from, fromlen);
+ if (sendto(priv->sock, "FAIL\n", 5, 0,
+ (struct sockaddr *) &from, fromlen) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "ctrl_iface sendto failed: %s",
+ strerror(errno));
+ }
}
}
}
@@ -680,11 +798,11 @@
{
struct wpa_global *global = eloop_ctx;
struct ctrl_iface_global_priv *priv = sock_ctx;
- char buf[256];
+ char buf[4096];
int res;
struct sockaddr_un from;
socklen_t fromlen = sizeof(from);
- char *reply = NULL;
+ char *reply = NULL, *reply_buf = NULL;
size_t reply_len;
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
@@ -709,41 +827,37 @@
else
reply_len = 2;
} else {
- reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
- &reply_len);
+ reply_buf = wpa_supplicant_global_ctrl_iface_process(
+ global, buf, &reply_len);
+ reply = reply_buf;
+ }
+
+ if (!reply && reply_len == 1) {
+ reply = "FAIL\n";
+ reply_len = 5;
+ } else if (!reply && reply_len == 2) {
+ reply = "OK\n";
+ reply_len = 3;
}
if (reply) {
- sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
- fromlen);
- os_free(reply);
- } else if (reply_len == 1) {
- sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
- fromlen);
- } else if (reply_len == 2) {
- sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, fromlen);
+ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+ fromlen) < 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+ strerror(errno));
+ }
}
+ os_free(reply_buf);
}
-struct ctrl_iface_global_priv *
-wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global,
+ struct ctrl_iface_global_priv *priv)
{
- struct ctrl_iface_global_priv *priv;
struct sockaddr_un addr;
const char *ctrl = global->params.ctrl_interface;
int flags;
- priv = os_zalloc(sizeof(*priv));
- if (priv == NULL)
- return NULL;
- dl_list_init(&priv->ctrl_dst);
- priv->global = global;
- priv->sock = -1;
-
- if (ctrl == NULL)
- return priv;
-
wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl);
#ifdef ANDROID
@@ -876,7 +990,13 @@
goto fail;
}
} else {
- chmod(ctrl, S_IRWXU);
+ if (chmod(ctrl, S_IRWXU) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "chmod[global_ctrl_interface=%s](S_IRWXU): %s",
+ ctrl, strerror(errno));
+ /* continue anyway since group change was not required
+ */
+ }
}
havesock:
@@ -899,13 +1019,58 @@
wpa_supplicant_global_ctrl_iface_receive,
global, priv);
- return priv;
+ return 0;
fail:
- if (priv->sock >= 0)
+ if (priv->sock >= 0) {
close(priv->sock);
- os_free(priv);
- return NULL;
+ priv->sock = -1;
+ }
+ return -1;
+}
+
+
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+ struct ctrl_iface_global_priv *priv;
+
+ priv = os_zalloc(sizeof(*priv));
+ if (priv == NULL)
+ return NULL;
+ dl_list_init(&priv->ctrl_dst);
+ priv->global = global;
+ priv->sock = -1;
+
+ if (global->params.ctrl_interface == NULL)
+ return priv;
+
+ if (wpas_global_ctrl_iface_open_sock(global, priv) < 0) {
+ os_free(priv);
+ return NULL;
+ }
+
+ wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+ return priv;
+}
+
+
+static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
+ struct ctrl_iface_global_priv *priv)
+{
+ int res;
+
+ if (priv->sock <= 0)
+ return -1;
+
+ eloop_unregister_read_sock(priv->sock);
+ close(priv->sock);
+ priv->sock = -1;
+ res = wpas_global_ctrl_iface_open_sock(global, priv);
+ if (res < 0)
+ return -1;
+ return priv->sock;
}
diff --git a/wpa_supplicant/dbus/Makefile b/wpa_supplicant/dbus/Makefile
index d64c65c..f355ebe 100644
--- a/wpa_supplicant/dbus/Makefile
+++ b/wpa_supplicant/dbus/Makefile
@@ -1,7 +1,7 @@
all: libwpadbus.a
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
rm -f libwpadbus.a
install:
diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c
index 6caf740..5cc1505 100644
--- a/wpa_supplicant/dbus/dbus_common.c
+++ b/wpa_supplicant/dbus/dbus_common.c
@@ -320,6 +320,8 @@
if (priv->con) {
eloop_cancel_timeout(dispatch_initial_dbus_messages,
priv->con, NULL);
+ eloop_cancel_timeout(process_timeout, priv, ELOOP_ALL_CTX);
+
dbus_connection_set_watch_functions(priv->con, NULL, NULL,
NULL, NULL, NULL);
dbus_connection_set_timeout_functions(priv->con, NULL, NULL,
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index 61a9430..949ce7c 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -881,6 +881,8 @@
}
dbus_message_iter_recurse(iter, &iter_array);
+ os_memset(&tmpentry, 0, sizeof(tmpentry));
+ tmpentry.type = DBUS_TYPE_ARRAY;
if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry)
== FALSE)
goto cleanup;
@@ -932,6 +934,7 @@
break;
case DBUS_TYPE_ARRAY:
success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry);
+ break;
default:
break;
}
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index ddd2c82..e9f6589 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -24,6 +24,7 @@
#include "dbus_common_i.h"
#include "dbus_new_handlers_p2p.h"
#include "p2p/p2p.h"
+#include "../p2p_supplicant.h"
#ifdef CONFIG_AP /* until needed by something else */
@@ -709,9 +710,9 @@
DBusMessage *msg;
DBusMessageIter iter, dict_iter;
struct wpas_dbus_priv *iface;
- char *auth_type[6]; /* we have six possible authorization types */
+ char *auth_type[5]; /* we have five possible authentication types */
int at_num = 0;
- char *encr_type[4]; /* we have four possible encryption types */
+ char *encr_type[3]; /* we have three possible encryption types */
int et_num = 0;
iface = wpa_s->global->dbus;
@@ -734,20 +735,15 @@
auth_type[at_num++] = "open";
if (cred->auth_type & WPS_AUTH_WPAPSK)
auth_type[at_num++] = "wpa-psk";
- if (cred->auth_type & WPS_AUTH_SHARED)
- auth_type[at_num++] = "shared";
if (cred->auth_type & WPS_AUTH_WPA)
auth_type[at_num++] = "wpa-eap";
if (cred->auth_type & WPS_AUTH_WPA2)
auth_type[at_num++] = "wpa2-eap";
if (cred->auth_type & WPS_AUTH_WPA2PSK)
- auth_type[at_num++] =
- "wpa2-psk";
+ auth_type[at_num++] = "wpa2-psk";
if (cred->encr_type & WPS_ENCR_NONE)
encr_type[et_num++] = "none";
- if (cred->encr_type & WPS_ENCR_WEP)
- encr_type[et_num++] = "wep";
if (cred->encr_type & WPS_ENCR_TKIP)
encr_type[et_num++] = "tkip";
if (cred->encr_type & WPS_ENCR_AES)
@@ -950,37 +946,49 @@
void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
const char *role)
{
-
+ int error = 1;
DBusMessage *msg;
- DBusMessageIter iter;
+ DBusMessageIter iter, dict_iter;
struct wpas_dbus_priv *iface = wpa_s->global->dbus;
- char *ifname = wpa_s->ifname;
/* Do nothing if the control interface is not turned on */
if (iface == NULL)
return;
- msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
WPAS_DBUS_NEW_IFACE_P2PDEVICE,
"GroupFinished");
if (msg == NULL)
return;
dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto nomem;
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ifname)) {
- wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
- "signal -not enough memory for ifname ");
- goto err;
- }
+ if (!wpa_dbus_dict_append_object_path(&dict_iter,
+ "interface_object",
+ wpa_s->dbus_new_path))
+ goto nomem;
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &role))
- wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
- "signal -not enough memory for role ");
- else
- dbus_connection_send(iface->con, msg, NULL);
+ if (!wpa_dbus_dict_append_string(&dict_iter, "role", role))
+ goto nomem;
-err:
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+ wpa_s->dbus_groupobj_path) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto nomem;
+
+ error = 0;
+ dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+ if (error > 0)
+ wpa_printf(MSG_ERROR,
+ "dbus: Failed to construct GroupFinished");
+
dbus_message_unref(msg);
}
@@ -1156,6 +1164,69 @@
}
+struct group_changed_data {
+ struct wpa_supplicant *wpa_s;
+ struct p2p_peer_info *info;
+};
+
+
+static int match_group_where_peer_is_client(struct p2p_group *group,
+ void *user_data)
+{
+ struct group_changed_data *data = user_data;
+ const struct p2p_group_config *cfg;
+ struct wpa_supplicant *wpa_s_go;
+
+ if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr))
+ return 1;
+
+ cfg = p2p_group_get_config(group);
+
+ wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid,
+ cfg->ssid_len);
+ if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) {
+ wpas_dbus_signal_peer_groups_changed(
+ data->wpa_s->parent, data->info->p2p_device_addr);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void signal_peer_groups_changed(struct p2p_peer_info *info,
+ void *user_data)
+{
+ struct group_changed_data *data = user_data;
+ struct wpa_supplicant *wpa_s_go;
+
+ wpa_s_go = wpas_get_p2p_client_iface(data->wpa_s,
+ info->p2p_device_addr);
+ if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) {
+ wpas_dbus_signal_peer_groups_changed(data->wpa_s->parent,
+ info->p2p_device_addr);
+ return;
+ }
+
+ data->info = info;
+ p2p_loop_on_all_groups(data->wpa_s->global->p2p,
+ match_group_where_peer_is_client, data);
+ data->info = NULL;
+}
+
+
+static void peer_groups_changed(struct wpa_supplicant *wpa_s)
+{
+ struct group_changed_data data;
+
+ os_memset(&data, 0, sizeof(data));
+ data.wpa_s = wpa_s;
+
+ p2p_loop_on_known_peers(wpa_s->global->p2p,
+ signal_peer_groups_changed, &data);
+}
+
+
/**
* wpas_dbus_signal_p2p_group_started - Signals P2P group has
* started. Emitted when a group is successfully started
@@ -1174,7 +1245,6 @@
DBusMessage *msg;
DBusMessageIter iter, dict_iter;
struct wpas_dbus_priv *iface;
- char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
iface = wpa_s->parent->global->dbus;
@@ -1182,14 +1252,13 @@
if (iface == NULL)
return;
- if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+ if (wpa_s->dbus_groupobj_path == NULL)
return;
/* New interface has been created for this group */
msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
WPAS_DBUS_NEW_IFACE_P2PDEVICE,
"GroupStarted");
-
if (msg == NULL)
return;
@@ -1212,12 +1281,15 @@
goto nomem;
if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
- group_obj_path) ||
+ wpa_s->dbus_groupobj_path) ||
!wpa_dbus_dict_close_write(&iter, &dict_iter))
goto nomem;
dbus_connection_send(iface->con, msg, NULL);
+ if (client)
+ peer_groups_changed(wpa_s);
+
nomem:
dbus_message_unref(msg);
}
@@ -1225,7 +1297,7 @@
/**
*
- * Method to emit GONeogtiation Success or Failure signals based
+ * Method to emit GONegotiation Success or Failure signals based
* on status.
* @status: Status of the GO neg request. 0 for success, other for errors.
*/
@@ -1362,7 +1434,7 @@
DBusMessageIter iter, dict_iter;
struct wpas_dbus_priv *iface;
- wpa_printf(MSG_INFO, "%s\n", __func__);
+ wpa_printf(MSG_DEBUG, "%s", __func__);
iface = wpa_s->global->dbus;
/* Do nothing if the control interface is not turned on */
@@ -1405,15 +1477,15 @@
* constructed using p2p i/f addr used for connecting.
*
* @wpa_s: %wpa_supplicant network interface data
- * @member_addr: addr (p2p i/f) of the peer joining the group
+ * @peer_addr: P2P Device Address of the peer joining the group
*/
void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
- const u8 *member)
+ const u8 *peer_addr)
{
struct wpas_dbus_priv *iface;
DBusMessage *msg;
DBusMessageIter iter;
- char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
iface = wpa_s->global->dbus;
@@ -1424,10 +1496,10 @@
if (!wpa_s->dbus_groupobj_path)
return;
- os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
COMPACT_MACSTR,
- wpa_s->dbus_groupobj_path, MAC2STR(member));
+ wpa_s->parent->dbus_new_path, MAC2STR(peer_addr));
msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
WPAS_DBUS_NEW_IFACE_P2P_GROUP,
@@ -1436,14 +1508,16 @@
return;
dbus_message_iter_init_append(msg, &iter);
- path = groupmember_obj_path;
+ path = peer_obj_path;
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
&path))
goto err;
dbus_connection_send(iface->con, msg, NULL);
-
dbus_message_unref(msg);
+
+ wpas_dbus_signal_peer_groups_changed(wpa_s->parent, peer_addr);
+
return;
err:
@@ -1456,18 +1530,18 @@
*
* Method to emit a signal for a peer disconnecting the group.
* The signal will carry path to the group member object
- * constructed using p2p i/f addr used for connecting.
+ * constructed using the P2P Device Address of the peer.
*
* @wpa_s: %wpa_supplicant network interface data
- * @member_addr: addr (p2p i/f) of the peer joining the group
+ * @peer_addr: P2P Device Address of the peer joining the group
*/
void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
- const u8 *member)
+ const u8 *peer_addr)
{
struct wpas_dbus_priv *iface;
DBusMessage *msg;
DBusMessageIter iter;
- char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
iface = wpa_s->global->dbus;
@@ -1478,10 +1552,10 @@
if (!wpa_s->dbus_groupobj_path)
return;
- os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
COMPACT_MACSTR,
- wpa_s->dbus_groupobj_path, MAC2STR(member));
+ wpa_s->dbus_groupobj_path, MAC2STR(peer_addr));
msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
WPAS_DBUS_NEW_IFACE_P2P_GROUP,
@@ -1490,14 +1564,16 @@
return;
dbus_message_iter_init_append(msg, &iter);
- path = groupmember_obj_path;
+ path = peer_obj_path;
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
&path))
goto err;
dbus_connection_send(iface->con, msg, NULL);
-
dbus_message_unref(msg);
+
+ wpas_dbus_signal_peer_groups_changed(wpa_s->parent, peer_addr);
+
return;
err:
@@ -2462,6 +2538,12 @@
END_ARGS
}
},
+ { "Reattach", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_reattach,
+ {
+ END_ARGS
+ }
+ },
{ "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
(WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
{
@@ -2516,6 +2598,15 @@
}
},
#endif /* CONFIG_NO_CONFIG_BLOBS */
+ { "SetPKCS11EngineAndModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler)
+ &wpas_dbus_handler_set_pkcs11_engine_and_module_path,
+ {
+ { "pkcs11_engine_path", "s", ARG_IN },
+ { "pkcs11_module_path", "s", ARG_IN },
+ END_ARGS
+ }
+ },
#ifdef CONFIG_WPS
{ "Start", WPAS_DBUS_NEW_IFACE_WPS,
(WPADBusMethodHandler) &wpas_dbus_handler_wps_start,
@@ -2634,6 +2725,7 @@
(WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req,
{
{ "args", "a{sv}", ARG_IN },
+ { "ref", "t", ARG_OUT },
END_ARGS
}
},
@@ -2664,13 +2756,6 @@
END_ARGS
}
},
- { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
- (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
- {
- { "arg", "i", ARG_IN },
- END_ARGS
- }
- },
{ "AddPersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
(WPADBusMethodHandler) wpas_dbus_handler_add_persistent_group,
{
@@ -2736,6 +2821,37 @@
}
},
#endif /* CONFIG_AUTOSCAN */
+#ifdef CONFIG_TDLS
+ { "TDLSDiscover", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_tdls_discover,
+ {
+ { "peer_address", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "TDLSSetup", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_tdls_setup,
+ {
+ { "peer_address", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "TDLSStatus", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_tdls_status,
+ {
+ { "peer_address", "s", ARG_IN },
+ { "status", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "TDLSTeardown", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_tdls_teardown,
+ {
+ { "peer_address", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_TDLS */
{ NULL, NULL, NULL, { END_ARGS } }
};
@@ -2812,6 +2928,14 @@
wpas_dbus_getter_scan_interval,
wpas_dbus_setter_scan_interval
},
+ { "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_pkcs11_engine_path,
+ NULL
+ },
+ { "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_pkcs11_module_path,
+ NULL
+ },
#ifdef CONFIG_WPS
{ "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
wpas_dbus_getter_process_credentials,
@@ -2941,7 +3065,6 @@
{ "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
{ "path", "o", ARG_OUT },
- { "properties", "a{sv}", ARG_OUT },
END_ARGS
}
},
@@ -3004,12 +3127,13 @@
},
{ "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
+ { "properties", "a{sv}", ARG_OUT },
END_ARGS
}
},
{ "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
- { "status", "i", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
END_ARGS
}
},
@@ -3028,8 +3152,7 @@
},
{ "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
- { "ifname", "s", ARG_OUT },
- { "role", "s", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
END_ARGS
}
},
@@ -3226,11 +3349,25 @@
wpas_dbus_getter_p2p_peer_ies,
NULL
},
+ { "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+ wpas_dbus_getter_p2p_peer_device_address,
+ NULL
+ },
+ { "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao",
+ wpas_dbus_getter_p2p_peer_groups,
+ NULL
+ },
{ NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
-
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+ { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_P2P_PEER,
+ {
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
{ NULL, NULL, { END_ARGS } }
};
@@ -3414,6 +3551,20 @@
}
+void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+ wpa_dbus_mark_property_changed(wpa_s->global->dbus, peer_obj_path,
+ WPAS_DBUS_NEW_IFACE_P2P_PEER, "Groups");
+}
+
+
static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = {
{ "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao",
wpas_dbus_getter_p2p_group_members,
@@ -3559,6 +3710,8 @@
return;
}
+ peer_groups_changed(wpa_s);
+
wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'",
wpa_s->dbus_groupobj_path);
@@ -3570,109 +3723,6 @@
}
static const struct wpa_dbus_property_desc
-wpas_dbus_p2p_groupmember_properties[] = {
- { NULL, NULL, NULL, NULL, NULL }
-};
-
-/**
- * wpas_dbus_register_p2p_groupmember - Register a p2p groupmember
- * object with dbus
- * @wpa_s: wpa_supplicant interface structure
- * @p2p_if_addr: i/f addr of the device joining this group
- *
- * Registers p2p groupmember representing object with dbus
- */
-void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
- const u8 *p2p_if_addr)
-{
- struct wpas_dbus_priv *ctrl_iface;
- struct wpa_dbus_object_desc *obj_desc = NULL;
- struct groupmember_handler_args *arg;
- char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-
- /* Do nothing if the control interface is not turned on */
- if (wpa_s == NULL || wpa_s->global == NULL)
- return;
-
- ctrl_iface = wpa_s->global->dbus;
- if (ctrl_iface == NULL)
- return;
-
- if (!wpa_s->dbus_groupobj_path)
- return;
-
- os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
- wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
-
- obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
- if (!obj_desc) {
- wpa_printf(MSG_ERROR, "Not enough memory "
- "to create object description");
- goto err;
- }
-
- /* allocate memory for handlers arguments */
- arg = os_zalloc(sizeof(struct groupmember_handler_args));
- if (!arg) {
- wpa_printf(MSG_ERROR, "Not enough memory "
- "to create arguments for method");
- goto err;
- }
-
- arg->wpa_s = wpa_s;
- os_memcpy(arg->member_addr, p2p_if_addr, ETH_ALEN);
-
- wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
- wpas_dbus_p2p_groupmember_properties, NULL);
-
- if (wpa_dbus_register_object_per_iface(ctrl_iface, groupmember_obj_path,
- wpa_s->ifname, obj_desc))
- goto err;
-
- wpa_printf(MSG_INFO,
- "dbus: Registered group member object '%s' successfully",
- groupmember_obj_path);
- return;
-
-err:
- free_dbus_object_desc(obj_desc);
-}
-
-/**
- * wpas_dbus_unregister_p2p_groupmember - Unregister a p2p groupmember
- * object with dbus
- * @wpa_s: wpa_supplicant interface structure
- * @p2p_if_addr: i/f addr of the device joining this group
- *
- * Unregisters p2p groupmember representing object with dbus
- */
-void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
- const u8 *p2p_if_addr)
-{
- struct wpas_dbus_priv *ctrl_iface;
- char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-
- /* Do nothing if the control interface is not turned on */
- if (wpa_s == NULL || wpa_s->global == NULL)
- return;
-
- ctrl_iface = wpa_s->global->dbus;
- if (ctrl_iface == NULL)
- return;
-
- if (!wpa_s->dbus_groupobj_path)
- return;
-
- os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
- wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
-
- wpa_dbus_unregister_object_per_iface(ctrl_iface, groupmember_obj_path);
-}
-
-
-static const struct wpa_dbus_property_desc
wpas_dbus_persistent_group_properties[] = {
{ "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}",
wpas_dbus_getter_persistent_group_properties,
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 61c480a..881d351 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -79,11 +79,7 @@
#define WPAS_DBUS_NEW_P2P_PEERS_PART "Peers"
#define WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer"
-#define WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "Members"
-#define WPAS_DBUS_NEW_IFACE_P2P_GROUPMEMBER \
- WPAS_DBUS_NEW_INTERFACE ".GroupMember"
-
-/* Errors */
+/* Top-level Errors */
#define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
WPAS_DBUS_NEW_INTERFACE ".UnknownError"
#define WPAS_DBUS_ERROR_INVALID_ARGS \
@@ -91,6 +87,8 @@
#define WPAS_DBUS_ERROR_IFACE_EXISTS \
WPAS_DBUS_NEW_INTERFACE ".InterfaceExists"
+#define WPAS_DBUS_ERROR_IFACE_DISABLED \
+ WPAS_DBUS_NEW_INTERFACE ".InterfaceDisabled"
#define WPAS_DBUS_ERROR_IFACE_UNKNOWN \
WPAS_DBUS_NEW_INTERFACE ".InterfaceUnknown"
@@ -118,6 +116,9 @@
#define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \
WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou"
+/* Interface-level errors */
+#define WPAS_DBUS_ERROR_IFACE_SCAN_ERROR \
+ WPAS_DBUS_NEW_IFACE_INTERFACE ".ScanError"
void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv);
void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv);
@@ -172,6 +173,8 @@
const u8 *dev_addr);
void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
const u8 *dev_addr);
+void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
const char *role);
void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
@@ -196,10 +199,6 @@
int nid);
void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
int status, const u8 *bssid);
-void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
- const u8 *p2p_if_addr);
-void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
- const u8 *p2p_if_addr);
void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
const u8 *member);
void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
@@ -355,6 +354,12 @@
}
static inline void
+wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+}
+
+static inline void
wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
const char *role)
{
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 478d02f..6dc5f76 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -28,10 +28,6 @@
#include "dbus_dict_helpers.h"
#include "dbus_common_i.h"
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
static const char *debug_strings[] = {
"excessive", "msgdump", "debug", "info", "warning", "error", NULL
};
@@ -120,6 +116,27 @@
}
+/**
+ * wpas_dbus_error_scan_error - Return a new ScanError error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * @error: Optional string to be used as the error message
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return a scan error
+ */
+DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message,
+ const char *error)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_error(message,
+ WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
+ error);
+
+ return reply;
+}
+
+
static const char *dont_quote[] = {
"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
@@ -254,6 +271,7 @@
wpa_config_update_prio_list(wpa_s->conf);
os_free(value);
+ value = NULL;
wpa_dbus_dict_entry_clear(&entry);
}
@@ -422,8 +440,13 @@
}
for (i = 0; i < array_len; i++) {
- dbus_message_iter_append_basic(&array_iter, type,
- array + i * element_size);
+ if (!dbus_message_iter_append_basic(&array_iter, type,
+ array + i * element_size)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 2.5",
+ __func__);
+ return FALSE;
+ }
}
if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
@@ -489,7 +512,7 @@
return FALSE;
}
- for (i = 0; i < array_len; i++) {
+ for (i = 0; i < array_len && array[i]; i++) {
wpa_dbus_dict_bin_array_add_element(&array_iter,
wpabuf_head(array[i]),
wpabuf_len(array[i]));
@@ -544,24 +567,28 @@
goto error;
if (!os_strcmp(entry.key, "Driver") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(driver);
driver = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (driver == NULL)
goto error;
} else if (!os_strcmp(entry.key, "Ifname") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(ifname);
ifname = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (ifname == NULL)
goto error;
} else if (!os_strcmp(entry.key, "ConfigFile") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(confname);
confname = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (confname == NULL)
goto error;
} else if (!os_strcmp(entry.key, "BridgeIfname") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(bridge_ifname);
bridge_ifname = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (bridge_ifname == NULL)
@@ -1236,6 +1263,23 @@
}
+static int wpas_dbus_get_scan_allow_roam(DBusMessage *message,
+ DBusMessageIter *var,
+ dbus_bool_t *allow,
+ DBusMessage **reply)
+{
+ if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) {
+ wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+ "Type must be a boolean");
+ *reply = wpas_dbus_error_invalid_args(
+ message, "Wrong Type value type. Boolean required");
+ return -1;
+ }
+ dbus_message_iter_get_basic(var, allow);
+ return 0;
+}
+
+
/**
* wpas_dbus_handler_scan - Request a wireless scan on an interface
* @message: Pointer to incoming dbus message
@@ -1254,6 +1298,7 @@
char *key = NULL, *type = NULL;
struct wpa_driver_scan_params params;
size_t i;
+ dbus_bool_t allow_roam = 1;
os_memset(¶ms, 0, sizeof(params));
@@ -1284,6 +1329,12 @@
if (wpas_dbus_get_scan_channels(message, &variant_iter,
¶ms, &reply) < 0)
goto out;
+ } else if (os_strcmp(key, "AllowRoam") == 0) {
+ if (wpas_dbus_get_scan_allow_roam(message,
+ &variant_iter,
+ &allow_roam,
+ &reply) < 0)
+ goto out;
} else {
wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
"Unknown argument %s", key);
@@ -1310,7 +1361,10 @@
"passive scan");
goto out;
} else if (params.freqs && params.freqs[0]) {
- wpa_supplicant_trigger_scan(wpa_s, ¶ms);
+ if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) {
+ reply = wpas_dbus_error_scan_error(
+ message, "Scan request rejected");
+ }
} else {
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
@@ -1323,7 +1377,10 @@
#ifdef CONFIG_AUTOSCAN
autoscan_deinit(wpa_s);
#endif /* CONFIG_AUTOSCAN */
- wpa_supplicant_trigger_scan(wpa_s, ¶ms);
+ if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) {
+ reply = wpas_dbus_error_scan_error(
+ message, "Scan request rejected");
+ }
} else {
wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
"Unknown scan type: %s", type);
@@ -1332,6 +1389,9 @@
goto out;
}
+ if (!allow_roam)
+ wpa_s->scan_res_handler = scan_only_handler;
+
out:
for (i = 0; i < WPAS_MAX_SCAN_SSIDS; i++)
os_free((u8 *) params.ssids[i].ssid);
@@ -1442,10 +1502,10 @@
/**
- * wpas_dbus_handler_reassociate - Reassociate to current AP
+ * wpas_dbus_handler_reassociate - Reassociate
* @message: Pointer to incoming dbus message
* @wpa_s: wpa_supplicant structure for a network interface
- * Returns: NotConnected DBus error message if not connected
+ * Returns: InterfaceDisabled DBus error message if disabled
* or NULL otherwise.
*
* Handler function for "Reassociate" method call of network interface.
@@ -1453,7 +1513,30 @@
DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
struct wpa_supplicant *wpa_s)
{
+ if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) {
+ wpas_request_connection(wpa_s);
+ return NULL;
+ }
+
+ return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_DISABLED,
+ "This interface is disabled");
+}
+
+
+/**
+ * wpas_dbus_handler_reattach - Reattach to current AP
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NotConnected DBus error message if not connected
+ * or NULL otherwise.
+ *
+ * Handler function for "Reattach" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
if (wpa_s->current_ssid != NULL) {
+ wpa_s->reattach = 1;
wpas_request_connection(wpa_s);
return NULL;
}
@@ -1510,16 +1593,6 @@
wpas_notify_network_removed(wpa_s, ssid);
- if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
- wpa_printf(MSG_ERROR,
- "wpas_dbus_handler_remove_network[dbus]: "
- "error occurred when removing network %d", id);
- reply = wpas_dbus_error_unknown_error(
- message, "error removing the specified network on "
- "this interface.");
- goto out;
- }
-
if (ssid == wpa_s->current_ssid)
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
@@ -1530,6 +1603,15 @@
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
+ if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+ wpa_printf(MSG_ERROR,
+ "wpas_dbus_handler_remove_network[dbus]: "
+ "error occurred when removing network %d", id);
+ reply = wpas_dbus_error_unknown_error(
+ message, "error removing the specified network on "
+ "this interface.");
+ goto out;
+ }
out:
os_free(iface);
@@ -1970,6 +2052,227 @@
}
+#ifdef CONFIG_TDLS
+
+static int get_peer_hwaddr_helper(DBusMessage *message, const char *func_name,
+ u8 *peer_address, DBusMessage **error)
+{
+ const char *peer_string;
+
+ *error = NULL;
+
+ if (!dbus_message_get_args(message, NULL,
+ DBUS_TYPE_STRING, &peer_string,
+ DBUS_TYPE_INVALID)) {
+ *error = wpas_dbus_error_invalid_args(message, NULL);
+ return -1;
+ }
+
+ if (hwaddr_aton(peer_string, peer_address)) {
+ wpa_printf(MSG_DEBUG, "%s: invalid address '%s'",
+ func_name, peer_string);
+ *error = wpas_dbus_error_invalid_args(
+ message, "Invalid hardware address format");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_discover - Discover TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSDiscover" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ u8 peer[ETH_ALEN];
+ DBusMessage *error_reply;
+ int ret;
+
+ if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+ return error_reply;
+
+ wpa_printf(MSG_DEBUG, "DBUS TDLS_DISCOVER " MACSTR, MAC2STR(peer));
+
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
+
+ if (ret) {
+ return wpas_dbus_error_unknown_error(
+ message, "error performing TDLS discovery");
+ }
+
+ return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_setup - Setup TDLS session
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSSetup" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ u8 peer[ETH_ALEN];
+ DBusMessage *error_reply;
+ int ret;
+
+ if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+ return error_reply;
+
+ wpa_printf(MSG_DEBUG, "DBUS TDLS_SETUP " MACSTR, MAC2STR(peer));
+
+ wpa_tdls_remove(wpa_s->wpa, peer);
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_start(wpa_s->wpa, peer);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+
+ if (ret) {
+ return wpas_dbus_error_unknown_error(
+ message, "error performing TDLS setup");
+ }
+
+ return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_status - Return TDLS session status
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A string representing the state of the link to this TDLS peer
+ *
+ * Handler function for "TDLSStatus" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ u8 peer[ETH_ALEN];
+ DBusMessage *reply;
+ const char *tdls_status;
+
+ if (get_peer_hwaddr_helper(message, __func__, peer, &reply) < 0)
+ return reply;
+
+ wpa_printf(MSG_DEBUG, "DBUS TDLS_STATUS " MACSTR, MAC2STR(peer));
+
+ tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
+
+ reply = dbus_message_new_method_return(message);
+ dbus_message_append_args(reply, DBUS_TYPE_STRING,
+ &tdls_status, DBUS_TYPE_INVALID);
+ return reply;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_teardown - Teardown TDLS session
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSTeardown" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ u8 peer[ETH_ALEN];
+ DBusMessage *error_reply;
+ int ret;
+
+ if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+ return error_reply;
+
+ wpa_printf(MSG_DEBUG, "DBUS TDLS_TEARDOWN " MACSTR, MAC2STR(peer));
+
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_teardown_link(
+ wpa_s->wpa, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+
+ if (ret) {
+ return wpas_dbus_error_unknown_error(
+ message, "error performing TDLS teardown");
+ }
+
+ return NULL;
+}
+
+#endif /* CONFIG_TDLS */
+
+
+/**
+ * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing an error on failure or NULL on success
+ *
+ * Sets the PKCS #11 engine and module path.
+ */
+DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ char *value = NULL;
+ char *pkcs11_engine_path = NULL;
+ char *pkcs11_module_path = NULL;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &value);
+ if (value == NULL) {
+ return dbus_message_new_error(
+ message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid pkcs11_engine_path argument");
+ }
+ /* Empty path defaults to NULL */
+ if (os_strlen(value))
+ pkcs11_engine_path = value;
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, &value);
+ if (value == NULL) {
+ os_free(pkcs11_engine_path);
+ return dbus_message_new_error(
+ message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid pkcs11_module_path argument");
+ }
+ /* Empty path defaults to NULL */
+ if (os_strlen(value))
+ pkcs11_module_path = value;
+
+ if (wpas_set_pkcs11_engine_and_module_path(wpa_s, pkcs11_engine_path,
+ pkcs11_module_path))
+ return dbus_message_new_error(
+ message, DBUS_ERROR_FAILED,
+ "Reinit of the EAPOL state machine with the new PKCS "
+ "#11 engine and module path failed.");
+
+ wpa_dbus_mark_property_changed(
+ wpa_s->global->dbus, wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath");
+ wpa_dbus_mark_property_changed(
+ wpa_s->global->dbus, wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath");
+
+ return NULL;
+}
+
+
/**
* wpas_dbus_getter_capabilities - Return interface capabilities
* @iter: Pointer to incoming dbus message iter
@@ -2003,7 +2306,7 @@
const char *args[] = {"ccmp", "tkip", "none"};
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "Pairwise", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto nomem;
} else {
if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise",
@@ -2012,6 +2315,18 @@
&iter_array))
goto nomem;
+ if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "ccmp-256"))
+ goto nomem;
+ }
+
+ if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "gcmp-256"))
+ goto nomem;
+ }
+
if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
if (!wpa_dbus_dict_string_array_add_element(
&iter_array, "ccmp"))
@@ -2050,7 +2365,7 @@
};
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "Group", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto nomem;
} else {
if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group",
@@ -2059,6 +2374,18 @@
&iter_array))
goto nomem;
+ if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "ccmp-256"))
+ goto nomem;
+ }
+
+ if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "gcmp-256"))
+ goto nomem;
+ }
+
if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
if (!wpa_dbus_dict_string_array_add_element(
&iter_array, "ccmp"))
@@ -2107,7 +2434,7 @@
};
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "KeyMgmt", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto nomem;
} else {
if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt",
@@ -2187,7 +2514,7 @@
const char *args[] = { "rsn", "wpa" };
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "Protocol", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto nomem;
} else {
if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol",
@@ -2222,7 +2549,7 @@
const char *args[] = { "open", "shared", "leap" };
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "AuthAlg", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto nomem;
} else {
if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg",
@@ -2258,7 +2585,7 @@
/***** Scan */
if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans,
- sizeof(scans) / sizeof(char *)))
+ ARRAY_SIZE(scans)))
goto nomem;
/***** Modes */
@@ -2986,6 +3313,76 @@
/**
+ * wpas_dbus_getter_pkcs11_engine_path - Get PKCS #11 engine path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: A dbus message containing the PKCS #11 engine path
+ *
+ * Getter for "PKCS11EnginePath" property.
+ */
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *pkcs11_engine_path;
+
+ if (wpa_s->conf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "wpas_dbus_getter_pkcs11_engine_path[dbus]: An "
+ "error occurred getting the PKCS #11 engine path.");
+ dbus_set_error_const(
+ error, DBUS_ERROR_FAILED,
+ "An error occured getting the PKCS #11 engine path.");
+ return FALSE;
+ }
+
+ if (wpa_s->conf->pkcs11_engine_path == NULL)
+ pkcs11_engine_path = "";
+ else
+ pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &pkcs11_engine_path, error);
+}
+
+
+/**
+ * wpas_dbus_getter_pkcs11_module_path - Get PKCS #11 module path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: A dbus message containing the PKCS #11 module path
+ *
+ * Getter for "PKCS11ModulePath" property.
+ */
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *pkcs11_module_path;
+
+ if (wpa_s->conf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "wpas_dbus_getter_pkcs11_module_path[dbus]: An "
+ "error occurred getting the PKCS #11 module path.");
+ dbus_set_error_const(
+ error, DBUS_ERROR_FAILED,
+ "An error occured getting the PKCS #11 module path.");
+ return FALSE;
+ }
+
+ if (wpa_s->conf->pkcs11_module_path == NULL)
+ pkcs11_module_path = "";
+ else
+ pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &pkcs11_module_path, error);
+}
+
+
+/**
* wpas_dbus_getter_blobs - Get all blobs defined for this interface
* @iter: Pointer to incoming dbus message iter
* @error: Location to store error on failure
@@ -3159,11 +3556,22 @@
res = get_bss_helper(args, error, __func__);
if (!res)
return FALSE;
-
- if (res->caps & IEEE80211_CAP_IBSS)
- mode = "ad-hoc";
- else
- mode = "infrastructure";
+ if (bss_is_dmg(res)) {
+ switch (res->caps & IEEE80211_CAP_DMG_MASK) {
+ case IEEE80211_CAP_DMG_PBSS:
+ case IEEE80211_CAP_DMG_IBSS:
+ mode = "ad-hoc";
+ break;
+ case IEEE80211_CAP_DMG_AP:
+ mode = "infrastructure";
+ break;
+ }
+ } else {
+ if (res->caps & IEEE80211_CAP_IBSS)
+ mode = "ad-hoc";
+ else
+ mode = "infrastructure";
+ }
return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
&mode, error);
@@ -3283,7 +3691,7 @@
{
DBusMessageIter iter_dict, variant_iter;
const char *group;
- const char *pairwise[3]; /* max 3 pairwise ciphers is supported */
+ const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
const char *key_mgmt[7]; /* max 7 key managements may be supported */
int n;
@@ -3332,6 +3740,12 @@
case WPA_CIPHER_WEP104:
group = "wep104";
break;
+ case WPA_CIPHER_CCMP_256:
+ group = "ccmp-256";
+ break;
+ case WPA_CIPHER_GCMP_256:
+ group = "gcmp-256";
+ break;
default:
group = "";
break;
@@ -3348,6 +3762,10 @@
pairwise[n++] = "ccmp";
if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP)
pairwise[n++] = "gcmp";
+ if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP_256)
+ pairwise[n++] = "ccmp-256";
+ if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP_256)
+ pairwise[n++] = "gcmp-256";
if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise",
pairwise, n))
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index fbc8358..461970d 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -101,6 +101,9 @@
DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -122,6 +125,9 @@
DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -218,6 +224,14 @@
dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
void *user_data);
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
void *user_data);
@@ -278,6 +292,15 @@
DBusError *error,
void *user_data);
+DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
const char *arg);
DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 6ec96df..c9ecc7b 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -346,14 +346,14 @@
if (ssid == NULL || ssid->disabled != 2)
goto inv_args;
- if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0,
- NULL)) {
+ if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
+ NULL, 0)) {
reply = wpas_dbus_error_unknown_error(
message,
"Failed to reinvoke a persistent group");
goto out;
}
- } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0))
+ } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0))
goto inv_args;
out:
@@ -505,7 +505,7 @@
new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
persistent_group, 0, join, authorize_only,
- go_intent, freq, -1, 0, 0);
+ go_intent, freq, -1, 0, 0, 0);
if (new_pin >= 0) {
char npin[9];
@@ -631,8 +631,8 @@
if (ssid == NULL || ssid->disabled != 2)
goto err;
- if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0) < 0)
- {
+ if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) <
+ 0) {
reply = wpas_dbus_error_unknown_error(
message,
"Failed to reinvoke a persistent group");
@@ -825,6 +825,16 @@
wpa_s->conf->disassoc_low_ack))
goto err_no_mem;
+ /* No Group Iface */
+ if (!wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface",
+ wpa_s->conf->p2p_no_group_iface))
+ goto err_no_mem;
+
+ /* P2P Search Delay */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay",
+ wpa_s->conf->p2p_search_delay))
+ goto err_no_mem;
+
if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
!dbus_message_iter_close_container(iter, &variant_iter))
goto err_no_mem;
@@ -974,6 +984,12 @@
else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 &&
entry.type == DBUS_TYPE_UINT32)
wpa_s->conf->disassoc_low_ack = entry.uint32_value;
+ else if (os_strcmp(entry.key, "NoGroupIface") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN)
+ wpa_s->conf->p2p_no_group_iface = entry.bool_value;
+ else if (os_strcmp(entry.key, "p2p_search_delay") == 0 &&
+ entry.type == DBUS_TYPE_UINT32)
+ wpa_s->conf->p2p_search_delay = entry.uint32_value;
else
goto error;
@@ -1127,6 +1143,7 @@
break;
default:
str = "device";
+ break;
}
return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str,
@@ -1438,7 +1455,7 @@
void *user_data)
{
struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
- int i, num;
+ unsigned int i, num = 0;
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1451,7 +1468,8 @@
}
/* Add WPS vendor extensions attribute */
- for (i = 0, num = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ os_memset(vendor_extension, 0, sizeof(vendor_extension));
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
if (info->wps_vendor_ext[i] == NULL)
continue;
vendor_extension[num] = info->wps_vendor_ext[i];
@@ -1470,11 +1488,145 @@
dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
DBusError *error, void *user_data)
{
- dbus_bool_t success;
- /* struct peer_handler_args *peer_args = user_data; */
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
- success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
- NULL, 0, error);
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (info->wfd_subelems == NULL)
+ return wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_BYTE,
+ NULL, 0, error);
+
+ return wpas_dbus_simple_array_property_getter(
+ iter, DBUS_TYPE_BYTE, (char *) info->wfd_subelems->buf,
+ info->wfd_subelems->used, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ return wpas_dbus_simple_array_property_getter(
+ iter, DBUS_TYPE_BYTE, (char *) info->p2p_device_addr,
+ ETH_ALEN, error);
+}
+
+
+struct peer_group_data {
+ struct wpa_supplicant *wpa_s;
+ const struct p2p_peer_info *info;
+ char **paths;
+ unsigned int nb_paths;
+ int error;
+};
+
+
+static int match_group_where_peer_is_client(struct p2p_group *group,
+ void *user_data)
+{
+ struct peer_group_data *data = user_data;
+ const struct p2p_group_config *cfg;
+ struct wpa_supplicant *wpa_s_go;
+ char **paths;
+
+ if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr))
+ return 1;
+
+ cfg = p2p_group_get_config(group);
+
+ wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid,
+ cfg->ssid_len);
+ if (wpa_s_go == NULL)
+ return 1;
+
+ paths = os_realloc_array(data->paths, data->nb_paths + 1,
+ sizeof(char *));
+ if (paths == NULL)
+ goto out_of_memory;
+
+ data->paths = paths;
+ data->paths[data->nb_paths] = wpa_s_go->dbus_groupobj_path;
+ data->nb_paths++;
+
+ return 1;
+
+out_of_memory:
+ data->error = ENOMEM;
+ return 0;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+ struct peer_group_data data;
+ struct wpa_supplicant *wpa_s_go;
+ dbus_bool_t success = FALSE;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ os_memset(&data, 0, sizeof(data));
+ wpa_s_go = wpas_get_p2p_client_iface(peer_args->wpa_s,
+ info->p2p_device_addr);
+ if (wpa_s_go) {
+ data.paths = os_calloc(1, sizeof(char *));
+ if (data.paths == NULL)
+ goto out_of_memory;
+ data.paths[0] = wpa_s_go->dbus_groupobj_path;
+ data.nb_paths = 1;
+ }
+
+ data.wpa_s = peer_args->wpa_s;
+ data.info = info;
+
+ p2p_loop_on_all_groups(peer_args->wpa_s->global->p2p,
+ match_group_where_peer_is_client, &data);
+ if (data.error)
+ goto out_of_memory;
+
+ if (data.paths == NULL) {
+ return wpas_dbus_simple_array_property_getter(
+ iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error);
+ }
+
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ data.paths,
+ data.nb_paths, error);
+ goto out;
+
+out_of_memory:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+out:
+ os_free(data.paths);
return success;
}
@@ -1828,9 +1980,9 @@
if (!paths[i])
goto out_of_memory;
os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
"/" COMPACT_MACSTR,
- wpa_s->dbus_groupobj_path, MAC2STR(addr));
+ wpa_s->parent->dbus_new_path, MAC2STR(addr));
i++;
}
@@ -1964,8 +2116,9 @@
struct wpa_supplicant *wpa_s = user_data;
struct hostapd_data *hapd;
struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
- int num_vendor_ext = 0;
- int i;
+ unsigned int i, num_vendor_ext = 0;
+
+ os_memset(vendor_ext, 0, sizeof(vendor_ext));
/* Verify correct role for this property */
if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) {
@@ -1976,11 +2129,9 @@
/* Parse WPS Vendor Extensions sent in Beacon/Probe Response */
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
if (hapd->conf->wps_vendor_ext[i] == NULL)
- vendor_ext[i] = NULL;
- else {
- vendor_ext[num_vendor_ext++] =
- hapd->conf->wps_vendor_ext[i];
- }
+ continue;
+ vendor_ext[num_vendor_ext++] =
+ hapd->conf->wps_vendor_ext[i];
}
}
@@ -1989,7 +2140,7 @@
DBUS_TYPE_BYTE,
vendor_ext,
num_vendor_ext,
- error);
+ error);
}
@@ -2407,7 +2558,7 @@
if (req == 0)
goto error;
- if (!wpas_p2p_sd_cancel_request(wpa_s, req))
+ if (wpas_p2p_sd_cancel_request(wpa_s, req) < 0)
goto error;
return NULL;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
index a11b3c8..67dbfc9 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -14,11 +14,6 @@
u8 p2p_device_addr[ETH_ALEN];
};
-struct groupmember_handler_args {
- struct wpa_supplicant *wpa_s;
- u8 member_addr[ETH_ALEN];
-};
-
/*
* P2P Device methods
*/
@@ -147,6 +142,14 @@
DBusError *error,
void *user_data);
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
/*
* P2P Group properties
*/
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index e26086d..750522d 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -38,27 +38,25 @@
if (!dbus_message_iter_open_container(dict_iter,
DBUS_TYPE_DICT_ENTRY,
- NULL, &entry_iter)) {
- dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
- "no memory");
- return FALSE;
- }
- if (!dbus_message_iter_append_basic(&entry_iter,
+ NULL, &entry_iter) ||
+ !dbus_message_iter_append_basic(&entry_iter,
DBUS_TYPE_STRING,
- &dsc->dbus_property)) {
- dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
- "no memory");
- return FALSE;
- }
+ &dsc->dbus_property))
+ goto error;
/* An error getting a property fails the request entirely */
if (!dsc->getter(&entry_iter, error, user_data))
return FALSE;
- dbus_message_iter_close_container(dict_iter, &entry_iter);
+ if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
+ goto error;
}
return TRUE;
+
+error:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
@@ -111,7 +109,12 @@
return reply;
}
- wpa_dbus_dict_close_write(&iter, &dict_iter);
+ if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+ dbus_message_unref(reply);
+ return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ "out of memory");
+ }
+
return reply;
}
@@ -840,7 +843,6 @@
return;
eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
- dsc = obj_desc->properties;
for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
dsc++, i++) {
if (obj_desc->prop_changed_flags == NULL ||
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
index e565de9..048158f 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -25,10 +25,6 @@
#include "dbus_old_handlers.h"
#include "dbus_dict_helpers.h"
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
/**
* wpas_dbus_new_invalid_opts_error - Return a new invalid options error message
* @message: Pointer to incoming dbus message this error refers to
@@ -117,24 +113,28 @@
goto error;
if (!strcmp(entry.key, "driver") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(driver);
driver = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (driver == NULL)
goto error;
} else if (!strcmp(entry.key, "driver-params") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(driver_param);
driver_param = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (driver_param == NULL)
goto error;
} else if (!strcmp(entry.key, "config-file") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(confname);
confname = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (confname == NULL)
goto error;
} else if (!strcmp(entry.key, "bridge-ifname") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(bridge_ifname);
bridge_ifname = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (bridge_ifname == NULL)
@@ -350,7 +350,7 @@
DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
struct wpa_supplicant *wpa_s)
{
- DBusMessage *reply = NULL;
+ DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter sub_iter;
struct wpa_bss *bss;
@@ -358,9 +358,10 @@
/* Create and initialize the return message */
reply = dbus_message_new_method_return(message);
dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_OBJECT_PATH_AS_STRING,
- &sub_iter);
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING,
+ &sub_iter))
+ goto error;
/* Loop through scan results and append each result's object path */
dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
@@ -374,13 +375,21 @@
"%s/" WPAS_DBUS_BSSIDS_PART "/"
WPAS_DBUS_BSSID_FORMAT,
wpa_s->dbus_path, MAC2STR(bss->bssid));
- dbus_message_iter_append_basic(&sub_iter,
- DBUS_TYPE_OBJECT_PATH, &path);
+ if (!dbus_message_iter_append_basic(&sub_iter,
+ DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto error;
}
- dbus_message_iter_close_container(&iter, &sub_iter);
+ if (!dbus_message_iter_close_container(&iter, &sub_iter))
+ goto error;
return reply;
+
+error:
+ dbus_message_unref(reply);
+ return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
+ "an internal error occurred returning scan results");
}
@@ -419,7 +428,7 @@
if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid",
(const char *) (ie + 2),
ie[1]))
- goto error;
+ goto error;
}
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
@@ -539,7 +548,7 @@
const char *args[] = {"CCMP", "TKIP", "NONE"};
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "pairwise", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto error;
}
} else {
@@ -582,7 +591,7 @@
};
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "group", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto error;
}
} else {
@@ -632,7 +641,7 @@
};
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "key_mgmt", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto error;
}
} else {
@@ -683,7 +692,7 @@
const char *args[] = { "RSN", "WPA" };
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "proto", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto error;
}
} else {
@@ -720,7 +729,7 @@
const char *args[] = { "OPEN", "SHARED", "LEAP" };
if (!wpa_dbus_dict_append_string_array(
&iter_dict, "auth_alg", args,
- sizeof(args) / sizeof(char*)))
+ ARRAY_SIZE(args)))
goto error;
}
} else {
@@ -1204,16 +1213,19 @@
goto error;
if (!strcmp(entry.key, "opensc_engine_path") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(opensc_engine_path);
opensc_engine_path = os_strdup(entry.str_value);
if (opensc_engine_path == NULL)
goto error;
} else if (!strcmp(entry.key, "pkcs11_engine_path") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(pkcs11_engine_path);
pkcs11_engine_path = os_strdup(entry.str_value);
if (pkcs11_engine_path == NULL)
goto error;
} else if (!strcmp(entry.key, "pkcs11_module_path") &&
(entry.type == DBUS_TYPE_STRING)) {
+ os_free(pkcs11_module_path);
pkcs11_module_path = os_strdup(entry.str_value);
if (pkcs11_module_path == NULL)
goto error;
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index aa6005f..94c94b1 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -20,63 +20,6 @@
# used to fix build issues on such systems (krb5.h not found).
#CFLAGS += -I/usr/include/kerberos
-# Example configuration for various cross-compilation platforms
-
-#### sveasoft (e.g., for Linksys WRT54G) ######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
-#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
-###############################################################################
-
-#### openwrt (e.g., for Linksys WRT54G) #######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
-# -I../WRT54GS/release/src/include
-#LIBS = -lssl
-###############################################################################
-
-
-# Driver interface for Host AP driver
-CONFIG_DRIVER_HOSTAP=y
-
-# Driver interface for Agere driver
-#CONFIG_DRIVER_HERMES=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
-#CFLAGS += -I../../include/wireless
-
-# Driver interface for madwifi driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_MADWIFI=y
-# Set include directory to the madwifi source tree
-#CFLAGS += -I../../madwifi
-
-# Driver interface for ndiswrapper
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_NDISWRAPPER=y
-
-# Driver interface for Atmel driver
-CONFIG_DRIVER_ATMEL=y
-
-# Driver interface for old Broadcom driver
-# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
-# Linux wireless extensions and does not need (or even work) with the old
-# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
-#CONFIG_DRIVER_BROADCOM=y
-# Example path for wlioctl.h; change to match your configuration
-#CFLAGS += -I/opt/WRT54GS/release/src/include
-
-# Driver interface for Intel ipw2100/2200 driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_IPW=y
-
-# Driver interface for Ralink driver
-#CONFIG_DRIVER_RALINK=y
-
# Driver interface for generic Linux wireless extensions
# Note: WEXT is deprecated in the current Linux kernel version and no new
# functionality is added to it. nl80211-based interface is the new
@@ -88,6 +31,19 @@
# Driver interface for Linux 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$<path to libnl include files>
+#LIBS += -L$<path to libnl library files>
+
+# 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
@@ -147,10 +103,9 @@
CONFIG_EAP_TTLS=y
# EAP-FAST
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch)
-# to add the needed functions.
+# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
+# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
+# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
#CONFIG_EAP_FAST=y
# EAP-GTC
@@ -197,8 +152,6 @@
# Wi-Fi Protected Setup (WPS)
#CONFIG_WPS=y
-# Enable WSC 2.0 support
-#CONFIG_WPS2=y
# Enable WPS external registrar functionality
#CONFIG_WPS_ER=y
# Disable credentials for an open network by default when acting as a WPS
@@ -237,8 +190,10 @@
# Select control interface backend for external programs, e.g, wpa_cli:
# unix = UNIX domain sockets (default for Linux/*BSD)
# udp = UDP sockets using localhost (127.0.0.1)
+# udp6 = UDP IPv6 sockets using localhost (::1)
# named_pipe = Windows Named Pipe (default for Windows)
# udp-remote = UDP sockets with remote access (only for tests systems/purpose)
+# udp6-remote = UDP IPv6 sockets with remote access (only for tests purpose)
# y = use default (backwards compatibility)
# If this option is commented out, control interface is not included in the
# build.
@@ -298,7 +253,7 @@
# main_none = Very basic example (development use only)
#CONFIG_MAIN=main
-# Select wrapper for operatins system and C library specific functions
+# Select wrapper for operating system and C library specific functions
# unix = UNIX/POSIX like systems (default)
# win32 = Windows systems
# none = Empty template
@@ -307,12 +262,14 @@
# Select event loop implementation
# eloop = select() loop (default)
# eloop_win = Windows events and WaitForMultipleObject() loop
-# eloop_none = Empty template
#CONFIG_ELOOP=eloop
# Should we use poll instead of select? Select is used by default.
#CONFIG_ELOOP_POLL=y
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
# Select layer 2 packet implementation
# linux = Linux packet socket (default)
# pcap = libpcap/libdnet/WinPcap
@@ -484,6 +441,10 @@
# IEEE 802.11n (High Throughput) support (mainly for AP mode)
#CONFIG_IEEE80211N=y
+# IEEE 802.11ac (Very High Throughput) support (mainly for AP mode)
+# (depends on CONFIG_IEEE80211N)
+#CONFIG_IEEE80211AC=y
+
# Wireless Network Management (IEEE Std 802.11v-2011)
# Note: This is experimental and not complete implementation.
#CONFIG_WNM=y
@@ -515,9 +476,14 @@
# Enable TDLS support
#CONFIG_TDLS=y
+# Wi-Fi Direct
+# This can be used to enable Wi-Fi Direct extensions for P2P using an external
+# program to control the additional information exchanges in the messages.
+#CONFIG_WIFI_DISPLAY=y
+
# Autoscan
# This can be used to enable automatic scan support in wpa_supplicant.
-# See wpa_supplicant.conf for more information on autoscan usage.
+# See wpa_supplicant.conf for more information on autoscan usage.
#
# Enabling directly a module will enable autoscan support.
# For exponential module:
diff --git a/wpa_supplicant/doc/docbook/Makefile b/wpa_supplicant/doc/docbook/Makefile
index aaeee2e..82f9de3 100644
--- a/wpa_supplicant/doc/docbook/Makefile
+++ b/wpa_supplicant/doc/docbook/Makefile
@@ -7,6 +7,7 @@
FILES += wpa_priv
FILES += wpa_supplicant.conf
FILES += wpa_supplicant
+FILES += eapol_test
man:
for i in $(FILES); do docbook2man $$i.sgml; done
@@ -20,7 +21,7 @@
clean:
- rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8
+ rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 eapol_test.8
rm -f wpa_supplicant.conf.5
rm -f manpage.links manpage.refs
rm -f $(FILES:%=%.pdf)
diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml
new file mode 100644
index 0000000..fec174b
--- /dev/null
+++ b/wpa_supplicant/doc/docbook/eapol_test.sgml
@@ -0,0 +1,205 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+ <refmeta>
+ <refentrytitle>eapol_test</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+ <refnamediv>
+ <refname>eapol_test</refname>
+
+ <refpurpose>EAP peer and RADIUS client testing</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>eapol_test</command>
+ <arg>-nWS</arg>
+ <arg>-c<replaceable>config file</replaceable></arg>
+ <arg>-a<replaceable>server IP address</replaceable></arg>
+ <arg>-A<replaceable>client IP address</replaceable></arg>
+ <arg>-p<replaceable>UDP port</replaceable></arg>
+ <arg>-s<replaceable>shared secret</replaceable></arg>
+ <arg>-r<replaceable>re-authentications</replaceable></arg>
+ <arg>-t<replaceable>timeout</replaceable></arg>
+ <arg>-C<replaceable>Connect-Info</replaceable></arg>
+ <arg>-M<replaceable>MAC address</replaceable></arg>
+ <arg>-o<replaceable>file</replaceable></arg>
+ <arg>-N<replaceable>attr spec</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>eapol_test scard</command>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>eapol_test sim</command>
+ <arg>PIN</arg>
+ <arg>num triplets</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Overview</title>
+
+ <para>eapol_test is a program that links together the same EAP
+ peer implementation that wpa_supplicant is using and the RADIUS
+ authentication client code from hostapd. In addition, it has
+ minimal glue code to combine these two components in similar
+ ways to IEEE 802.1X/EAPOL Authenticator state machines. In other
+ words, it integrates IEEE 802.1X Authenticator (normally, an
+ access point) and IEEE 802.1X Supplicant (normally, a wireless
+ client) together to generate a single program that can be used to
+ test EAP methods without having to setup an access point and a
+ wireless client.</para>
+
+ <para>The main uses for eapol_test are in interoperability testing
+ of EAP methods against RADIUS servers and in development testing
+ for new EAP methods. It can be easily used to automate EAP testing
+ for interoperability and regression since the program can be run
+ from shell scripts without require additional test components apart
+ from a RADIUS server. For example, the automated EAP tests described
+ in eap_testing.txt are implemented with eapol_test. Similarly,
+ eapol_test could be used to implement an automated regression
+ test suite for a RADIUS authentication server.</para>
+
+
+ <para>As an example:</para>
+
+<blockquote><programlisting>
+eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1
+</programlisting></blockquote>
+
+ <para>tries to complete EAP authentication based on the network
+ configuration from test.conf against the RADIUS server running
+ on the local host. A re-authentication is triggered to test fast
+ re-authentication. The configuration file uses the same format for
+ network blocks as wpa_supplicant.</para>
+
+ </refsect1>
+ <refsect1>
+ <title>Command Arguments</title>
+ <variablelist>
+ <varlistentry>
+ <term>-c configuration file path</term>
+
+ <listitem><para>A configuration to use. The configuration should
+ use the same format for network blocks as wpa_supplicant.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-a AS address</term>
+
+ <listitem><para>IP address of the authentication server. The
+ default is '127.0.0.1'.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-A client address</term>
+
+ <listitem><para>IP address of the client. The default is to
+ select an address automatically.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-p AS port</term>
+
+ <listitem><para>UDP port of the authentication server. The
+ default is '1812'.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s AS secret</term>
+
+ <listitem><para>Shared secret with the authentication server.
+ The default is 'radius'.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-r count</term>
+
+ <listitem><para>Number of reauthentications.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-t timeout</term>
+
+ <listitem><para>Timeout in seconds. The default is 30.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-C info</term>
+
+ <listitem><para>RADIUS Connect-Info. The default is
+ 'CONNECT 11Mbps 802.11b'.</para></listitem>
+ </varlistentry>
+
+
+ <varlistentry>
+ <term>-M mac address</term>
+
+ <listitem><para>Client MAC address (Calling-Station-Id). The
+ default is '02:00:00:00:00:01'.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-o file</term>
+
+ <listitem><para>Location to write out server certificate.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-N attr spec</term>
+
+ <listitem><para>Send arbitrary attribute specific by
+ attr_id:syntax:value, or attr_id alone. attr_id should be the numeric
+ ID of the attribute, and syntax should be one of 's' (string),
+ 'd' (integer), or 'x' (octet string). The value is the attribute value
+ to send. When attr_id is given alone, NULL is used as the attribute
+ value. Multiple attributes can be specified by using the option
+ several times.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-n</term>
+
+ <listitem><para>Indicates that no MPPE keys are expected.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-W</term>
+
+ <listitem><para>Wait for a control interface monitor before starting.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-S</term>
+
+ <listitem><para>Save configuration after authentication.
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>wpa_supplicant</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+ <refsect1>
+ <title>Legal</title>
+ <para>wpa_supplicant is copyright (c) 2003-2014,
+ Jouni Malinen <email>j@w1.fi</email> and
+ contributors.
+ All Rights Reserved.</para>
+
+ <para>This program is licensed under the BSD license (the one with
+ advertisement clause removed).</para>
+ </refsect1>
+</refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml
index eb3a089..860b5a0 100644
--- a/wpa_supplicant/doc/docbook/wpa_background.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_background.sgml
@@ -90,7 +90,7 @@
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2012,
+ <para>wpa_supplicant is copyright (c) 2003-2014,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml
index c080c07..142e1ab 100644
--- a/wpa_supplicant/doc/docbook/wpa_cli.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml
@@ -15,10 +15,12 @@
<cmdsynopsis>
<command>wpa_cli</command>
<arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
+ <arg>-g <replaceable>path to global ctrl_interface socket</replaceable></arg>
<arg>-i <replaceable>ifname</replaceable></arg>
<arg>-hvB</arg>
<arg>-a <replaceable>action file</replaceable></arg>
<arg>-P <replaceable>pid file</replaceable></arg>
+ <arg>-G <replaceable>ping interval</replaceable></arg>
<arg><replaceable>command ...</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -111,6 +113,14 @@
</varlistentry>
<varlistentry>
+ <term>-g control socket path</term>
+
+ <listitem><para>Connect to the global control socket at the
+ indicated path rather than an interface-specific control
+ socket.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>-i ifname</term>
<listitem><para>Specify the interface that is being
@@ -161,6 +171,13 @@
</varlistentry>
<varlistentry>
+ <term>-G ping interval</term>
+
+ <listitem><para>Set the interval (in seconds) at which
+ wpa_cli pings the supplicant.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>command</term>
<listitem><para>Run a command. The available commands are
@@ -328,7 +345,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2012,
+ <para>wpa_supplicant is copyright (c) 2003-2014,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
index 0ab6419..f6ef8f1 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -74,7 +74,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2012,
+ <para>wpa_supplicant is copyright (c) 2003-2014,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
index 336c03b..3b4360b 100644
--- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
@@ -62,7 +62,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2012,
+ <para>wpa_supplicant is copyright (c) 2003-2014,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml
index eb907a8..9c114cc 100644
--- a/wpa_supplicant/doc/docbook/wpa_priv.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml
@@ -137,7 +137,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2012,
+ <para>wpa_supplicant is copyright (c) 2003-2014,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index aa20e57..182060d 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -12,7 +12,7 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>wpa_supplicant</command>
- <arg>-BddfhKLqqtuvW</arg>
+ <arg>-BddfhKLqqsTtuvW</arg>
<arg>-i<replaceable>ifname</replaceable></arg>
<arg>-c<replaceable>config file</replaceable></arg>
<arg>-D<replaceable>driver</replaceable></arg>
@@ -344,9 +344,20 @@
</varlistentry>
<varlistentry>
+ <term>-e entropy file</term>
+ <listitem>
+ <para>File for <command>wpa_supplicant</command> to use to
+ maintain its internal entropy store in over restarts.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>-f output file</term>
<listitem>
- <para>Log output to specified file instead of stdout.</para>
+ <para>Log output to specified file instead of stdout. (This
+ is only available if <command>wpa_supplicant</command> was
+ built with the <literal>CONFIG_DEBUG_FILE</literal>
+ option.)</para>
</listitem>
</varlistentry>
@@ -387,6 +398,22 @@
</varlistentry>
<varlistentry>
+ <term>-o override driver</term>
+ <listitem>
+ <para>Override the driver parameter for new
+ interfaces.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-O override ctrl_interface</term>
+ <listitem>
+ <para>Override the ctrl_interface parameter for new
+ interfaces.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>-p</term>
<listitem>
<para>Driver parameters. (Per interface)</para>
@@ -409,10 +436,40 @@
</varlistentry>
<varlistentry>
+ <term>-s</term>
+ <listitem>
+ <para>Log output to syslog instead of stdout. (This is only
+ available if <command>wpa_supplicant</command> was built
+ with the <literal>CONFIG_DEBUG_SYSLOG</literal>
+ option.)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-T</term>
+ <listitem>
+ <para>Log output to Linux tracing in addition to any other
+ destinations. (This is only available
+ if <command>wpa_supplicant</command> was built with
+ the <literal>CONFIG_DEBUG_LINUX_TRACING</literal>
+ option.)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-t</term>
+ <listitem>
+ <para>Include timestamp in debug messages.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>-u</term>
<listitem>
- <para>Enabled DBus control interface. If enabled, interface
- definitions may be omitted.</para>
+ <para>Enable DBus control interface. If enabled, interface
+ definitions may be omitted. (This is only available
+ if <command>wpa_supplicant</command> was built with
+ the <literal>CONFIG_DBUS</literal> option.)</para>0
</listitem>
</varlistentry>
@@ -679,7 +736,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2012,
+ <para>wpa_supplicant is copyright (c) 2003-2014,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 5e7dbf7..00703d9 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -117,11 +117,16 @@
static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
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)
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
{
+ if (alg != WPA_ALG_NONE) {
+ if (key_idx >= 0 && key_idx <= 6)
+ wpa_s->keys_cleared &= ~BIT(key_idx);
+ else
+ wpa_s->keys_cleared = 0;
+ }
if (wpa_s->driver->set_key) {
- wpa_s->keys_cleared = 0;
return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv,
alg, addr, key_idx, set_tx,
seq, seq_len, key, key_len);
@@ -201,6 +206,14 @@
return NULL;
}
+static inline const char *
+wpa_driver_get_radio_name(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->get_radio_name)
+ return wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+ return NULL;
+}
+
static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s)
{
if (wpa_s->driver->get_mac_addr) {
@@ -380,7 +393,7 @@
if (wpa_s->driver->if_add)
return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
addr, bss_ctx, NULL, force_ifname,
- if_addr, bridge);
+ if_addr, bridge, 0);
return -1;
}
@@ -516,143 +529,17 @@
return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu);
}
-static inline int wpa_drv_p2p_find(struct wpa_supplicant *wpa_s,
- unsigned int timeout, int type)
-{
- if (!wpa_s->driver->p2p_find)
- return -1;
- return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type);
-}
-
-static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s)
-{
- if (!wpa_s->driver->p2p_stop_find)
- return -1;
- return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv);
-}
-
-static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s,
- unsigned int timeout)
-{
- if (!wpa_s->driver->p2p_listen)
- return -1;
- return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout);
-}
-
-static inline int wpa_drv_p2p_connect(struct wpa_supplicant *wpa_s,
- const u8 *peer_addr, int wps_method,
- int go_intent,
- const u8 *own_interface_addr,
- unsigned int force_freq,
- int persistent_group)
-{
- if (!wpa_s->driver->p2p_connect)
- return -1;
- return wpa_s->driver->p2p_connect(wpa_s->drv_priv, peer_addr,
- wps_method, go_intent,
- own_interface_addr, force_freq,
- persistent_group);
-}
-
-static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s,
- const u8 *peer_addr)
-{
- if (!wpa_s->driver->wps_success_cb)
- return -1;
- return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr);
-}
-
-static inline int
-wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
-{
- if (!wpa_s->driver->p2p_group_formation_failed)
- return -1;
- return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv);
-}
-
-static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s,
- const struct p2p_params *params)
-{
- if (!wpa_s->driver->p2p_set_params)
- return -1;
- return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params);
-}
-
-static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s,
- const u8 *peer_addr,
- u16 config_methods, int join)
-{
- if (!wpa_s->driver->p2p_prov_disc_req)
- return -1;
- return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr,
- config_methods, join);
-}
-
-static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s,
- const u8 *dst,
- const struct wpabuf *tlvs)
-{
- if (!wpa_s->driver->p2p_sd_request)
- return 0;
- return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs);
-}
-
-static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s,
- u64 req)
-{
- if (!wpa_s->driver->p2p_sd_cancel_request)
- return -1;
- return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req);
-}
-
-static inline int wpa_drv_p2p_sd_response(struct wpa_supplicant *wpa_s,
- int freq, const u8 *dst,
- u8 dialog_token,
- const struct wpabuf *resp_tlvs)
-{
- if (!wpa_s->driver->p2p_sd_response)
- return -1;
- return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst,
- dialog_token, resp_tlvs);
-}
-
-static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s)
-{
- if (!wpa_s->driver->p2p_service_update)
- return -1;
- return wpa_s->driver->p2p_service_update(wpa_s->drv_priv);
-}
-
-static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s,
- const u8 *addr)
-{
- if (!wpa_s->driver->p2p_reject)
- return -1;
- return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr);
-}
-
-static inline int wpa_drv_p2p_invite(struct wpa_supplicant *wpa_s,
- const u8 *peer, int role, const u8 *bssid,
- const u8 *ssid, size_t ssid_len,
- const u8 *go_dev_addr,
- int persistent_group)
-{
- if (!wpa_s->driver->p2p_invite)
- return -1;
- return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid,
- ssid, ssid_len, go_dev_addr,
- persistent_group);
-}
-
static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s,
const u8 *dst, u8 action_code,
u8 dialog_token, u16 status_code,
- const u8 *buf, size_t len)
+ u32 peer_capab, const u8 *buf,
+ size_t len)
{
if (wpa_s->driver->send_tdls_mgmt) {
return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
action_code, dialog_token,
- status_code, buf, len);
+ status_code, peer_capab,
+ buf, len);
}
return -1;
}
@@ -673,7 +560,7 @@
return -1;
return wpa_s->driver->driver_cmd(wpa_s->drv_priv, cmd, buf, buf_len);
}
-#endif
+#endif /* ANDROID */
static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
const u8 *kek, const u8 *kck,
@@ -693,11 +580,11 @@
}
static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
- unsigned int freq)
+ struct csa_settings *settings)
{
if (!wpa_s->driver->switch_channel)
return -1;
- return wpa_s->driver->switch_channel(wpa_s->drv_priv, freq);
+ return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings);
}
static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
@@ -710,4 +597,234 @@
buf_len);
}
+static inline int wpa_drv_status(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ if (!wpa_s->driver->status)
+ return -1;
+ return wpa_s->driver->status(wpa_s->drv_priv, buf, buflen);
+}
+
+static inline int wpa_drv_set_qos_map(struct wpa_supplicant *wpa_s,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+{
+ if (!wpa_s->driver->set_qos_map)
+ return -1;
+ return wpa_s->driver->set_qos_map(wpa_s->drv_priv, qos_map_set,
+ qos_map_set_len);
+}
+
+static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s,
+ const struct wowlan_triggers *triggers)
+{
+ if (!wpa_s->driver->set_wowlan)
+ return -1;
+ return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers);
+}
+
+static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
+ int vendor_id, int subcmd, const u8 *data,
+ size_t data_len, struct wpabuf *buf)
+{
+ if (!wpa_s->driver->vendor_cmd)
+ return -1;
+ return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd,
+ data, data_len, buf);
+}
+
+
+#ifdef CONFIG_MACSEC
+
+static inline int wpa_drv_macsec_init(struct wpa_supplicant *wpa_s,
+ struct macsec_init_params *params)
+{
+ if (!wpa_s->driver->macsec_init)
+ return -1;
+ return wpa_s->driver->macsec_init(wpa_s->drv_priv, params);
+}
+
+static inline int wpa_drv_macsec_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->macsec_deinit)
+ return -1;
+ return wpa_s->driver->macsec_deinit(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
+ Boolean enabled)
+{
+ if (!wpa_s->driver->enable_protect_frames)
+ return -1;
+ return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled);
+}
+
+static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s,
+ Boolean enabled, u32 window)
+{
+ if (!wpa_s->driver->set_replay_protect)
+ return -1;
+ return wpa_s->driver->set_replay_protect(wpa_s->drv_priv, enabled,
+ window);
+}
+
+static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s,
+ const u8 *cs, size_t cs_len)
+{
+ if (!wpa_s->driver->set_current_cipher_suite)
+ return -1;
+ return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs,
+ cs_len);
+}
+
+static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s,
+ Boolean enabled)
+{
+ if (!wpa_s->driver->enable_controlled_port)
+ return -1;
+ return wpa_s->driver->enable_controlled_port(wpa_s->drv_priv, enabled);
+}
+
+static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 *lowest_pn)
+{
+ if (!wpa_s->driver->get_receive_lowest_pn)
+ return -1;
+ return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel,
+ an, lowest_pn);
+}
+
+static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 *next_pn)
+{
+ if (!wpa_s->driver->get_transmit_next_pn)
+ return -1;
+ return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, channel,
+ an, next_pn);
+}
+
+static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 next_pn)
+{
+ if (!wpa_s->driver->set_transmit_next_pn)
+ return -1;
+ return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, channel,
+ an, next_pn);
+}
+
+static inline int wpa_drv_get_available_receive_sc(struct wpa_supplicant *wpa_s,
+ u32 *channel)
+{
+ if (!wpa_s->driver->get_available_receive_sc)
+ return -1;
+ return wpa_s->driver->get_available_receive_sc(wpa_s->drv_priv,
+ channel);
+}
+
+static inline int
+wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel,
+ const u8 *sci_addr, u16 sci_port,
+ unsigned int conf_offset, int validation)
+{
+ if (!wpa_s->driver->create_receive_sc)
+ return -1;
+ return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, channel,
+ sci_addr, sci_port, conf_offset,
+ validation);
+}
+
+static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s,
+ u32 channel)
+{
+ if (!wpa_s->driver->delete_receive_sc)
+ return -1;
+ return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel);
+}
+
+static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 lowest_pn, const u8 *sak)
+{
+ if (!wpa_s->driver->create_receive_sa)
+ return -1;
+ return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, channel, an,
+ lowest_pn, sak);
+}
+
+static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an)
+{
+ if (!wpa_s->driver->enable_receive_sa)
+ return -1;
+ return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an)
+{
+ if (!wpa_s->driver->disable_receive_sa)
+ return -1;
+ return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int
+wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel)
+{
+ if (!wpa_s->driver->get_available_transmit_sc)
+ return -1;
+ return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv,
+ channel);
+}
+
+static inline int
+wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel,
+ const u8 *sci_addr, u16 sci_port,
+ unsigned int conf_offset)
+{
+ if (!wpa_s->driver->create_transmit_sc)
+ return -1;
+ return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, channel,
+ sci_addr, sci_port,
+ conf_offset);
+}
+
+static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s,
+ u32 channel)
+{
+ if (!wpa_s->driver->delete_transmit_sc)
+ return -1;
+ return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel);
+}
+
+static inline int wpa_drv_create_transmit_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 next_pn,
+ Boolean confidentiality,
+ const u8 *sak)
+{
+ if (!wpa_s->driver->create_transmit_sa)
+ return -1;
+ return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, channel, an,
+ next_pn, confidentiality, sak);
+}
+
+static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an)
+{
+ if (!wpa_s->driver->enable_transmit_sa)
+ return -1;
+ return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an)
+{
+ if (!wpa_s->driver->disable_transmit_sa)
+ return -1;
+ return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an);
+}
+#endif /* CONFIG_MACSEC */
+
#endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c
index 6cd2fc5..ece5716 100644
--- a/wpa_supplicant/eap_register.c
+++ b/wpa_supplicant/eap_register.c
@@ -40,6 +40,13 @@
ret = eap_peer_unauth_tls_register();
#endif /* EAP_UNAUTH_TLS */
+#ifdef EAP_TLS
+#ifdef CONFIG_HS20
+ if (ret == 0)
+ ret = eap_peer_wfa_unauth_tls_register();
+#endif /* CONFIG_HS20 */
+#endif /* EAP_TLS */
+
#ifdef EAP_MSCHAPv2
if (ret == 0)
ret = eap_peer_mschapv2_register();
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index dad2765..e19782f 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - test code
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -27,11 +27,9 @@
#include "common/wpa_ctrl.h"
#include "ctrl_iface.h"
#include "pcsc_funcs.h"
+#include "wpas_glue.h"
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
struct wpa_driver_ops *wpa_drivers[] = { NULL };
@@ -48,6 +46,7 @@
int eapol_test_num_reauths;
int no_mppe_keys;
int num_mppe_ok, num_mppe_mismatch;
+ int req_eap_key_name;
u8 radius_identifier;
struct radius_msg *last_recv_radius;
@@ -60,6 +59,8 @@
u8 authenticator_pmk[PMK_LEN];
size_t authenticator_pmk_len;
+ u8 authenticator_eap_key_name[256];
+ size_t authenticator_eap_key_name_len;
int radius_access_accept_received;
int radius_access_reject_received;
int auth_timed_out;
@@ -72,6 +73,9 @@
struct extra_radius_attr *extra_attrs;
FILE *server_cert_file;
+
+ const char *pcsc_reader;
+ const char *pcsc_pin;
};
static struct eapol_test_data eapol_test;
@@ -210,6 +214,13 @@
goto fail;
}
+ if (e->req_eap_key_name &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME, (u8 *) "\0",
+ 1)) {
+ printf("Could not add EAP-Key-Name\n");
+ goto fail;
+ }
+
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) &&
!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
(u8 *) &e->own_ip_addr, 4)) {
@@ -335,6 +346,8 @@
{
u8 pmk[PMK_LEN];
int ret = 1;
+ const u8 *sess_id;
+ size_t sess_id_len;
if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) {
wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN);
@@ -363,14 +376,37 @@
else if (!e->no_mppe_keys)
e->num_mppe_ok++;
+ sess_id = eapol_sm_get_session_id(e->wpa_s->eapol, &sess_id_len);
+ if (!sess_id)
+ return ret;
+ if (e->authenticator_eap_key_name_len == 0) {
+ wpa_printf(MSG_INFO, "No EAP-Key-Name received from server");
+ return ret;
+ }
+
+ if (e->authenticator_eap_key_name_len != sess_id_len ||
+ os_memcmp(e->authenticator_eap_key_name, sess_id, sess_id_len) != 0)
+ {
+ wpa_printf(MSG_INFO,
+ "Locally derived EAP Session-Id does not match EAP-Key-Name from server");
+ wpa_hexdump(MSG_DEBUG, "EAP Session-Id", sess_id, sess_id_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-Key-Name from server",
+ e->authenticator_eap_key_name,
+ e->authenticator_eap_key_name_len);
+ } else {
+ wpa_printf(MSG_INFO,
+ "Locally derived EAP Session-Id matches EAP-Key-Name from server");
+ }
+
return ret;
}
-static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx)
+static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result,
+ void *ctx)
{
struct eapol_test_data *e = ctx;
- printf("eapol_sm_cb: success=%d\n", success);
+ printf("eapol_sm_cb: result=%d\n", result);
e->eapol_test_num_reauths--;
if (e->eapol_test_num_reauths < 0)
eloop_terminate();
@@ -395,6 +431,54 @@
}
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
+ const char *default_txt)
+{
+ struct eapol_test_data *e = ctx;
+ struct wpa_supplicant *wpa_s = e->wpa_s;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const char *field_name, *txt = NULL;
+ char *buf;
+ size_t buflen;
+ int len;
+
+ if (ssid == NULL)
+ return;
+
+ field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
+ &txt);
+ if (field_name == NULL) {
+ wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed",
+ field);
+ return;
+ }
+
+ buflen = 100 + os_strlen(txt) + ssid->ssid_len;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ len = os_snprintf(buf, buflen,
+ WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
+ field_name, ssid->id, txt);
+ if (len < 0 || (size_t) len >= buflen) {
+ os_free(buf);
+ return;
+ }
+ if (ssid->ssid && buflen > len + ssid->ssid_len) {
+ os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
+ len += ssid->ssid_len;
+ buf[len] = '\0';
+ }
+ buf[buflen - 1] = '\0';
+ wpa_msg(wpa_s, MSG_INFO, "%s", buf);
+ os_free(buf);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eapol_test_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
const char *cert_hash,
const struct wpabuf *cert)
@@ -484,6 +568,7 @@
ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+ ctx->eap_param_needed = eapol_test_eap_param_needed;
ctx->cert_cb = eapol_test_cert_cb;
ctx->cert_in_cb = 1;
ctx->set_anon_id = eapol_test_set_anon_id;
@@ -501,6 +586,7 @@
eapol_conf.required_keys = 0;
eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
+ eapol_conf.external_sim = wpa_s->conf->external_sim;
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
@@ -700,6 +786,8 @@
size_t shared_secret_len)
{
struct radius_ms_mppe_keys *keys;
+ u8 *buf;
+ size_t len;
keys = radius_msg_get_ms_keys(msg, req, shared_secret,
shared_secret_len);
@@ -738,6 +826,14 @@
os_free(keys->recv);
os_free(keys);
}
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
+ NULL) == 0) {
+ os_memcpy(e->authenticator_eap_key_name, buf, len);
+ e->authenticator_eap_key_name_len = len;
+ } else {
+ e->authenticator_eap_key_name_len = 0;
+ }
}
@@ -861,7 +957,7 @@
}
-static int scard_test(void)
+static int scard_test(struct eapol_test_data *e)
{
struct scard_data *scard;
size_t len;
@@ -892,10 +988,10 @@
unsigned char aka_ik[IK_LEN];
unsigned char aka_ck[CK_LEN];
- scard = scard_init(SCARD_TRY_BOTH, NULL);
+ scard = scard_init(e->pcsc_reader);
if (scard == NULL)
return -1;
- if (scard_set_pin(scard, "1234")) {
+ if (scard_set_pin(scard, e->pcsc_pin)) {
wpa_printf(MSG_WARNING, "PIN validation failed");
scard_deinit(scard);
return -1;
@@ -970,7 +1066,7 @@
}
-static int scard_get_triplets(int argc, char *argv[])
+static int scard_get_triplets(struct eapol_test_data *e, int argc, char *argv[])
{
struct scard_data *scard;
size_t len;
@@ -992,7 +1088,7 @@
wpa_debug_level = 99;
}
- scard = scard_init(SCARD_GSM_SIM_ONLY, NULL);
+ scard = scard_init(e->pcsc_reader);
if (scard == NULL) {
printf("Failed to open smartcard connection\n");
return -1;
@@ -1046,11 +1142,12 @@
static void usage(void)
{
printf("usage:\n"
- "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
+ "eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
"[-s<AS secret>]\\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
" [-M<client MAC address>] [-o<server cert file] \\\n"
- " [-N<attr spec>] \\\n"
+ " [-N<attr spec>] [-R<PC/SC reader>] "
+ "[-P<PC/SC PIN>] \\\n"
" [-A<client IP>]\n"
"eapol_test scard\n"
"eapol_test sim <PIN> <num triplets> [debug]\n"
@@ -1066,6 +1163,7 @@
" -A<client IP> = IP address of the client, default: select "
"automatically\n"
" -r<count> = number of re-authentications\n"
+ " -e = Request EAP-Key-Name\n"
" -W = wait for a control interface monitor before starting\n"
" -S = save configuration after authentication\n"
" -n = no MPPE keys expected\n"
@@ -1094,6 +1192,7 @@
int main(int argc, char *argv[])
{
+ struct wpa_global global;
struct wpa_supplicant wpa_s;
int c, ret = 1, wait_for_monitor = 0, save_config = 0;
char *as_addr = "127.0.0.1";
@@ -1113,12 +1212,13 @@
os_memset(&eapol_test, 0, sizeof(eapol_test));
eapol_test.connect_info = "CONNECT 11Mbps 802.11b";
os_memcpy(eapol_test.own_addr, "\x02\x00\x00\x00\x00\x01", ETH_ALEN);
+ eapol_test.pcsc_pin = "1234";
wpa_debug_level = 0;
wpa_debug_show_keys = 1;
for (;;) {
- c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W");
+ c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W");
if (c < 0)
break;
switch (c) {
@@ -1134,6 +1234,9 @@
case 'C':
eapol_test.connect_info = optarg;
break;
+ case 'e':
+ eapol_test.req_eap_key_name = 1;
+ break;
case 'M':
if (hwaddr_aton(optarg, eapol_test.own_addr)) {
usage();
@@ -1156,9 +1259,15 @@
case 'p':
as_port = atoi(optarg);
break;
+ case 'P':
+ eapol_test.pcsc_pin = optarg;
+ break;
case 'r':
eapol_test.eapol_test_num_reauths = atoi(optarg);
break;
+ case 'R':
+ eapol_test.pcsc_reader = optarg;
+ break;
case 's':
as_secret = optarg;
break;
@@ -1206,11 +1315,11 @@
}
if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
- return scard_test();
+ return scard_test(&eapol_test);
}
if (argc > optind && os_strcmp(argv[optind], "sim") == 0) {
- return scard_get_triplets(argc - optind - 1,
+ return scard_get_triplets(&eapol_test, argc - optind - 1,
&argv[optind + 1]);
}
@@ -1230,8 +1339,12 @@
return -1;
}
+ os_memset(&global, 0, sizeof(global));
os_memset(&wpa_s, 0, sizeof(wpa_s));
+ wpa_s.global = &global;
eapol_test.wpa_s = &wpa_s;
+ dl_list_init(&wpa_s.bss);
+ dl_list_init(&wpa_s.bss_id);
wpa_s.conf = wpa_config_read(conf, NULL);
if (wpa_s.conf == NULL) {
printf("Failed to parse configuration file '%s'.\n", conf);
@@ -1242,6 +1355,11 @@
return -1;
}
+ if (eapol_test.pcsc_reader) {
+ os_free(wpa_s.conf->pcsc_reader);
+ wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
+ }
+
wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
cli_addr);
wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 9de8d7f..ba9e083 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -46,19 +46,19 @@
#ifndef CONFIG_NO_SCAN_PROCESSING
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
- int new_scan);
+ int new_scan, int own_request);
#endif /* CONFIG_NO_SCAN_PROCESSING */
static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
- struct os_time now;
+ struct os_reltime now;
if (ssid == NULL || ssid->disabled_until.sec == 0)
return 0;
- os_get_time(&now);
+ os_get_reltime(&now);
if (ssid->disabled_until.sec > now.sec)
return ssid->disabled_until.sec - now.sec;
@@ -68,13 +68,46 @@
}
+static struct wpa_bss * wpa_supplicant_get_new_bss(
+ struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bss *bss = NULL;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid->ssid_len > 0)
+ bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+ if (!bss)
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+ return bss;
+}
+
+
+static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+
+ if (!bss) {
+ wpa_supplicant_update_scan_results(wpa_s);
+
+ /* Get the BSS from the new scan results */
+ bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+ }
+
+ if (bss)
+ wpa_s->current_bss = bss;
+}
+
+
static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid, *old_ssid;
int res;
- if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
+ if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
+ wpa_supplicant_update_current_bss(wpa_s);
return 0;
+ }
wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
"information");
@@ -119,6 +152,9 @@
eapol_sm_invalidate_cached_session(wpa_s->eapol);
old_ssid = wpa_s->current_ssid;
wpa_s->current_ssid = ssid;
+
+ wpa_supplicant_update_current_bss(wpa_s);
+
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
@@ -160,9 +196,6 @@
return;
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
-#ifdef ANDROID
- wpa_s->conf->ap_scan = DEFAULT_AP_SCAN;
-#endif
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
@@ -278,10 +311,11 @@
#ifdef PCSC_FUNCS
int aka = 0, sim = 0;
- if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL)
+ if ((ssid != NULL && ssid->eap.pcsc == NULL) ||
+ wpa_s->scard != NULL || wpa_s->conf->external_sim)
return 0;
- if (ssid->eap.eap_methods == NULL) {
+ if (ssid == NULL || ssid->eap.eap_methods == NULL) {
sim = 1;
aka = 1;
} else {
@@ -316,8 +350,7 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
"(sim=%d aka=%d) - initialize PCSC", sim, aka);
- wpa_s->scard = scard_init((!sim && aka) ? SCARD_USIM_ONLY :
- SCARD_TRY_BOTH, NULL);
+ wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
if (wpa_s->scard == NULL) {
wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
"(pcsc-lite)");
@@ -373,6 +406,9 @@
if (wpa_key_mgmt_wpa(ssid->key_mgmt))
privacy = 1;
+ if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)
+ privacy = 1;
+
if (bss->caps & IEEE80211_CAP_PRIVACY)
return privacy;
return !privacy;
@@ -514,6 +550,12 @@
return 0;
}
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
+ wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN");
+ return 1;
+ }
+
if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2");
return 1;
@@ -679,12 +721,6 @@
}
-static int bss_is_dmg(struct wpa_bss *bss)
-{
- return bss->freq > 45000;
-}
-
-
/*
* Test whether BSS is in an ESS.
* This is done differently in DMG (60 GHz) and non-DMG bands
@@ -703,13 +739,15 @@
static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
- struct wpa_ssid *group)
+ struct wpa_ssid *group,
+ int only_first_ssid)
{
u8 wpa_ie_len, rsn_ie_len;
int wpa;
struct wpa_blacklist *e;
const u8 *ie;
struct wpa_ssid *ssid;
+ int osen;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
wpa_ie_len = ie ? ie[1] : 0;
@@ -717,11 +755,18 @@
ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
rsn_ie_len = ie ? ie[1] : 0;
+ ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+ osen = ie != NULL;
+
wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
- "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s",
+ "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s",
i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
- wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
+ wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
+ (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+ wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
+ " p2p" : "",
+ osen ? " osen=1" : "");
e = wpa_blacklist_get(wpa_s, bss->bssid);
if (e) {
@@ -761,7 +806,7 @@
wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
- for (ssid = group; ssid; ssid = ssid->pnext) {
+ for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
int res;
@@ -819,7 +864,7 @@
if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
continue;
- if (!wpa &&
+ if (!osen && !wpa &&
!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
@@ -834,6 +879,12 @@
continue;
}
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-OSEN network "
+ "not allowed");
+ continue;
+ }
+
if (!wpa_supplicant_match_privacy(bss, ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy "
"mismatch");
@@ -858,6 +909,39 @@
}
#ifdef CONFIG_P2P
+ if (ssid->p2p_group &&
+ !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+ !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen");
+ continue;
+ }
+
+ if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
+ struct wpabuf *p2p_ie;
+ u8 dev_addr[ETH_ALEN];
+
+ ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+ if (ie == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P element");
+ continue;
+ }
+ p2p_ie = wpa_bss_get_vendor_ie_multi(
+ bss, P2P_IE_VENDOR_TYPE);
+ if (p2p_ie == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - could not fetch P2P element");
+ continue;
+ }
+
+ if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0
+ || os_memcmp(dev_addr, ssid->go_p2p_dev_addr,
+ ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no matching GO P2P Device Address in P2P element");
+ wpabuf_free(p2p_ie);
+ continue;
+ }
+ wpabuf_free(p2p_ie);
+ }
+
/*
* TODO: skip the AP if its P2P IE has Group Formation
* bit set in the P2P Group Capability Bitmap and we
@@ -877,16 +961,22 @@
static struct wpa_bss *
wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
struct wpa_ssid *group,
- struct wpa_ssid **selected_ssid)
+ struct wpa_ssid **selected_ssid,
+ int only_first_ssid)
{
unsigned int i;
- wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
- group->priority);
+ if (only_first_ssid)
+ wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
+ group->id);
+ else
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
+ group->priority);
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
struct wpa_bss *bss = wpa_s->last_scan_res[i];
- *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
+ *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
+ only_first_ssid);
if (!*selected_ssid)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR
@@ -905,16 +995,36 @@
{
struct wpa_bss *selected = NULL;
int prio;
+ struct wpa_ssid *next_ssid = NULL;
if (wpa_s->last_scan_res == NULL ||
wpa_s->last_scan_res_used == 0)
return NULL; /* no scan results from last update */
+ if (wpa_s->next_ssid) {
+ struct wpa_ssid *ssid;
+
+ /* check that next_ssid is still valid */
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == wpa_s->next_ssid)
+ break;
+ }
+ next_ssid = ssid;
+ wpa_s->next_ssid = NULL;
+ }
+
while (selected == NULL) {
for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+ if (next_ssid && next_ssid->priority ==
+ wpa_s->conf->pssid[prio]->priority) {
+ selected = wpa_supplicant_select_bss(
+ wpa_s, next_ssid, selected_ssid, 1);
+ if (selected)
+ break;
+ }
selected = wpa_supplicant_select_bss(
wpa_s, wpa_s->conf->pssid[prio],
- selected_ssid);
+ selected_ssid, 0);
if (selected)
break;
}
@@ -945,9 +1055,6 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request "
"since there are no enabled networks");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
-#ifdef CONFIG_P2P
- wpa_s->sta_scan_pending = 0;
-#endif /* CONFIG_P2P */
return;
}
@@ -964,8 +1071,12 @@
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
#ifdef CONFIG_P2P
- if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1)
+ if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+ wpa_s->p2p_in_provisioning) {
+ eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb,
+ wpa_s, NULL);
return -1;
+ }
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS
@@ -1148,7 +1259,8 @@
union wpa_event_data *data,
int own_request)
{
- struct wpa_scan_results *scan_res;
+ struct wpa_scan_results *scan_res = NULL;
+ int ret = 0;
int ap = 0;
#ifndef CONFIG_NO_RANDOM_POOL
size_t i, num;
@@ -1161,23 +1273,6 @@
wpa_supplicant_notify_scanning(wpa_s, 0);
-#ifdef CONFIG_P2P
- if (own_request && wpa_s->global->p2p_cb_on_scan_complete &&
- !wpa_s->global->p2p_disabled &&
- wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending &&
- !wpa_s->scan_res_handler) {
- wpa_s->global->p2p_cb_on_scan_complete = 0;
- if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
- "stopped scan processing");
- wpa_s->sta_scan_pending = 1;
- wpa_supplicant_req_scan(wpa_s, 5, 0);
- return -1;
- }
- }
- wpa_s->sta_scan_pending = 0;
-#endif /* CONFIG_P2P */
-
scan_res = wpa_supplicant_get_scan_results(wpa_s,
data ? &data->scan_info :
NULL, 1);
@@ -1190,7 +1285,8 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
"scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
- return -1;
+ ret = -1;
+ goto scan_work_done;
}
#ifndef CONFIG_NO_RANDOM_POOL
@@ -1209,16 +1305,16 @@
}
#endif /* CONFIG_NO_RANDOM_POOL */
- if (own_request && wpa_s->scan_res_handler) {
+ if (own_request && wpa_s->scan_res_handler &&
+ (wpa_s->own_scan_running || !wpa_s->external_scan_running)) {
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
scan_res_handler = wpa_s->scan_res_handler;
wpa_s->scan_res_handler = NULL;
scan_res_handler(wpa_s, scan_res);
-
- wpa_scan_results_free(scan_res);
- return -2;
+ ret = -2;
+ goto scan_work_done;
}
if (ap) {
@@ -1227,53 +1323,72 @@
if (wpa_s->ap_iface->scan_cb)
wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
#endif /* CONFIG_AP */
- wpa_scan_results_free(scan_res);
- return 0;
+ goto scan_work_done;
}
- wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available");
- wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+ wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
+ wpa_s->own_scan_running, wpa_s->external_scan_running);
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
+ wpa_s->manual_scan_id);
+ wpa_s->manual_scan_use_id = 0;
+ } else {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+ }
wpas_notify_scan_results(wpa_s);
wpas_notify_scan_done(wpa_s, 1);
- if (sme_proc_obss_scan(wpa_s) > 0) {
+ if (!wpa_s->own_scan_running && wpa_s->external_scan_running) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
wpa_scan_results_free(scan_res);
return 0;
}
- if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
- wpa_scan_results_free(scan_res);
- return 0;
- }
+ if (sme_proc_obss_scan(wpa_s) > 0)
+ goto scan_work_done;
- if (autoscan_notify_scan(wpa_s, scan_res)) {
- wpa_scan_results_free(scan_res);
- return 0;
- }
+ if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
+ goto scan_work_done;
+
+ if (autoscan_notify_scan(wpa_s, scan_res))
+ goto scan_work_done;
if (wpa_s->disconnected) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
- wpa_scan_results_free(scan_res);
- return 0;
+ goto scan_work_done;
}
if (!wpas_driver_bss_selection(wpa_s) &&
- bgscan_notify_scan(wpa_s, scan_res) == 1) {
- wpa_scan_results_free(scan_res);
- return 0;
- }
+ bgscan_notify_scan(wpa_s, scan_res) == 1)
+ goto scan_work_done;
wpas_wps_update_ap_info(wpa_s, scan_res);
wpa_scan_results_free(scan_res);
- return wpas_select_network_from_last_scan(wpa_s, 1);
+ if (wpa_s->scan_work) {
+ struct wpa_radio_work *work = wpa_s->scan_work;
+ wpa_s->scan_work = NULL;
+ radio_work_done(work);
+ }
+
+ return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
+
+scan_work_done:
+ wpa_scan_results_free(scan_res);
+ if (wpa_s->scan_work) {
+ struct wpa_radio_work *work = wpa_s->scan_work;
+ wpa_s->scan_work = NULL;
+ radio_work_done(work);
+ }
+ return ret;
}
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
- int new_scan)
+ int new_scan, int own_request)
{
struct wpa_bss *selected;
struct wpa_ssid *ssid = NULL;
@@ -1308,17 +1423,25 @@
wpa_supplicant_associate(wpa_s, NULL, ssid);
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
- } else {
+ } else if (own_request) {
+ /*
+ * No SSID found. If SCAN results are as a result of
+ * own scan request and not due to a scan request on
+ * another shared interface, try another scan.
+ */
int timeout_sec = wpa_s->scan_interval;
int timeout_usec = 0;
#ifdef CONFIG_P2P
if (wpas_p2p_scan_no_go_seen(wpa_s) == 1)
return 0;
- if (wpa_s->p2p_in_provisioning) {
+ if (wpa_s->p2p_in_provisioning ||
+ wpa_s->show_group_started ||
+ wpa_s->p2p_in_invitation) {
/*
* Use shorter wait during P2P Provisioning
- * state to speed up group formation.
+ * state and during P2P join-a-group operation
+ * to speed up group formation.
*/
timeout_sec = 0;
timeout_usec = 250000;
@@ -1340,6 +1463,16 @@
return 1;
}
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_WPS
+ if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing");
+ timeout_sec = 0;
+ timeout_usec = 500000;
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+ return 0;
+ }
+#endif /* CONFIG_WPS */
if (wpa_supplicant_req_sched_scan(wpa_s))
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
@@ -1352,7 +1485,6 @@
static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- const char *rn, *rn2;
struct wpa_supplicant *ifs;
if (_wpa_supplicant_event_scan_results(wpa_s, data, 1) != 0) {
@@ -1367,25 +1499,12 @@
}
/*
- * Check other interfaces to see if they have the same radio-name. If
+ * Check other interfaces to see if they share the same radio. If
* so, they get updated with this same scan info.
*/
- if (!wpa_s->driver->get_radio_name)
- return;
-
- rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
- if (rn == NULL || rn[0] == '\0')
- return;
-
- wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces "
- "sharing same radio (%s) in event_scan_results", rn);
-
- for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
- if (ifs == wpa_s || !ifs->driver->get_radio_name)
- continue;
-
- rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
- if (rn2 && os_strcmp(rn, rn2) == 0) {
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (ifs != wpa_s) {
wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
"sibling", ifs->ifname);
_wpa_supplicant_event_scan_results(ifs, data, 0);
@@ -1401,18 +1520,18 @@
#ifdef CONFIG_NO_SCAN_PROCESSING
return -1;
#else /* CONFIG_NO_SCAN_PROCESSING */
- struct os_time now;
+ struct os_reltime now;
if (wpa_s->last_scan_res_used <= 0)
return -1;
- os_get_time(&now);
- if (now.sec - wpa_s->last_scan.sec > 5) {
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &wpa_s->last_scan, 5)) {
wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
return -1;
}
- return wpas_select_network_from_last_scan(wpa_s, 0);
+ return wpas_select_network_from_last_scan(wpa_s, 0, 1);
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
@@ -1496,6 +1615,43 @@
}
+#ifdef CONFIG_INTERWORKING
+
+static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
+ size_t len)
+{
+ int res;
+
+ wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len);
+ res = wpa_drv_set_qos_map(wpa_s, qos_map, len);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver");
+ }
+
+ return res;
+}
+
+
+static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
+ const u8 *ies, size_t ies_len)
+{
+ struct ieee802_11_elems elems;
+
+ if (ies == NULL)
+ return;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
+ return;
+
+ if (elems.qos_map_set) {
+ wpas_qos_map_set(wpa_s, elems.qos_map_set,
+ elems.qos_map_set_len);
+ }
+}
+
+#endif /* CONFIG_INTERWORKING */
+
+
static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
@@ -1520,6 +1676,10 @@
wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_WNM */
+#ifdef CONFIG_INTERWORKING
+ interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#endif /* CONFIG_INTERWORKING */
}
if (data->assoc_info.beacon_ies)
wpa_hexdump(MSG_DEBUG, "beacon_ies",
@@ -1688,21 +1848,6 @@
}
-static struct wpa_bss * wpa_supplicant_get_new_bss(
- struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
- struct wpa_bss *bss = NULL;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
-
- if (ssid->ssid_len > 0)
- bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
- if (!bss)
- bss = wpa_bss_get_bssid(wpa_s, bssid);
-
- return bss;
-}
-
-
static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
{
const u8 *bss_wpa = NULL, *bss_rsn = NULL;
@@ -1735,6 +1880,8 @@
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
+ if (!data)
+ return;
hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
data->assoc_info.addr,
data->assoc_info.req_ies,
@@ -1772,20 +1919,6 @@
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
}
- if (wpa_s->current_ssid) {
- struct wpa_bss *bss = NULL;
-
- bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
- if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
-
- /* Get the BSS from the new scan results */
- bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
- }
-
- if (bss)
- wpa_s->current_bss = bss;
- }
#ifdef ANDROID
if (wpa_s->conf->ap_scan == 1) {
@@ -1802,6 +1935,7 @@
#ifdef CONFIG_SME
os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
wpa_s->sme.prev_bssid_set = 1;
+ wpa_s->sme.last_unprot_disconnect.sec = 0;
#endif /* CONFIG_SME */
wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid));
@@ -1837,7 +1971,8 @@
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
(wpa_s->current_ssid &&
wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) {
- if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
+ if (wpa_s->current_ssid &&
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
/*
@@ -1887,9 +2022,9 @@
wpa_s->last_eapol_matches_bssid = 0;
if (wpa_s->pending_eapol_rx) {
- struct os_time now, age;
- os_get_time(&now);
- os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
+ struct os_reltime now, age;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
if (age.sec == 0 && age.usec < 100000 &&
os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
0) {
@@ -2002,9 +2137,7 @@
int authenticating;
u8 prev_pending_bssid[ETH_ALEN];
struct wpa_bss *fast_reconnect = NULL;
-#ifndef CONFIG_NO_SCAN_PROCESSING
struct wpa_ssid *fast_reconnect_ssid = NULL;
-#endif /* CONFIG_NO_SCAN_PROCESSING */
struct wpa_ssid *last_ssid;
authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
@@ -2026,7 +2159,7 @@
"pre-shared key may be incorrect");
if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
return; /* P2P group removed */
- wpas_auth_failed(wpa_s);
+ wpas_auth_failed(wpa_s, "WRONG_KEY");
}
if (!wpa_s->disconnected &&
(!wpa_s->auto_reconnect_disabled ||
@@ -2047,15 +2180,9 @@
* time for some common cases.
*/
fast_reconnect = wpa_s->current_bss;
-#ifndef CONFIG_NO_SCAN_PROCESSING
fast_reconnect_ssid = wpa_s->current_ssid;
-#endif /* CONFIG_NO_SCAN_PROCESSING */
} else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
-#ifdef ANDROID
- wpa_supplicant_req_scan(wpa_s, 0, 500000);
-#else
wpa_supplicant_req_scan(wpa_s, 0, 100000);
-#endif
else
wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
"immediate scan");
@@ -2079,7 +2206,6 @@
wpas_notify_disconnect_reason(wpa_s);
if (wpa_supplicant_dynamic_keys(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
- wpa_s->keys_cleared = 0;
wpa_clear_keys(wpa_s, wpa_s->bssid);
}
last_ssid = wpa_s->current_ssid;
@@ -2090,7 +2216,12 @@
wpa_s->current_ssid = last_ssid;
}
- if (fast_reconnect) {
+ if (fast_reconnect &&
+ !wpas_network_disabled(wpa_s, fast_reconnect_ssid) &&
+ !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
+ !disallowed_ssid(wpa_s, fast_reconnect->ssid,
+ fast_reconnect->ssid_len) &&
+ !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) {
#ifndef CONFIG_NO_SCAN_PROCESSING
wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
if (wpa_supplicant_connect(wpa_s, fast_reconnect,
@@ -2099,6 +2230,14 @@
wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
#endif /* CONFIG_NO_SCAN_PROCESSING */
+ } else if (fast_reconnect) {
+ /*
+ * Could not reconnect to the same BSS due to network being
+ * disabled. Use a new scan to match the alternative behavior
+ * above, i.e., to continue automatic reconnection attempt in a
+ * way that enforces disabled network rules.
+ */
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
}
@@ -2123,13 +2262,13 @@
union wpa_event_data *data)
{
int pairwise;
- struct os_time t;
+ struct os_reltime t;
wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
pairwise = (data && data->michael_mic_failure.unicast);
- os_get_time(&t);
- if ((wpa_s->last_michael_mic_error &&
- t.sec - wpa_s->last_michael_mic_error <= 60) ||
+ os_get_reltime(&t);
+ if ((wpa_s->last_michael_mic_error.sec &&
+ !os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) ||
wpa_s->pending_mic_error_report) {
if (wpa_s->pending_mic_error_report) {
/*
@@ -2207,7 +2346,7 @@
wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
}
- wpa_s->last_michael_mic_error = t.sec;
+ wpa_s->last_michael_mic_error = t;
wpa_s->mic_errors_seen++;
}
@@ -2242,7 +2381,6 @@
wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
"driver after interface was added");
}
- wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
break;
case EVENT_INTERFACE_REMOVED:
wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
@@ -2251,10 +2389,6 @@
wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = NULL;
-#ifdef CONFIG_IBSS_RSN
- ibss_rsn_deinit(wpa_s->ibss_rsn);
- wpa_s->ibss_rsn = NULL;
-#endif /* CONFIG_IBSS_RSN */
#ifdef CONFIG_TERMINATE_ONLASTIF
/* check if last interface */
if (!any_interfaces(wpa_s->global->ifaces))
@@ -2478,11 +2612,12 @@
wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated);
- if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
- ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
- (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
- eapol_sm_failed(wpa_s->eapol)))
- wpas_auth_failed(wpa_s);
+ if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
+ ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+ (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
+ eapol_sm_failed(wpa_s->eapol))) &&
+ !wpa_s->eap_expected_failure))
+ wpas_auth_failed(wpa_s, "AUTH_FAILED");
#ifdef CONFIG_P2P
if (deauth && reason_code > 0) {
@@ -2591,6 +2726,212 @@
}
+static const char * reg_init_str(enum reg_change_initiator init)
+{
+ switch (init) {
+ case REGDOM_SET_BY_CORE:
+ return "CORE";
+ case REGDOM_SET_BY_USER:
+ return "USER";
+ case REGDOM_SET_BY_DRIVER:
+ return "DRIVER";
+ case REGDOM_SET_BY_COUNTRY_IE:
+ return "COUNTRY_IE";
+ case REGDOM_BEACON_HINT:
+ return "BEACON_HINT";
+ }
+ return "?";
+}
+
+
+static const char * reg_type_str(enum reg_type type)
+{
+ switch (type) {
+ case REGDOM_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case REGDOM_TYPE_COUNTRY:
+ return "COUNTRY";
+ case REGDOM_TYPE_WORLD:
+ return "WORLD";
+ case REGDOM_TYPE_CUSTOM_WORLD:
+ return "CUSTOM_WORLD";
+ case REGDOM_TYPE_INTERSECTION:
+ return "INTERSECTION";
+ }
+ return "?";
+}
+
+
+static void wpa_supplicant_update_channel_list(
+ struct wpa_supplicant *wpa_s, struct channel_list_changed *info)
+{
+ struct wpa_supplicant *ifs;
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
+ reg_init_str(info->initiator), reg_type_str(info->type),
+ info->alpha2[0] ? " alpha2=" : "",
+ info->alpha2[0] ? info->alpha2 : "");
+
+ if (wpa_s->drv_priv == NULL)
+ return; /* Ignore event during drv initialization */
+
+ free_hw_features(wpa_s);
+ wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
+ wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
+
+ wpas_p2p_update_channel_list(wpa_s);
+
+ /*
+ * Check other interfaces to see if they share the same radio. If
+ * so, they get updated with this same hw mode info.
+ */
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (ifs != wpa_s) {
+ wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
+ ifs->ifname);
+ free_hw_features(ifs);
+ ifs->hw.modes = wpa_drv_get_hw_feature_data(
+ ifs, &ifs->hw.num_modes, &ifs->hw.flags);
+ }
+ }
+}
+
+
+static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
+ const u8 *frame, size_t len, int freq)
+{
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *payload;
+ size_t plen;
+ u8 category;
+
+ if (len < IEEE80211_HDRLEN + 2)
+ return;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ payload = frame + IEEE80211_HDRLEN;
+ category = *payload++;
+ plen = len - IEEE80211_HDRLEN - 1;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
+ " Category=%u DataLen=%d freq=%d MHz",
+ MAC2STR(mgmt->sa), category, (int) plen, freq);
+
+#ifdef CONFIG_IEEE80211R
+ if (category == WLAN_ACTION_FT) {
+ ft_rx_action(wpa_s, payload, plen);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+#ifdef CONFIG_SME
+ if (category == WLAN_ACTION_SA_QUERY) {
+ sme_sa_query_rx(wpa_s, mgmt->sa, payload, plen);
+ return;
+ }
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_WNM
+ if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+ ieee802_11_rx_wnm_action(wpa_s, mgmt, len);
+ return;
+ }
+#endif /* CONFIG_WNM */
+
+#ifdef CONFIG_GAS
+ if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
+ mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
+ gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ payload, plen, freq) == 0)
+ return;
+#endif /* CONFIG_GAS */
+
+#ifdef CONFIG_TDLS
+ if (category == WLAN_ACTION_PUBLIC && plen >= 4 &&
+ payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "TDLS: Received Discovery Response from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_INTERWORKING
+ if (category == WLAN_ACTION_QOS && plen >= 1 &&
+ payload[0] == QOS_QOS_MAP_CONFIG) {
+ const u8 *pos = payload + 1;
+ size_t qlen = plen - 1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 &&
+ qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET &&
+ pos[1] <= qlen - 2 && pos[1] >= 16)
+ wpas_qos_map_set(wpa_s, pos + 2, pos[1]);
+ return;
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+ category, payload, plen, freq);
+}
+
+
+static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *event)
+{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *ifs;
+#endif /* CONFIG_P2P */
+ struct wpa_freq_range_list *list;
+ char *str = NULL;
+
+ list = &event->freq_range;
+
+ if (list->num)
+ str = freq_range_list_str(list);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s",
+ str ? str : "");
+
+#ifdef CONFIG_P2P
+ if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) {
+ wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range",
+ __func__);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
+ wpas_p2p_update_channel_list(wpa_s);
+ }
+
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ int freq;
+ if (!ifs->current_ssid ||
+ !ifs->current_ssid->p2p_group ||
+ (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
+ ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
+ continue;
+
+ freq = ifs->current_ssid->frequency;
+ if (!freq_range_list_includes(list, freq)) {
+ wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range",
+ freq);
+ continue;
+ }
+
+ wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz",
+ freq);
+ /* TODO: Consider using CSA or removing the group within
+ * wpa_supplicant */
+ wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+ }
+#endif /* CONFIG_P2P */
+
+ os_free(str);
+}
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
@@ -2644,11 +2985,44 @@
wpa_supplicant_event_michael_mic_failure(wpa_s, data);
break;
#ifndef CONFIG_NO_SCAN_PROCESSING
+ case EVENT_SCAN_STARTED:
+ os_get_reltime(&wpa_s->scan_start_time);
+ if (wpa_s->own_scan_requested) {
+ struct os_reltime diff;
+
+ os_reltime_sub(&wpa_s->scan_start_time,
+ &wpa_s->scan_trigger_time, &diff);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
+ diff.sec, diff.usec);
+ wpa_s->own_scan_requested = 0;
+ wpa_s->own_scan_running = 1;
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_use_id) {
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED "id=%u",
+ wpa_s->manual_scan_id);
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
+ }
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan");
+ wpa_s->external_scan_running = 1;
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
+ }
+ break;
case EVENT_SCAN_RESULTS:
+ if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+ struct os_reltime now, diff;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
+ wpa_s->scan_start_time.sec = 0;
+ wpa_s->scan_start_time.usec = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
+ diff.sec, diff.usec);
+ }
wpa_supplicant_event_scan_results(wpa_s, data);
- if (wpa_s->wpa_state != WPA_AUTHENTICATING &&
- wpa_s->wpa_state != WPA_ASSOCIATING)
- wpas_p2p_continue_after_scan(wpa_s);
+ wpa_s->own_scan_running = 0;
+ wpa_s->external_scan_running = 0;
+ radio_work_check_next(wpa_s);
break;
#endif /* CONFIG_NO_SCAN_PROCESSING */
case EVENT_ASSOCINFO:
@@ -2698,52 +3072,11 @@
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
sme_event_assoc_reject(wpa_s, data);
else {
-#ifdef ANDROID_P2P
- if(!wpa_s->current_ssid) {
- wpa_printf(MSG_ERROR, "current_ssid == NULL");
- break;
- }
- /* If assoc reject is reported by the driver, then avoid
- * waiting for the authentication timeout. Cancel the
- * authentication timeout and retry the assoc.
- */
- if(wpa_s->current_ssid->assoc_retry++ < 10) {
- wpa_printf(MSG_ERROR, "Retrying assoc: %d ",
- wpa_s->current_ssid->assoc_retry);
-
- wpa_supplicant_cancel_auth_timeout(wpa_s);
-
- /* Clear the states */
- wpa_sm_notify_disassoc(wpa_s->wpa);
- wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
-
- wpa_s->reassociate = 1;
- if (wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE) {
- const u8 *bl_bssid = data->assoc_reject.bssid;
- if (!bl_bssid || is_zero_ether_addr(bl_bssid))
- bl_bssid = wpa_s->pending_bssid;
- wpa_blacklist_add(wpa_s, bl_bssid);
- wpa_supplicant_req_scan(wpa_s, 0, 0);
- } else {
- wpa_supplicant_req_scan(wpa_s, 1, 0);
- }
- } else if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
- /* If we ASSOC_REJECT's hits threshold, disable the
- * network
- */
- wpa_printf(MSG_ERROR, "Assoc retry threshold reached. "
- "Disabling the network");
- wpa_s->current_ssid->assoc_retry = 0;
- wpa_supplicant_disable_network(wpa_s, wpa_s->current_ssid);
- wpas_p2p_group_remove(wpa_s, wpa_s->ifname);
- }
-#else
const u8 *bssid = data->assoc_reject.bssid;
if (bssid == NULL || is_zero_ether_addr(bssid))
bssid = wpa_s->pending_bssid;
wpas_connection_failed(wpa_s, bssid);
wpa_supplicant_mark_disassoc(wpa_s);
-#endif /* ANDROID_P2P */
}
break;
case EVENT_AUTH_TIMED_OUT:
@@ -2840,14 +3173,33 @@
wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
data->ch_switch.ht_enabled,
- data->ch_switch.ch_offset);
+ data->ch_switch.ch_offset,
+ data->ch_switch.ch_width,
+ data->ch_switch.cf1,
+ data->ch_switch.cf2);
break;
#endif /* CONFIG_AP */
-#if defined(CONFIG_AP) || defined(CONFIG_IBSS_RSN)
case EVENT_RX_MGMT: {
u16 fc, stype;
const struct ieee80211_mgmt *mgmt;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->ext_mgmt_frame_handling) {
+ struct rx_mgmt *rx = &data->rx_mgmt;
+ size_t hex_len = 2 * rx->frame_len + 1;
+ char *hex = os_malloc(hex_len);
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len,
+ rx->frame, rx->frame_len);
+ wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s",
+ rx->freq, rx->datarate, rx->ssi_signal,
+ hex);
+ os_free(hex);
+ }
+ break;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
mgmt = (const struct ieee80211_mgmt *)
data->rx_mgmt.frame;
fc = le_to_host16(mgmt->frame_control);
@@ -2878,6 +3230,15 @@
break;
}
#endif /* CONFIG_IBSS_RSN */
+
+ if (stype == WLAN_FC_STYPE_ACTION) {
+ wpas_event_rx_mgmt_action(
+ wpa_s, data->rx_mgmt.frame,
+ data->rx_mgmt.frame_len,
+ data->rx_mgmt.freq);
+ break;
+ }
+
wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
"management frame in non-AP mode");
break;
@@ -2900,63 +3261,6 @@
#endif /* CONFIG_AP */
break;
}
-#endif /* CONFIG_AP || CONFIG_IBSS_RSN */
- case EVENT_RX_ACTION:
- wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
- " Category=%u DataLen=%d freq=%d MHz",
- MAC2STR(data->rx_action.sa),
- data->rx_action.category, (int) data->rx_action.len,
- data->rx_action.freq);
-#ifdef CONFIG_IEEE80211R
- if (data->rx_action.category == WLAN_ACTION_FT) {
- ft_rx_action(wpa_s, data->rx_action.data,
- data->rx_action.len);
- break;
- }
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
-#ifdef CONFIG_SME
- if (data->rx_action.category == WLAN_ACTION_SA_QUERY) {
- sme_sa_query_rx(wpa_s, data->rx_action.sa,
- data->rx_action.data,
- data->rx_action.len);
- break;
- }
-#endif /* CONFIG_SME */
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
- if (data->rx_action.category == WLAN_ACTION_WNM) {
- ieee802_11_rx_wnm_action(wpa_s, &data->rx_action);
- break;
- }
-#endif /* CONFIG_WNM */
-#ifdef CONFIG_GAS
- if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
- gas_query_rx(wpa_s->gas, data->rx_action.da,
- data->rx_action.sa, data->rx_action.bssid,
- data->rx_action.data, data->rx_action.len,
- data->rx_action.freq) == 0)
- break;
-#endif /* CONFIG_GAS */
-#ifdef CONFIG_TDLS
- if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
- data->rx_action.len >= 4 &&
- data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
- wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery "
- "Response from " MACSTR,
- MAC2STR(data->rx_action.sa));
- break;
- }
-#endif /* CONFIG_TDLS */
-#ifdef CONFIG_P2P
- wpas_p2p_rx_action(wpa_s, data->rx_action.da,
- data->rx_action.sa,
- data->rx_action.bssid,
- data->rx_action.category,
- data->rx_action.data,
- data->rx_action.len, data->rx_action.freq);
-#endif /* CONFIG_P2P */
- break;
case EVENT_RX_PROBE_REQ:
if (data->rx_probe_req.sa == NULL ||
data->rx_probe_req.ie == NULL)
@@ -2973,14 +3277,12 @@
break;
}
#endif /* CONFIG_AP */
-#ifdef CONFIG_P2P
wpas_p2p_probe_req_rx(wpa_s, 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);
-#endif /* CONFIG_P2P */
break;
case EVENT_REMAIN_ON_CHANNEL:
#ifdef CONFIG_OFFCHANNEL
@@ -2988,93 +3290,30 @@
wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration);
#endif /* CONFIG_OFFCHANNEL */
-#ifdef CONFIG_P2P
wpas_p2p_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration);
-#endif /* CONFIG_P2P */
break;
case EVENT_CANCEL_REMAIN_ON_CHANNEL:
#ifdef CONFIG_OFFCHANNEL
offchannel_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq);
#endif /* CONFIG_OFFCHANNEL */
-#ifdef CONFIG_P2P
wpas_p2p_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq);
-#endif /* CONFIG_P2P */
break;
-#ifdef CONFIG_P2P
- case EVENT_P2P_DEV_FOUND: {
- struct p2p_peer_info peer_info;
-
- os_memset(&peer_info, 0, sizeof(peer_info));
- if (data->p2p_dev_found.dev_addr)
- os_memcpy(peer_info.p2p_device_addr,
- data->p2p_dev_found.dev_addr, ETH_ALEN);
- if (data->p2p_dev_found.pri_dev_type)
- os_memcpy(peer_info.pri_dev_type,
- data->p2p_dev_found.pri_dev_type,
- sizeof(peer_info.pri_dev_type));
- if (data->p2p_dev_found.dev_name)
- os_strlcpy(peer_info.device_name,
- data->p2p_dev_found.dev_name,
- sizeof(peer_info.device_name));
- peer_info.config_methods = data->p2p_dev_found.config_methods;
- peer_info.dev_capab = data->p2p_dev_found.dev_capab;
- peer_info.group_capab = data->p2p_dev_found.group_capab;
-
- /*
- * FIX: new_device=1 is not necessarily correct. We should
- * maintain a P2P peer database in wpa_supplicant and update
- * this information based on whether the peer is truly new.
- */
- wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1);
- break;
- }
- case EVENT_P2P_GO_NEG_REQ_RX:
- wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src,
- data->p2p_go_neg_req_rx.dev_passwd_id);
- break;
- case EVENT_P2P_GO_NEG_COMPLETED:
- wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res);
- break;
- case EVENT_P2P_PROV_DISC_REQUEST:
- wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer,
- data->p2p_prov_disc_req.config_methods,
- data->p2p_prov_disc_req.dev_addr,
- data->p2p_prov_disc_req.pri_dev_type,
- data->p2p_prov_disc_req.dev_name,
- data->p2p_prov_disc_req.supp_config_methods,
- data->p2p_prov_disc_req.dev_capab,
- data->p2p_prov_disc_req.group_capab,
- NULL, 0);
- break;
- case EVENT_P2P_PROV_DISC_RESPONSE:
- wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer,
- data->p2p_prov_disc_resp.config_methods);
- break;
- case EVENT_P2P_SD_REQUEST:
- wpas_sd_request(wpa_s, data->p2p_sd_req.freq,
- data->p2p_sd_req.sa,
- data->p2p_sd_req.dialog_token,
- data->p2p_sd_req.update_indic,
- data->p2p_sd_req.tlvs,
- data->p2p_sd_req.tlvs_len);
- break;
- case EVENT_P2P_SD_RESPONSE:
- wpas_sd_response(wpa_s, data->p2p_sd_resp.sa,
- data->p2p_sd_resp.update_indic,
- data->p2p_sd_resp.tlvs,
- data->p2p_sd_resp.tlvs_len);
- break;
-#endif /* CONFIG_P2P */
case EVENT_EAPOL_RX:
wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
data->eapol_rx.data,
data->eapol_rx.data_len);
break;
case EVENT_SIGNAL_CHANGE:
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
+ "above=%d signal=%d noise=%d txrate=%d",
+ data->signal_change.above_threshold,
+ data->signal_change.current_signal,
+ data->signal_change.current_noise,
+ data->signal_change.current_txrate);
bgscan_notify_signal_change(
wpa_s, data->signal_change.above_threshold,
data->signal_change.current_signal,
@@ -3085,6 +3324,12 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_supplicant_update_mac_addr(wpa_s);
+ if (wpa_s->p2p_mgmt) {
+ wpa_supplicant_set_state(wpa_s,
+ WPA_DISCONNECTED);
+ break;
+ }
+
#ifdef CONFIG_AP
if (!wpa_s->ap_iface) {
wpa_supplicant_set_state(wpa_s,
@@ -3101,25 +3346,46 @@
break;
case EVENT_INTERFACE_DISABLED:
wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled");
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
+ (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group &&
+ wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) {
+ /*
+ * The interface was externally disabled. Remove
+ * it assuming an external entity will start a
+ * new session if needed.
+ */
+ wpas_p2p_disconnect(wpa_s);
+ break;
+ }
+ if (wpa_s->p2p_scan_work && wpa_s->global->p2p &&
+ p2p_in_progress(wpa_s->global->p2p) > 1) {
+ /* This radio work will be cancelled, so clear P2P
+ * state as well.
+ */
+ p2p_stop_find(wpa_s->global->p2p);
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ /*
+ * Indicate disconnection to keep ctrl_iface events
+ * consistent.
+ */
+ wpa_supplicant_event_disassoc(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1);
+ }
wpa_supplicant_mark_disassoc(wpa_s);
+ radio_remove_works(wpa_s, NULL, 0);
+
wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
break;
case EVENT_CHANNEL_LIST_CHANGED:
- if (wpa_s->drv_priv == NULL)
- break; /* Ignore event during drv initialization */
-
- free_hw_features(wpa_s);
- wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
- wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
-
-#ifdef CONFIG_P2P
- wpas_p2p_update_channel_list(wpa_s);
-#endif /* CONFIG_P2P */
+ wpa_supplicant_update_channel_list(
+ wpa_s, &data->channel_list_changed);
break;
case EVENT_INTERFACE_UNAVAILABLE:
-#ifdef CONFIG_P2P
wpas_p2p_interface_unavailable(wpa_s);
-#endif /* CONFIG_P2P */
break;
case EVENT_BEST_CHANNEL:
wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
@@ -3129,11 +3395,9 @@
wpa_s->best_24_freq = data->best_chan.freq_24;
wpa_s->best_5_freq = data->best_chan.freq_5;
wpa_s->best_overall_freq = data->best_chan.freq_overall;
-#ifdef CONFIG_P2P
wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
data->best_chan.freq_5,
data->best_chan.freq_overall);
-#endif /* CONFIG_P2P */
break;
case EVENT_UNPROT_DEAUTH:
wpa_supplicant_event_unprot_deauth(wpa_s,
@@ -3151,7 +3415,8 @@
#endif /* CONFIG_AP */
#ifdef CONFIG_TDLS
if (data)
- wpa_tdls_disable_link(wpa_s->wpa, data->low_ack.addr);
+ wpa_tdls_disable_unreachable_link(wpa_s->wpa,
+ data->low_ack.addr);
#endif /* CONFIG_TDLS */
break;
case EVENT_IBSS_PEER_LOST:
@@ -3169,6 +3434,7 @@
data->driver_gtk_rekey.replay_ctr);
break;
case EVENT_SCHED_SCAN_STOPPED:
+ wpa_s->pno = 0;
wpa_s->sched_scanning = 0;
wpa_supplicant_notify_scanning(wpa_s, 0);
@@ -3176,17 +3442,25 @@
break;
/*
- * If we timed out, start a new sched scan to continue
- * searching for more SSIDs.
+ * Start a new sched scan to continue searching for more SSIDs
+ * either if timed out or PNO schedule scan is pending.
*/
- if (wpa_s->sched_scan_timed_out)
+ if (wpa_s->sched_scan_timed_out) {
wpa_supplicant_req_sched_scan(wpa_s);
+ } else if (wpa_s->pno_sched_pending) {
+ wpa_s->pno_sched_pending = 0;
+ wpas_start_pno(wpa_s);
+ }
+
break;
case EVENT_WPS_BUTTON_PUSHED:
#ifdef CONFIG_WPS
wpas_wps_start_pbc(wpa_s, NULL, 0);
#endif /* CONFIG_WPS */
break;
+ case EVENT_AVOID_FREQUENCIES:
+ wpa_supplicant_notify_avoid_freq(wpa_s, data);
+ break;
case EVENT_CONNECT_FAILED_REASON:
#ifdef CONFIG_AP
if (!wpa_s->ap_iface || !data)
diff --git a/wpa_supplicant/examples/p2p-action.sh b/wpa_supplicant/examples/p2p-action.sh
index 8759f54..797d43a 100755
--- a/wpa_supplicant/examples/p2p-action.sh
+++ b/wpa_supplicant/examples/p2p-action.sh
@@ -34,13 +34,26 @@
# start with -z to avoid that
dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
-i $GIFNAME \
- -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z
+ -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z -p 0
fi
fi
if [ "$4" = "client" ]; then
kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
rm /var/run/dhclient.leases-$GIFNAME
kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+ ipaddr=`echo "$*" | sed 's/.* ip_addr=\([^ ]*\).*/\1/'`
+ ipmask=`echo "$*" | sed 's/.* ip_mask=\([^ ]*\).*/\1/'`
+ goipaddr=`echo "$*" | sed 's/.* go_ip_addr=\([^ ]*\).*/\1/'`
+ if echo "$ipaddr$ipmask$goipaddr" | grep -q ' '; then
+ ipaddr=""
+ ipmask=""
+ goipaddr=""
+ fi
+ if [ -n "$ipaddr" ]; then
+ sudo ifconfig $GIFNAME "$ipaddr" netmask "$ipmask"
+ sudo ip ro re default via "$goipaddr"
+ exit 0
+ fi
dhclient -pf /var/run/dhclient-$GIFNAME.pid \
-lf /var/run/dhclient.leases-$GIFNAME \
-nw \
diff --git a/wpa_supplicant/examples/p2p-nfc.py b/wpa_supplicant/examples/p2p-nfc.py
new file mode 100644
index 0000000..91eba28
--- /dev/null
+++ b/wpa_supplicant/examples/p2p-nfc.py
@@ -0,0 +1,654 @@
+#!/usr/bin/python
+#
+# Example nfcpy to wpa_supplicant wrapper for P2P NFC operations
+# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import time
+import random
+import threading
+import argparse
+
+import nfc
+import nfc.ndef
+import nfc.llcp
+import nfc.handover
+
+import logging
+
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+ifname = None
+init_on_touch = False
+in_raw_mode = False
+prev_tcgetattr = 0
+include_wps_req = True
+include_p2p_req = True
+no_input = False
+srv = None
+continue_loop = True
+terminate_now = False
+summary_file = None
+success_file = None
+
+def summary(txt):
+ print txt
+ if summary_file:
+ with open(summary_file, 'a') as f:
+ f.write(txt + "\n")
+
+def success_report(txt):
+ summary(txt)
+ if success_file:
+ with open(success_file, 'a') as f:
+ f.write(txt + "\n")
+
+def wpas_connect():
+ ifaces = []
+ if os.path.isdir(wpas_ctrl):
+ try:
+ ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+ except OSError, error:
+ print "Could not find wpa_supplicant: ", error
+ return None
+
+ if len(ifaces) < 1:
+ print "No wpa_supplicant control interface found"
+ return None
+
+ for ctrl in ifaces:
+ if ifname:
+ if ifname not in ctrl:
+ continue
+ try:
+ print "Trying to use control interface " + ctrl
+ wpas = wpaspy.Ctrl(ctrl)
+ return wpas
+ except Exception, e:
+ pass
+ return None
+
+
+def wpas_tag_read(message):
+ wpas = wpas_connect()
+ if (wpas == None):
+ return False
+ cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex")
+ global force_freq
+ if force_freq:
+ cmd = cmd + " freq=" + force_freq
+ if "FAIL" in wpas.request(cmd):
+ return False
+ return True
+
+
+def wpas_get_handover_req():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in res:
+ return None
+ return res.decode("hex")
+
+def wpas_get_handover_req_wps():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ res = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in res:
+ return None
+ return res.decode("hex")
+
+
+def wpas_get_handover_sel(tag=False):
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ if tag:
+ res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ else:
+ res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in res:
+ return None
+ return res.decode("hex")
+
+
+def wpas_get_handover_sel_wps():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR");
+ if "FAIL" in res:
+ return None
+ return res.rstrip().decode("hex")
+
+
+def wpas_report_handover(req, sel, type):
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex")
+ global force_freq
+ if force_freq:
+ cmd = cmd + " freq=" + force_freq
+ return wpas.request(cmd)
+
+
+def wpas_report_handover_wsc(req, sel, type):
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex")
+ if force_freq:
+ cmd = cmd + " freq=" + force_freq
+ return wpas.request(cmd)
+
+
+def p2p_handover_client(llc):
+ message = nfc.ndef.HandoverRequestMessage(version="1.2")
+ message.nonce = random.randint(0, 0xffff)
+
+ global include_p2p_req
+ if include_p2p_req:
+ data = wpas_get_handover_req()
+ if (data == None):
+ summary("Could not get handover request carrier record from wpa_supplicant")
+ return
+ print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
+ datamsg = nfc.ndef.Message(data)
+ message.add_carrier(datamsg[0], "active", datamsg[1:])
+
+ global include_wps_req
+ if include_wps_req:
+ print "Handover request (pre-WPS):"
+ try:
+ print message.pretty()
+ except Exception, e:
+ print e
+
+ data = wpas_get_handover_req_wps()
+ if data:
+ print "Add WPS request in addition to P2P"
+ datamsg = nfc.ndef.Message(data)
+ message.add_carrier(datamsg[0], "active", datamsg[1:])
+
+ print "Handover request:"
+ try:
+ print message.pretty()
+ except Exception, e:
+ print e
+ print str(message).encode("hex")
+
+ client = nfc.handover.HandoverClient(llc)
+ try:
+ summary("Trying to initiate NFC connection handover")
+ client.connect()
+ summary("Connected for handover")
+ except nfc.llcp.ConnectRefused:
+ summary("Handover connection refused")
+ client.close()
+ return
+ except Exception, e:
+ summary("Other exception: " + str(e))
+ client.close()
+ return
+
+ summary("Sending handover request")
+
+ if not client.send(message):
+ summary("Failed to send handover request")
+ client.close()
+ return
+
+ summary("Receiving handover response")
+ message = client._recv()
+ if message is None:
+ summary("No response received")
+ client.close()
+ return
+ if message.type != "urn:nfc:wkt:Hs":
+ summary("Response was not Hs - received: " + message.type)
+ client.close()
+ return
+
+ print "Received message"
+ try:
+ print message.pretty()
+ except Exception, e:
+ print e
+ print str(message).encode("hex")
+ message = nfc.ndef.HandoverSelectMessage(message)
+ summary("Handover select received")
+ try:
+ print message.pretty()
+ except Exception, e:
+ print e
+
+ for carrier in message.carriers:
+ print "Remote carrier type: " + carrier.type
+ if carrier.type == "application/vnd.wfa.p2p":
+ print "P2P carrier type match - send to wpa_supplicant"
+ if "OK" in wpas_report_handover(data, carrier.record, "INIT"):
+ success_report("P2P handover reported successfully (initiator)")
+ else:
+ summary("P2P handover report rejected")
+ break
+
+ print "Remove peer"
+ client.close()
+ print "Done with handover"
+ global only_one
+ if only_one:
+ print "only_one -> stop loop"
+ global continue_loop
+ continue_loop = False
+
+ global no_wait
+ if no_wait:
+ print "Trying to exit.."
+ global terminate_now
+ terminate_now = True
+
+
+class HandoverServer(nfc.handover.HandoverServer):
+ def __init__(self, llc):
+ super(HandoverServer, self).__init__(llc)
+ self.sent_carrier = None
+ self.ho_server_processing = False
+ self.success = False
+
+ # override to avoid parser error in request/response.pretty() in nfcpy
+ # due to new WSC handover format
+ def _process_request(self, request):
+ summary("received handover request {}".format(request.type))
+ response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+ if not request.type == 'urn:nfc:wkt:Hr':
+ summary("not a handover request")
+ else:
+ try:
+ request = nfc.ndef.HandoverRequestMessage(request)
+ except nfc.ndef.DecodeError as e:
+ summary("error decoding 'Hr' message: {}".format(e))
+ else:
+ response = self.process_request(request)
+ summary("send handover response {}".format(response.type))
+ return response
+
+ def process_request(self, request):
+ self.ho_server_processing = True
+ clear_raw_mode()
+ print "HandoverServer - request received"
+ try:
+ print "Parsed handover request: " + request.pretty()
+ except Exception, e:
+ print e
+
+ sel = nfc.ndef.HandoverSelectMessage(version="1.2")
+
+ found = False
+
+ for carrier in request.carriers:
+ print "Remote carrier type: " + carrier.type
+ if carrier.type == "application/vnd.wfa.p2p":
+ print "P2P carrier type match - add P2P carrier record"
+ found = True
+ self.received_carrier = carrier.record
+ print "Carrier record:"
+ try:
+ print carrier.record.pretty()
+ except Exception, e:
+ print e
+ data = wpas_get_handover_sel()
+ if data is None:
+ print "Could not get handover select carrier record from wpa_supplicant"
+ continue
+ print "Handover select carrier record from wpa_supplicant:"
+ print data.encode("hex")
+ self.sent_carrier = data
+ if "OK" in wpas_report_handover(self.received_carrier, self.sent_carrier, "RESP"):
+ success_report("P2P handover reported successfully (responder)")
+ else:
+ summary("P2P handover report rejected")
+ break
+
+ message = nfc.ndef.Message(data);
+ sel.add_carrier(message[0], "active", message[1:])
+ break
+
+ for carrier in request.carriers:
+ if found:
+ break
+ print "Remote carrier type: " + carrier.type
+ if carrier.type == "application/vnd.wfa.wsc":
+ print "WSC carrier type match - add WSC carrier record"
+ found = True
+ self.received_carrier = carrier.record
+ print "Carrier record:"
+ try:
+ print carrier.record.pretty()
+ except Exception, e:
+ print e
+ data = wpas_get_handover_sel_wps()
+ if data is None:
+ print "Could not get handover select carrier record from wpa_supplicant"
+ continue
+ print "Handover select carrier record from wpa_supplicant:"
+ print data.encode("hex")
+ self.sent_carrier = data
+ if "OK" in wpas_report_handover_wsc(self.received_carrier, self.sent_carrier, "RESP"):
+ success_report("WSC handover reported successfully")
+ else:
+ summary("WSC handover report rejected")
+ break
+
+ message = nfc.ndef.Message(data);
+ sel.add_carrier(message[0], "active", message[1:])
+ found = True
+ break
+
+ print "Handover select:"
+ try:
+ print sel.pretty()
+ except Exception, e:
+ print e
+ print str(sel).encode("hex")
+
+ summary("Sending handover select")
+ self.success = True
+ return sel
+
+
+def clear_raw_mode():
+ import sys, tty, termios
+ global prev_tcgetattr, in_raw_mode
+ if not in_raw_mode:
+ return
+ fd = sys.stdin.fileno()
+ termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+ in_raw_mode = False
+
+
+def getch():
+ import sys, tty, termios, select
+ global prev_tcgetattr, in_raw_mode
+ fd = sys.stdin.fileno()
+ prev_tcgetattr = termios.tcgetattr(fd)
+ ch = None
+ try:
+ tty.setraw(fd)
+ in_raw_mode = True
+ [i, o, e] = select.select([fd], [], [], 0.05)
+ if i:
+ ch = sys.stdin.read(1)
+ finally:
+ termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+ in_raw_mode = False
+ return ch
+
+
+def p2p_tag_read(tag):
+ success = False
+ if len(tag.ndef.message):
+ for record in tag.ndef.message:
+ print "record type " + record.type
+ if record.type == "application/vnd.wfa.wsc":
+ summary("WPS tag - send to wpa_supplicant")
+ success = wpas_tag_read(tag.ndef.message)
+ break
+ if record.type == "application/vnd.wfa.p2p":
+ summary("P2P tag - send to wpa_supplicant")
+ success = wpas_tag_read(tag.ndef.message)
+ break
+ else:
+ summary("Empty tag")
+
+ if success:
+ success_report("Tag read succeeded")
+
+ return success
+
+
+def rdwr_connected_p2p_write(tag):
+ summary("Tag found - writing - " + str(tag))
+ global p2p_sel_data
+ tag.ndef.message = str(p2p_sel_data)
+ success_report("Tag write succeeded")
+ print "Done - remove tag"
+ global only_one
+ if only_one:
+ global continue_loop
+ continue_loop = False
+ global p2p_sel_wait_remove
+ return p2p_sel_wait_remove
+
+def wps_write_p2p_handover_sel(clf, wait_remove=True):
+ print "Write P2P handover select"
+ data = wpas_get_handover_sel(tag=True)
+ if (data == None):
+ summary("Could not get P2P handover select from wpa_supplicant")
+ return
+
+ global p2p_sel_wait_remove
+ p2p_sel_wait_remove = wait_remove
+ global p2p_sel_data
+ p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2")
+ message = nfc.ndef.Message(data);
+ p2p_sel_data.add_carrier(message[0], "active", message[1:])
+ print "Handover select:"
+ try:
+ print p2p_sel_data.pretty()
+ except Exception, e:
+ print e
+ print str(p2p_sel_data).encode("hex")
+
+ print "Touch an NFC tag"
+ clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write})
+
+
+def rdwr_connected(tag):
+ global only_one, no_wait
+ summary("Tag connected: " + str(tag))
+
+ if tag.ndef:
+ print "NDEF tag: " + tag.type
+ try:
+ print tag.ndef.message.pretty()
+ except Exception, e:
+ print e
+ success = p2p_tag_read(tag)
+ if only_one and success:
+ global continue_loop
+ continue_loop = False
+ else:
+ summary("Not an NDEF tag - remove tag")
+ return True
+
+ return not no_wait
+
+
+def llcp_worker(llc):
+ global init_on_touch
+ if init_on_touch:
+ print "Starting handover client"
+ p2p_handover_client(llc)
+ return
+
+ global no_input
+ if no_input:
+ print "Wait for handover to complete"
+ else:
+ print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)"
+ global srv
+ global wait_connection
+ while not wait_connection and srv.sent_carrier is None:
+ if srv.ho_server_processing:
+ time.sleep(0.025)
+ elif no_input:
+ time.sleep(0.5)
+ else:
+ global include_wps_req, include_p2p_req
+ res = getch()
+ if res == 'i':
+ include_wps_req = True
+ include_p2p_req = True
+ elif res == 'p':
+ include_wps_req = False
+ include_p2p_req = True
+ elif res == 'w':
+ include_wps_req = True
+ include_p2p_req = False
+ else:
+ continue
+ clear_raw_mode()
+ print "Starting handover client"
+ p2p_handover_client(llc)
+ return
+
+ clear_raw_mode()
+ print "Exiting llcp_worker thread"
+
+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 init_on_touch
+ if not init_on_touch:
+ global srv
+ srv.start()
+ if init_on_touch or not no_input:
+ threading.Thread(target=llcp_worker, args=(llc,)).start()
+ return True
+
+def terminate_loop():
+ global terminate_now
+ return terminate_now
+
+def main():
+ clf = nfc.ContactlessFrontend()
+
+ parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and 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('--init-on-touch', '-I', action='store_true',
+ help='initiate handover on touch')
+ parser.add_argument('--no-wait', action='store_true',
+ help='do not wait for tag to be removed before exiting')
+ parser.add_argument('--ifname', '-i',
+ help='network interface name')
+ parser.add_argument('--no-wps-req', '-N', action='store_true',
+ help='do not include WPS carrier record in request')
+ parser.add_argument('--no-input', '-a', action='store_true',
+ help='do not use stdout input to initiate handover')
+ parser.add_argument('--tag-read-only', '-t', action='store_true',
+ help='tag read only (do not allow connection handover)')
+ parser.add_argument('--handover-only', action='store_true',
+ help='connection handover only (do not allow tag read)')
+ parser.add_argument('--freq', '-f',
+ help='forced frequency of operating channel in MHz')
+ parser.add_argument('--summary',
+ help='summary file for writing status updates')
+ parser.add_argument('--success',
+ help='success file for writing success update')
+ parser.add_argument('command', choices=['write-p2p-sel'],
+ nargs='?')
+ args = parser.parse_args()
+
+ global only_one
+ only_one = args.only_one
+
+ global no_wait
+ no_wait = args.no_wait
+
+ global force_freq
+ force_freq = args.freq
+
+ logging.basicConfig(level=args.loglevel)
+
+ global init_on_touch
+ init_on_touch = args.init_on_touch
+
+ if args.ifname:
+ global ifname
+ ifname = args.ifname
+ print "Selected ifname " + ifname
+
+ if args.no_wps_req:
+ global include_wps_req
+ include_wps_req = False
+
+ if args.summary:
+ global summary_file
+ summary_file = args.summary
+
+ if args.success:
+ global success_file
+ success_file = args.success
+
+ if args.no_input:
+ global no_input
+ no_input = True
+
+ clf = nfc.ContactlessFrontend()
+ global wait_connection
+
+ try:
+ if not clf.open("usb"):
+ print "Could not open connection with an NFC device"
+ raise SystemExit
+
+ if args.command == "write-p2p-sel":
+ wps_write_p2p_handover_sel(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 args.tag_read_only:
+ if not clf.connect(rdwr={'on-connect': rdwr_connected}):
+ break
+ elif args.handover_only:
+ if not clf.connect(llcp={'on-startup': llcp_startup,
+ 'on-connect': llcp_connected},
+ terminate=terminate_loop):
+ break
+ else:
+ if not clf.connect(rdwr={'on-connect': rdwr_connected},
+ llcp={'on-startup': llcp_startup,
+ 'on-connect': llcp_connected},
+ terminate=terminate_loop):
+ 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()
diff --git a/wpa_supplicant/examples/wps-nfc.py b/wpa_supplicant/examples/wps-nfc.py
index d6dec85..7459eb9 100755
--- a/wpa_supplicant/examples/wps-nfc.py
+++ b/wpa_supplicant/examples/wps-nfc.py
@@ -10,7 +10,8 @@
import sys
import time
import random
-import StringIO
+import threading
+import argparse
import nfc
import nfc.ndef
@@ -18,11 +19,27 @@
import nfc.handover
import logging
-logging.basicConfig()
import wpaspy
wpas_ctrl = '/var/run/wpa_supplicant'
+srv = None
+continue_loop = True
+terminate_now = False
+summary_file = None
+success_file = None
+
+def summary(txt):
+ print txt
+ if summary_file:
+ with open(summary_file, 'a') as f:
+ f.write(txt + "\n")
+
+def success_report(txt):
+ summary(txt)
+ if success_file:
+ with open(success_file, 'a') as f:
+ f.write(txt + "\n")
def wpas_connect():
ifaces = []
@@ -50,7 +67,7 @@
wpas = wpas_connect()
if (wpas == None):
return False
- if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + message.encode("hex")):
+ if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
return False
return True
@@ -71,21 +88,29 @@
wpas = wpas_connect()
if (wpas == None):
return None
- return wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid).rstrip().decode("hex")
+ ret = wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid)
+ if "FAIL" in ret:
+ return None
+ return ret.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")
-
+ ret = wpas.request("WPS_NFC_TOKEN NDEF")
+ if "FAIL" in ret:
+ return None
+ return ret.rstrip().decode("hex")
def wpas_get_handover_req():
wpas = wpas_connect()
if (wpas == None):
return None
- return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex")
+ ret = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR")
+ if "FAIL" in ret:
+ return None
+ return ret.rstrip().decode("hex")
def wpas_get_handover_sel(uuid):
@@ -93,8 +118,12 @@
if (wpas == None):
return None
if uuid is None:
- return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
- return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip().decode("hex")
+ res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ else:
+ res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip()
+ if "FAIL" in res:
+ return None
+ return res.decode("hex")
def wpas_report_handover(req, sel, type):
@@ -107,159 +136,176 @@
class HandoverServer(nfc.handover.HandoverServer):
- def __init__(self):
- super(HandoverServer, self).__init__()
+ def __init__(self, llc):
+ super(HandoverServer, self).__init__(llc)
+ self.sent_carrier = None
+ self.ho_server_processing = False
+ self.success = False
+
+ # override to avoid parser error in request/response.pretty() in nfcpy
+ # due to new WSC handover format
+ def _process_request(self, request):
+ summary("received handover request {}".format(request.type))
+ response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+ if not request.type == 'urn:nfc:wkt:Hr':
+ summary("not a handover request")
+ else:
+ try:
+ request = nfc.ndef.HandoverRequestMessage(request)
+ except nfc.ndef.DecodeError as e:
+ summary("error decoding 'Hr' message: {}".format(e))
+ else:
+ response = self.process_request(request)
+ summary("send handover response {}".format(response.type))
+ return response
def process_request(self, request):
- print "HandoverServer - request received"
- print "Parsed handover request: " + request.pretty()
+ self.ho_server_processing = True
+ summary("HandoverServer - request received")
+ try:
+ print "Parsed handover request: " + request.pretty()
+ except Exception, e:
+ print e
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"
- self.received_carrier = carrier.record
+ summary("WPS carrier type match - add WPS carrier record")
data = wpas_get_handover_sel(self.uuid)
if data is None:
- print "Could not get handover select carrier record from wpa_supplicant"
+ summary("Could not get handover select carrier record from wpa_supplicant")
continue
print "Handover select carrier record from wpa_supplicant:"
print data.encode("hex")
self.sent_carrier = data
+ if "OK" in wpas_report_handover(carrier.record, self.sent_carrier, "RESP"):
+ success_report("Handover reported successfully (responder)")
+ else:
+ summary("Handover report rejected (responder)")
message = nfc.ndef.Message(data);
sel.add_carrier(message[0], "active", message[1:])
print "Handover select:"
- print sel.pretty()
+ try:
+ print sel.pretty()
+ except Exception, e:
+ print e
print str(sel).encode("hex")
- print "Sending handover select"
+ summary("Sending handover select")
+ self.success = True
return sel
-def wps_handover_resp(peer, uuid):
- if uuid is None:
- print "Trying to handle WPS handover"
- else:
- print "Trying to handle WPS handover with AP " + uuid
-
- srv = HandoverServer()
- srv.sent_carrier = None
- srv.uuid = uuid
-
- nfc.llcp.activate(peer);
-
- try:
- print "Trying handover";
- srv.start()
- print "Wait for disconnect"
- while nfc.llcp.connected():
- time.sleep(0.1)
- print "Disconnected after handover"
- except nfc.llcp.ConnectRefused:
- print "Handover connection refused"
- nfc.llcp.shutdown()
- return
-
- if srv.sent_carrier:
- wpas_report_handover(srv.received_carrier, srv.sent_carrier, "RESP")
-
- print "Remove peer"
- nfc.llcp.shutdown()
- print "Done with handover"
- time.sleep(1)
-
-
-def wps_handover_init(peer):
- print "Trying to initiate WPS handover"
+def wps_handover_init(llc):
+ summary("Trying to initiate WPS handover")
data = wpas_get_handover_req()
if (data == None):
- print "Could not get handover request carrier record from wpa_supplicant"
+ summary("Could not get handover request carrier record from wpa_supplicant")
return
print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
- record = nfc.ndef.Record()
- f = StringIO.StringIO(data)
- record._read(f)
- record = nfc.ndef.HandoverCarrierRecord(record)
- print "Parsed handover request carrier record:"
- print record.pretty()
message = nfc.ndef.HandoverRequestMessage(version="1.2")
message.nonce = random.randint(0, 0xffff)
- message.add_carrier(record, "active")
+ datamsg = nfc.ndef.Message(data)
+ message.add_carrier(datamsg[0], "active", datamsg[1:])
print "Handover request:"
- print message.pretty()
-
- nfc.llcp.activate(peer);
-
- client = nfc.handover.HandoverClient()
try:
- print "Trying handover";
+ print message.pretty()
+ except Exception, e:
+ print e
+ print str(message).encode("hex")
+
+ client = nfc.handover.HandoverClient(llc)
+ try:
+ summary("Trying to initiate NFC connection handover")
client.connect()
- print "Connected for handover"
+ summary("Connected for handover")
except nfc.llcp.ConnectRefused:
- print "Handover connection refused"
- nfc.llcp.shutdown()
+ summary("Handover connection refused")
+ client.close()
+ return
+ except Exception, e:
+ summary("Other exception: " + str(e))
client.close()
return
- print "Sending handover request"
+ summary("Sending handover request")
if not client.send(message):
- print "Failed to send handover request"
+ summary("Failed to send handover request")
+ client.close()
+ return
- print "Receiving handover response"
+ summary("Receiving handover response")
message = client._recv()
if message is None:
- print "No response received"
- nfc.llcp.shutdown()
+ summary("No response received")
client.close()
return
if message.type != "urn:nfc:wkt:Hs":
- print "Response was not Hs - received: " + message.type
- nfc.llcp.shutdown()
+ summary("Response was not Hs - received: " + message.type)
client.close()
return
print "Received message"
- print message.pretty()
+ try:
+ print message.pretty()
+ except Exception, e:
+ print e
+ print str(message).encode("hex")
message = nfc.ndef.HandoverSelectMessage(message)
- print "Handover select received"
- print message.pretty()
+ summary("Handover select received")
+ try:
+ print message.pretty()
+ except Exception, e:
+ print e
for carrier in message.carriers:
print "Remote carrier type: " + carrier.type
if carrier.type == "application/vnd.wfa.wsc":
print "WPS carrier type match - send to wpa_supplicant"
- wpas_report_handover(data, carrier.record, "INIT")
- wifi = nfc.ndef.WifiConfigRecord(carrier.record)
- print wifi.pretty()
+ if "OK" in wpas_report_handover(data, carrier.record, "INIT"):
+ success_report("Handover reported successfully (initiator)")
+ else:
+ summary("Handover report rejected (initiator)")
+ # nfcpy does not support the new format..
+ #wifi = nfc.ndef.WifiConfigRecord(carrier.record)
+ #print wifi.pretty()
print "Remove peer"
- nfc.llcp.shutdown()
client.close()
print "Done with handover"
+ global only_one
+ if only_one:
+ global continue_loop
+ continue_loop = False
+ global no_wait
+ if no_wait:
+ print "Trying to exit.."
+ global terminate_now
+ terminate_now = True
def wps_tag_read(tag, wait_remove=True):
success = False
if len(tag.ndef.message):
- message = nfc.ndef.Message(tag.ndef.message)
- print "message type " + message.type
-
- for record in message:
+ for record in tag.ndef.message:
print "record type " + record.type
if record.type == "application/vnd.wfa.wsc":
- print "WPS tag - send to wpa_supplicant"
+ summary("WPS tag - send to wpa_supplicant")
success = wpas_tag_read(tag.ndef.message)
break
else:
- print "Empty tag"
+ summary("Empty tag")
+
+ if success:
+ success_report("Tag read succeeded")
if wait_remove:
print "Remove tag"
@@ -269,166 +315,204 @@
return success
+def rdwr_connected_write(tag):
+ summary("Tag found - writing - " + str(tag))
+ global write_data
+ tag.ndef.message = str(write_data)
+ success_report("Tag write succeeded")
+ print "Done - remove tag"
+ global only_one
+ if only_one:
+ global continue_loop
+ continue_loop = False
+ global write_wait_remove
+ while write_wait_remove and tag.is_present:
+ time.sleep(0.1)
+
def wps_write_config_tag(clf, id=None, wait_remove=True):
print "Write WPS config token"
- data = wpas_get_config_token(id)
- if (data == None):
+ global write_data, write_wait_remove
+ write_wait_remove = wait_remove
+ write_data = wpas_get_config_token(id)
+ if write_data == None:
print "Could not get WPS config token from wpa_supplicant"
sys.exit(1)
return
-
print "Touch an NFC tag"
- while True:
- tag = clf.poll()
- if tag == None:
- time.sleep(0.1)
- continue
- break
-
- print "Tag found - writing"
- tag.ndef.message = data
- print "Done - remove tag"
- while wait_remove and tag.is_present:
- time.sleep(0.1)
+ clf.connect(rdwr={'on-connect': rdwr_connected_write})
-def wps_write_er_config_tag(clf, uuid):
+def wps_write_er_config_tag(clf, uuid, wait_remove=True):
print "Write WPS ER config token"
- data = wpas_get_er_config_token(uuid)
- if (data == None):
+ global write_data, write_wait_remove
+ write_wait_remove = wait_remove
+ write_data = wpas_get_er_config_token(uuid)
+ if write_data == None:
print "Could not get WPS config token from wpa_supplicant"
return
print "Touch an NFC tag"
- while True:
- tag = clf.poll()
- if tag == None:
- time.sleep(0.1)
- continue
- break
-
- print "Tag found - writing"
- tag.ndef.message = data
- print "Done - remove tag"
- while tag.is_present:
- time.sleep(0.1)
+ clf.connect(rdwr={'on-connect': rdwr_connected_write})
def wps_write_password_tag(clf, wait_remove=True):
print "Write WPS password token"
- data = wpas_get_password_token()
- if (data == None):
+ 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 wpa_supplicant"
return
print "Touch an NFC tag"
- while True:
- tag = clf.poll()
- if tag == None:
- time.sleep(0.1)
- continue
- break
-
- print "Tag found - writing"
- tag.ndef.message = data
- print "Done - remove tag"
- while wait_remove and tag.is_present:
- time.sleep(0.1)
+ clf.connect(rdwr={'on-connect': rdwr_connected_write})
-def find_peer(clf):
- while True:
- if nfc.llcp.connected():
- print "LLCP connected"
- general_bytes = nfc.llcp.startup({})
- peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
- if isinstance(peer, nfc.DEP):
- print "listen -> DEP";
- if peer.general_bytes.startswith("Ffm"):
- print "Found DEP"
- return peer
- print "mismatch in general_bytes"
- print peer.general_bytes
+def rdwr_connected(tag):
+ global only_one, no_wait
+ summary("Tag connected: " + str(tag))
- peer = clf.poll(general_bytes)
- if isinstance(peer, nfc.DEP):
- print "poll -> DEP";
- if peer.general_bytes.startswith("Ffm"):
- print "Found DEP"
- return peer
- print "mismatch in general_bytes"
- print peer.general_bytes
+ if tag.ndef:
+ print "NDEF tag: " + tag.type
+ try:
+ print tag.ndef.message.pretty()
+ except Exception, e:
+ print e
+ success = wps_tag_read(tag, not only_one)
+ if only_one and success:
+ global continue_loop
+ continue_loop = False
+ else:
+ summary("Not an NDEF tag - remove tag")
+ return True
- if peer:
- print "Found tag"
- return peer
+ return not no_wait
+def llcp_worker(llc):
+ global arg_uuid
+ if arg_uuid is None:
+ wps_handover_init(llc)
+ print "Exiting llcp_worker thread"
+ return
+
+ global srv
+ global wait_connection
+ while not wait_connection and srv.sent_carrier is None:
+ if srv.ho_server_processing:
+ time.sleep(0.025)
+
+def llcp_startup(clf, llc):
+ global arg_uuid
+ if arg_uuid:
+ print "Start LLCP server"
+ global srv
+ srv = HandoverServer(llc)
+ if arg_uuid is "ap":
+ print "Trying to handle WPS handover"
+ srv.uuid = None
+ else:
+ print "Trying to handle WPS handover with AP " + arg_uuid
+ srv.uuid = arg_uuid
+ return llc
+
+def llcp_connected(llc):
+ print "P2P LLCP connected"
+ global wait_connection
+ wait_connection = False
+ global arg_uuid
+ if arg_uuid:
+ global srv
+ srv.start()
+ else:
+ threading.Thread(target=llcp_worker, args=(llc,)).start()
+ print "llcp_connected returning"
+ return True
+
+
+def terminate_loop():
+ global terminate_now
+ return terminate_now
+
def main():
clf = nfc.ContactlessFrontend()
+ parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant 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('--uuid',
+ help='UUID of an AP (used for WPS ER operations)')
+ parser.add_argument('--id',
+ help='network id (used for WPS ER operations)')
+ parser.add_argument('--summary',
+ help='summary file for writing status updates')
+ parser.add_argument('--success',
+ help='success file for writing success update')
+ parser.add_argument('command', choices=['write-config',
+ 'write-er-config',
+ 'write-password'],
+ nargs='?')
+ args = parser.parse_args()
+
+ global arg_uuid
+ arg_uuid = args.uuid
+
+ global only_one
+ only_one = args.only_one
+
+ global no_wait
+ no_wait = args.no_wait
+
+ if args.summary:
+ global summary_file
+ summary_file = args.summary
+
+ if args.success:
+ global success_file
+ success_file = args.success
+
+ logging.basicConfig(level=args.loglevel)
+
try:
- arg_uuid = None
- if len(sys.argv) > 1 and sys.argv[1] != '-1':
- arg_uuid = sys.argv[1]
-
- if len(sys.argv) > 1 and sys.argv[1] == '-1':
- only_one = True
- else:
- only_one = False
-
- if len(sys.argv) > 1 and sys.argv[1] == "write-config":
- wps_write_config_tag(clf)
+ if not clf.open("usb"):
+ print "Could not open connection with an NFC device"
raise SystemExit
- if len(sys.argv) > 1 and sys.argv[1] == "write-config-no-wait":
- wps_write_config_tag(clf, wait_remove=False)
+ if args.command == "write-config":
+ wps_write_config_tag(clf, id=args.id, wait_remove=not args.no_wait)
raise SystemExit
- if len(sys.argv) > 2 and sys.argv[1] == "write-config-id":
- wps_write_config_tag(clf, sys.argv[2])
+ if args.command == "write-er-config":
+ wps_write_er_config_tag(clf, args.uuid, wait_remove=not args.no_wait)
raise SystemExit
- if len(sys.argv) > 2 and sys.argv[1] == "write-er-config":
- wps_write_er_config_tag(clf, sys.argv[2])
+ if args.command == "write-password":
+ wps_write_password_tag(clf, wait_remove=not args.no_wait)
raise SystemExit
- if len(sys.argv) > 1 and sys.argv[1] == "write-password":
- wps_write_password_tag(clf)
- raise SystemExit
-
- if len(sys.argv) > 1 and sys.argv[1] == "write-password-no-wait":
- wps_write_password_tag(clf, wait_remove=False)
- raise SystemExit
-
- while True:
+ global continue_loop
+ while continue_loop:
print "Waiting for a tag or peer to be touched"
-
- tag = find_peer(clf)
- if isinstance(tag, nfc.DEP):
- if arg_uuid is None:
- wps_handover_init(tag)
- elif arg_uuid is "ap":
- wps_handover_resp(tag, None)
- else:
- wps_handover_resp(tag, arg_uuid)
- if only_one:
+ wait_connection = True
+ try:
+ if not clf.connect(rdwr={'on-connect': rdwr_connected},
+ llcp={'on-startup': llcp_startup,
+ 'on-connect': llcp_connected},
+ terminate=terminate_loop):
break
- continue
+ except Exception, e:
+ print "clf.connect failed"
- if tag.ndef:
- success = wps_tag_read(tag, not only_one)
- if only_one:
- if not success:
- sys.exit(1)
- break
- continue
-
- print "Not an NDEF tag - remove tag"
- if only_one:
- sys.exit(1)
- while tag.is_present:
- time.sleep(0.1)
+ global srv
+ if only_one and srv and srv.success:
+ raise SystemExit
except KeyboardInterrupt:
raise SystemExit
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 06a97d3..3986268 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -1,7 +1,8 @@
/*
* Generic advertisement service (GAS) query
* Copyright (c) 2009, Atheros Communications
- * Copyright (c) 2011, Qualcomm Atheros
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,6 +14,8 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "offchannel.h"
@@ -28,6 +31,7 @@
*/
struct gas_query_pending {
struct dl_list list;
+ struct gas_query *gas;
u8 addr[ETH_ALEN];
u8 dialog_token;
u8 next_frag_id;
@@ -35,8 +39,10 @@
unsigned int offchannel_tx_started:1;
int freq;
u16 status_code;
+ struct wpabuf *req;
struct wpabuf *adv_proto;
struct wpabuf *resp;
+ struct os_reltime last_oper;
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
@@ -50,6 +56,8 @@
struct gas_query {
struct wpa_supplicant *wpa_s;
struct dl_list pending; /* struct gas_query_pending */
+ struct gas_query_pending *current;
+ struct wpa_radio_work *work;
};
@@ -57,6 +65,16 @@
static void gas_query_timeout(void *eloop_data, void *user_ctx);
+static int ms_from_time(struct os_reltime *last)
+{
+ struct os_reltime now, res;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, last, &res);
+ return res.sec * 1000 + res.usec / 1000;
+}
+
+
/**
* gas_query_init - Initialize GAS query component
* @wpa_s: Pointer to wpa_supplicant data
@@ -77,10 +95,58 @@
}
+static const char * gas_result_txt(enum gas_query_result result)
+{
+ switch (result) {
+ case GAS_QUERY_SUCCESS:
+ return "SUCCESS";
+ case GAS_QUERY_FAILURE:
+ return "FAILURE";
+ case GAS_QUERY_TIMEOUT:
+ return "TIMEOUT";
+ case GAS_QUERY_PEER_ERROR:
+ return "PEER_ERROR";
+ case GAS_QUERY_INTERNAL_ERROR:
+ return "INTERNAL_ERROR";
+ case GAS_QUERY_CANCELLED:
+ return "CANCELLED";
+ case GAS_QUERY_DELETED_AT_DEINIT:
+ return "DELETED_AT_DEINIT";
+ }
+
+ return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+ struct gas_query *gas = query->gas;
+
+ if (del_list)
+ dl_list_del(&query->list);
+
+ if (gas->work && gas->work->ctx == query) {
+ radio_work_done(gas->work);
+ gas->work = NULL;
+ }
+
+ wpabuf_free(query->req);
+ wpabuf_free(query->adv_proto);
+ wpabuf_free(query->resp);
+ os_free(query);
+}
+
+
static void gas_query_done(struct gas_query *gas,
struct gas_query_pending *query,
enum gas_query_result result)
{
+ wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+ " dialog_token=%u freq=%d status_code=%u result=%s",
+ MAC2STR(query->addr), query->dialog_token, query->freq,
+ query->status_code, gas_result_txt(result));
+ if (gas->current == query)
+ gas->current = NULL;
if (query->offchannel_tx_started)
offchannel_send_action_done(gas->wpa_s);
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
@@ -88,9 +154,7 @@
dl_list_del(&query->list);
query->cb(query->ctx, query->addr, query->dialog_token, result,
query->adv_proto, query->resp, query->status_code);
- wpabuf_free(query->adv_proto);
- wpabuf_free(query->resp);
- os_free(query);
+ gas_query_free(query, 0);
}
@@ -138,17 +202,79 @@
}
+static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result result)
+{
+ struct gas_query_pending *query;
+ struct gas_query *gas = wpa_s->gas;
+ int dur;
+
+ if (gas->current == NULL) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
+ MACSTR " result=%d - no query in progress",
+ freq, MAC2STR(dst), result);
+ return;
+ }
+
+ query = gas->current;
+
+ dur = ms_from_time(&query->last_oper);
+ wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
+ " result=%d query=%p dialog_token=%u dur=%d ms",
+ freq, MAC2STR(dst), result, query, query->dialog_token, dur);
+ if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+ return;
+ }
+ os_get_reltime(&query->last_oper);
+
+ if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+ }
+ if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
+ }
+}
+
+
+static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ if (wpa_s->current_ssid == NULL ||
+ wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
+ os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
+ return 0;
+ return wpa_sm_pmf_enabled(wpa_s->wpa);
+}
+
+
static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
struct wpabuf *req)
{
- int res;
+ unsigned int wait_time;
+ int res, prot = pmf_in_use(gas->wpa_s, query->addr);
+
wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
- "freq=%d", MAC2STR(query->addr),
- (unsigned int) wpabuf_len(req), query->freq);
+ "freq=%d prot=%d", MAC2STR(query->addr),
+ (unsigned int) wpabuf_len(req), query->freq, prot);
+ if (prot) {
+ u8 *categ = wpabuf_mhead_u8(req);
+ *categ = WLAN_ACTION_PROTECTED_DUAL;
+ }
+ os_get_reltime(&query->last_oper);
+ wait_time = 1000;
+ if (gas->wpa_s->max_remain_on_chan &&
+ wait_time > gas->wpa_s->max_remain_on_chan)
+ wait_time = gas->wpa_s->max_remain_on_chan;
res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
gas->wpa_s->own_addr, query->addr,
- wpabuf_head(req), wpabuf_len(req), 1000,
- NULL, 0);
+ wpabuf_head(req), wpabuf_len(req),
+ wait_time, gas_query_tx_status, 0);
if (res == 0)
query->offchannel_tx_started = 1;
return res;
@@ -296,27 +422,41 @@
/**
- * gas_query_rx - Indicate reception of a Public Action frame
+ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
* @gas: GAS query data from gas_query_init()
* @da: Destination MAC address of the Action frame
* @sa: Source MAC address of the Action frame
* @bssid: BSSID of the Action frame
+ * @categ: Category of the Action frame
* @data: Payload of the Action frame
* @len: Length of @data
* @freq: Frequency (in MHz) on which the frame was received
* Returns: 0 if the Public Action frame was a GAS frame or -1 if not
*/
int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
- const u8 *bssid, const u8 *data, size_t len, int freq)
+ const u8 *bssid, u8 categ, const u8 *data, size_t len,
+ int freq)
{
struct gas_query_pending *query;
u8 action, dialog_token, frag_id = 0, more_frags = 0;
u16 comeback_delay, resp_len;
const u8 *pos, *adv_proto;
+ int prot, pmf;
if (gas == NULL || len < 4)
return -1;
+ prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+ pmf = pmf_in_use(gas->wpa_s, bssid);
+ if (prot && !pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+ return 0;
+ }
+ if (!prot && pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+ return 0;
+ }
+
pos = data;
action = *pos++;
dialog_token = *pos++;
@@ -332,6 +472,9 @@
return -1;
}
+ wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+ ms_from_time(&query->last_oper), MAC2STR(sa));
+
if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
MACSTR " dialog token %u when waiting for comeback "
@@ -349,7 +492,10 @@
query->status_code = WPA_GET_LE16(pos);
pos += 2;
- if (query->status_code != WLAN_STATUS_SUCCESS) {
+ if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+ action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+ } else if (query->status_code != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
"%u failed - status code %u",
MAC2STR(sa), dialog_token, query->status_code);
@@ -426,8 +572,9 @@
struct gas_query *gas = eloop_data;
struct gas_query_pending *query = user_ctx;
- wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
- MAC2STR(query->addr));
+ wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+ " dialog token %u",
+ MAC2STR(query->addr), query->dialog_token);
gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
}
@@ -446,12 +593,47 @@
}
+static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct gas_query_pending *query = work->ctx;
+ struct gas_query *gas = query->gas;
+
+ if (deinit) {
+ if (work->started) {
+ gas->work = NULL;
+ gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+ return;
+ }
+
+ gas_query_free(query, 1);
+ return;
+ }
+
+ gas->work = work;
+
+ if (gas_query_tx(gas, query, query->req) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_free(query, 1);
+ return;
+ }
+ gas->current = query;
+
+ wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+ query->dialog_token);
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+
+}
+
+
/**
* gas_query_req - Request a GAS query
* @gas: GAS query data from gas_query_init()
* @dst: Destination MAC address for the query
* @freq: Frequency (in MHz) for the channel on which to send the query
- * @req: GAS query payload
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ * return)
* @cb: Callback function for reporting GAS query result and response
* @ctx: Context pointer to use with the @cb call
* Returns: dialog token (>= 0) on success or -1 on failure
@@ -485,28 +667,27 @@
if (query == NULL)
return -1;
+ query->gas = gas;
os_memcpy(query->addr, dst, ETH_ALEN);
query->dialog_token = dialog_token;
query->freq = freq;
query->cb = cb;
query->ctx = ctx;
+ query->req = req;
dl_list_add(&gas->pending, &query->list);
*(wpabuf_mhead_u8(req) + 2) = dialog_token;
- wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
- " dialog_token %u", MAC2STR(dst), dialog_token);
- if (gas_query_tx(gas, query, req) < 0) {
- wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
- MACSTR, MAC2STR(query->addr));
- dl_list_del(&query->list);
- os_free(query);
+ wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+ " dialog_token=%u freq=%d",
+ MAC2STR(query->addr), query->dialog_token, query->freq);
+
+ if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
+ query) < 0) {
+ gas_query_free(query, 1);
return -1;
}
- eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
- gas, query);
-
return dialog_token;
}
diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h
index 5c3d161..ad13490 100644
--- a/wpa_supplicant/gas_query.h
+++ b/wpa_supplicant/gas_query.h
@@ -17,7 +17,8 @@
struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s);
void gas_query_deinit(struct gas_query *gas);
int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
- const u8 *bssid, const u8 *data, size_t len, int freq);
+ const u8 *bssid, u8 categ, const u8 *data, size_t len,
+ int freq);
/**
* enum gas_query_result - GAS query result
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 4048cf7..257aa6d 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2009, Atheros Communications, Inc.
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,22 +14,65 @@
#include "common/ieee802_11_defs.h"
#include "common/gas.h"
#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "config.h"
+#include "scan.h"
#include "bss.h"
+#include "blacklist.h"
#include "gas_query.h"
#include "interworking.h"
#include "hs20_supplicant.h"
-void wpas_hs20_add_indication(struct wpabuf *buf)
+#define OSU_MAX_ITEMS 10
+
+struct osu_lang_string {
+ char lang[4];
+ char text[253];
+};
+
+struct osu_icon {
+ u16 width;
+ u16 height;
+ char lang[4];
+ char icon_type[256];
+ char filename[256];
+ unsigned int id;
+ unsigned int failed:1;
+};
+
+struct osu_provider {
+ u8 bssid[ETH_ALEN];
+ u8 osu_ssid[32];
+ u8 osu_ssid_len;
+ char server_uri[256];
+ u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
+ char osu_nai[256];
+ struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
+ size_t friendly_name_count;
+ struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
+ size_t serv_desc_count;
+ struct osu_icon icon[OSU_MAX_ITEMS];
+ size_t icon_count;
+};
+
+
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id)
{
+ u8 conf;
+
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
- wpabuf_put_u8(buf, 5);
+ wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5);
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
- wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */
+ conf = HS20_VERSION;
+ if (pps_mo_id >= 0)
+ conf |= HS20_PPS_MO_ID_PRESENT;
+ wpabuf_put_u8(buf, conf);
+ if (pps_mo_id >= 0)
+ wpabuf_put_le16(buf, pps_mo_id);
}
@@ -62,15 +105,35 @@
}
-struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
- size_t payload_len)
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
- struct wpabuf *buf;
+ struct wpa_cred *cred;
+
+ if (ssid == NULL)
+ return 0;
+
+ if (ssid->update_identifier)
+ return ssid->update_identifier;
+
+ if (ssid->parent_cred == NULL)
+ return 0;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (ssid->parent_cred == cred)
+ return cred->update_identifier;
+ }
+
+ return 0;
+}
+
+
+void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
+ struct wpabuf *buf)
+{
u8 *len_pos;
- buf = gas_anqp_build_initial_req(0, 100 + payload_len);
if (buf == NULL)
- return NULL;
+ return;
len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
wpabuf_put_be24(buf, OUI_WFA);
@@ -80,6 +143,11 @@
wpabuf_put_u8(buf, 0); /* Reserved */
if (payload)
wpabuf_put_data(buf, payload, payload_len);
+ } else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) {
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ if (payload)
+ wpabuf_put_data(buf, payload, payload_len);
} else {
u8 i;
wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
@@ -92,6 +160,19 @@
gas_anqp_set_element_len(buf, len_pos);
gas_anqp_set_len(buf);
+}
+
+
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+ size_t payload_len)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_initial_req(0, 100 + payload_len);
+ if (buf == NULL)
+ return NULL;
+
+ hs20_put_anqp_req(stypes, payload, payload_len, buf);
return buf;
}
@@ -125,16 +206,126 @@
res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ wpabuf_free(buf);
ret = -1;
} else
wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
"%u", res);
- wpabuf_free(buf);
return ret;
}
+static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *pos,
+ size_t slen)
+{
+ char fname[256];
+ int png;
+ FILE *f;
+ u16 data_len;
+
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File",
+ MAC2STR(sa));
+
+ if (slen < 4) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
+ if (*pos != 0)
+ return -1;
+ pos++;
+ slen--;
+
+ if ((size_t) 1 + pos[0] > slen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
+ png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0;
+ slen -= 1 + pos[0];
+ pos += 1 + pos[0];
+
+ if (slen < 2) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+ data_len = WPA_GET_LE16(pos);
+ pos += 2;
+ slen -= 2;
+
+ if (data_len > slen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
+ if (wpa_s->conf->osu_dir == NULL)
+ return -1;
+
+ wpa_s->osu_icon_id++;
+ if (wpa_s->osu_icon_id == 0)
+ wpa_s->osu_icon_id++;
+ snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s",
+ wpa_s->conf->osu_dir, wpa_s->osu_icon_id,
+ png ? "png" : "icon");
+ f = fopen(fname, "wb");
+ if (f == NULL)
+ return -1;
+ if (fwrite(pos, slen, 1, f) != 1) {
+ fclose(f);
+ unlink(fname);
+ return -1;
+ }
+ fclose(f);
+
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname);
+ return 0;
+}
+
+
+static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
+{
+ size_t i, j;
+ struct os_reltime now, tmp;
+ int dur;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp);
+ dur = tmp.sec * 1000 + tmp.usec / 1000;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d",
+ dur, res);
+
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->id || icon->failed)
+ continue;
+ if (res < 0)
+ icon->failed = 1;
+ else
+ icon->id = wpa_s->osu_icon_id;
+ return;
+ }
+ }
+}
+
+
void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
const u8 *sa, const u8 *data, size_t slen)
{
@@ -142,6 +333,7 @@
u8 subtype;
struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
struct wpa_bss_anqp *anqp = NULL;
+ int ret;
if (slen < 2)
return;
@@ -207,8 +399,537 @@
wpabuf_alloc_copy(pos, slen);
}
break;
+ case HS20_STYPE_OSU_PROVIDERS_LIST:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " OSU Providers list", MAC2STR(sa));
+ wpa_s->num_prov_found++;
+ if (anqp) {
+ wpabuf_free(anqp->hs20_osu_providers_list);
+ anqp->hs20_osu_providers_list =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_ICON_BINARY_FILE:
+ ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen);
+ if (wpa_s->fetch_osu_icon_in_progress) {
+ hs20_osu_icon_fetch_result(wpa_s, ret);
+ eloop_cancel_timeout(hs20_continue_icon_fetch,
+ wpa_s, NULL);
+ eloop_register_timeout(0, 0, hs20_continue_icon_fetch,
+ wpa_s, NULL);
+ }
+ break;
default:
wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
break;
}
}
+
+
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->fetch_osu_icon_in_progress)
+ return;
+ if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL))
+ return;
+ /*
+ * We are going through icon fetch, but no icon response was received.
+ * Assume this means the current AP could not provide an answer to avoid
+ * getting stuck in fetch iteration.
+ */
+ hs20_icon_fetch_failed(wpa_s);
+}
+
+
+static void hs20_free_osu_prov_entry(struct osu_provider *prov)
+{
+}
+
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s)
+{
+ size_t i;
+ for (i = 0; i < wpa_s->osu_prov_count; i++)
+ hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]);
+ os_free(wpa_s->osu_prov);
+ wpa_s->osu_prov = NULL;
+ wpa_s->osu_prov_count = 0;
+}
+
+
+static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
+{
+ char fname[256];
+ FILE *f;
+ size_t i, j;
+
+ wpa_s->fetch_osu_info = 0;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+
+ if (wpa_s->conf->osu_dir == NULL) {
+ hs20_free_osu_prov(wpa_s);
+ wpa_s->fetch_anqp_in_progress = 0;
+ return;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/osu-providers.txt",
+ wpa_s->conf->osu_dir);
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ hs20_free_osu_prov(wpa_s);
+ return;
+ }
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ if (i > 0)
+ fprintf(f, "\n");
+ fprintf(f, "OSU-PROVIDER " MACSTR "\n"
+ "uri=%s\n"
+ "methods=%08x\n",
+ MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
+ if (osu->osu_ssid_len) {
+ fprintf(f, "osu_ssid=%s\n",
+ wpa_ssid_txt(osu->osu_ssid,
+ osu->osu_ssid_len));
+ }
+ if (osu->osu_nai[0])
+ fprintf(f, "osu_nai=%s\n", osu->osu_nai);
+ for (j = 0; j < osu->friendly_name_count; j++) {
+ fprintf(f, "friendly_name=%s:%s\n",
+ osu->friendly_name[j].lang,
+ osu->friendly_name[j].text);
+ }
+ for (j = 0; j < osu->serv_desc_count; j++) {
+ fprintf(f, "desc=%s:%s\n",
+ osu->serv_desc[j].lang,
+ osu->serv_desc[j].text);
+ }
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->failed)
+ continue; /* could not fetch icon */
+ fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n",
+ icon->id, icon->width, icon->height, icon->lang,
+ icon->icon_type, icon->filename);
+ }
+ }
+ fclose(f);
+ hs20_free_osu_prov(wpa_s);
+
+ wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed");
+ wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
+{
+ size_t i, j;
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon");
+
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->id || icon->failed)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' "
+ "from " MACSTR, icon->filename,
+ MAC2STR(osu->bssid));
+ os_get_reltime(&wpa_s->osu_icon_fetch_start);
+ if (hs20_anqp_send_req(wpa_s, osu->bssid,
+ BIT(HS20_STYPE_ICON_REQUEST),
+ (u8 *) icon->filename,
+ os_strlen(icon->filename)) < 0) {
+ icon->failed = 1;
+ continue;
+ }
+ return;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch");
+ hs20_osu_fetch_done(wpa_s);
+}
+
+
+static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ const u8 *osu_ssid, u8 osu_ssid_len,
+ const u8 *pos, size_t len)
+{
+ struct osu_provider *prov;
+ const u8 *end = pos + len;
+ u16 len2;
+ const u8 *pos2;
+
+ wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
+ prov = os_realloc_array(wpa_s->osu_prov,
+ wpa_s->osu_prov_count + 1,
+ sizeof(*prov));
+ if (prov == NULL)
+ return;
+ wpa_s->osu_prov = prov;
+ prov = &prov[wpa_s->osu_prov_count];
+ os_memset(prov, 0, sizeof(*prov));
+
+ os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
+ os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
+ prov->osu_ssid_len = osu_ssid_len;
+
+ /* OSU Friendly Name Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Friendly Name Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + len2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Friendly Name Duples");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* OSU Friendly Name Duples */
+ while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+ struct osu_lang_string *f;
+ if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+ wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
+ break;
+ }
+ f = &prov->friendly_name[prov->friendly_name_count++];
+ os_memcpy(f->lang, pos2 + 1, 3);
+ os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+ pos2 += 1 + pos2[0];
+ }
+
+ /* OSU Server URI */
+ if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
+ "URI");
+ return;
+ }
+ os_memcpy(prov->server_uri, pos + 1, pos[0]);
+ pos += 1 + pos[0];
+
+ /* OSU Method list */
+ if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+ "list");
+ return;
+ }
+ pos2 = pos + 1;
+ pos += 1 + pos[0];
+ while (pos2 < pos) {
+ if (*pos2 < 32)
+ prov->osu_methods |= BIT(*pos2);
+ pos2++;
+ }
+
+ /* Icons Available Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+ "Available Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + len2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+ "Available");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* Icons Available */
+ while (pos2 < pos) {
+ struct osu_icon *icon = &prov->icon[prov->icon_count];
+ if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
+ break;
+ }
+
+ icon->width = WPA_GET_LE16(pos2);
+ pos2 += 2;
+ icon->height = WPA_GET_LE16(pos2);
+ pos2 += 2;
+ os_memcpy(icon->lang, pos2, 3);
+ pos2 += 3;
+
+ if (pos2 + 1 + pos2[0] > pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
+ break;
+ }
+ os_memcpy(icon->icon_type, pos2 + 1, pos2[0]);
+ pos2 += 1 + pos2[0];
+
+ if (pos2 + 1 + pos2[0] > pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+ "Filename");
+ break;
+ }
+ os_memcpy(icon->filename, pos2 + 1, pos2[0]);
+ pos2 += 1 + pos2[0];
+
+ prov->icon_count++;
+ }
+
+ /* OSU_NAI */
+ if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+ return;
+ }
+ os_memcpy(prov->osu_nai, pos + 1, pos[0]);
+ pos += 1 + pos[0];
+
+ /* OSU Service Description Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Service Description Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + len2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Service Description Duples");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* OSU Service Description Duples */
+ while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+ struct osu_lang_string *f;
+ if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+ wpa_printf(MSG_DEBUG, "Invalid OSU Service "
+ "Description");
+ break;
+ }
+ f = &prov->serv_desc[prov->serv_desc_count++];
+ os_memcpy(f->lang, pos2 + 1, 3);
+ os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+ pos2 += 1 + pos2[0];
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
+ MAC2STR(bss->bssid));
+ wpa_s->osu_prov_count++;
+}
+
+
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ struct wpabuf *prov_anqp;
+ const u8 *pos, *end;
+ u16 len;
+ const u8 *osu_ssid;
+ u8 osu_ssid_len;
+ u8 num_providers;
+
+ hs20_free_osu_prov(wpa_s);
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss->anqp == NULL)
+ continue;
+ prov_anqp = bss->anqp->hs20_osu_providers_list;
+ if (prov_anqp == NULL)
+ continue;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
+ MACSTR, MAC2STR(bss->bssid));
+ wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
+ prov_anqp);
+ pos = wpabuf_head(prov_anqp);
+ end = pos + wpabuf_len(prov_anqp);
+
+ /* OSU SSID */
+ if (pos + 1 > end)
+ continue;
+ if (pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+ "OSU SSID");
+ continue;
+ }
+ osu_ssid_len = *pos++;
+ if (osu_ssid_len > 32) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
+ "Length %u", osu_ssid_len);
+ continue;
+ }
+ osu_ssid = pos;
+ pos += osu_ssid_len;
+
+ if (pos + 1 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+ "Number of OSU Providers");
+ continue;
+ }
+ num_providers = *pos++;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u",
+ num_providers);
+
+ /* OSU Providers */
+ while (pos + 2 < end && num_providers > 0) {
+ num_providers--;
+ len = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + len > end)
+ break;
+ hs20_osu_add_prov(wpa_s, bss, osu_ssid,
+ osu_ssid_len, pos, len);
+ pos += len;
+ }
+
+ if (pos != end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of "
+ "extra data after OSU Providers",
+ (int) (end - pos));
+ }
+ }
+
+ wpa_s->fetch_osu_icon_in_progress = 1;
+ hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
+ wpa_s->network_select = 0;
+ wpa_s->fetch_all_anqp = 1;
+ wpa_s->fetch_osu_info = 1;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+
+ interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "interface disabled");
+ return -1;
+ }
+
+ if (wpa_s->scanning) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "scanning");
+ return -1;
+ }
+
+ if (wpa_s->conf->osu_dir == NULL) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "osu_dir not configured");
+ return -1;
+ }
+
+ if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "fetch in progress (%d, %d)",
+ wpa_s->fetch_anqp_in_progress,
+ wpa_s->network_select);
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
+ wpa_s->num_osu_scans = 0;
+ wpa_s->num_prov_found = 0;
+ hs20_start_osu_scan(wpa_s);
+
+ return 0;
+}
+
+
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->num_osu_scans++;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
+ interworking_stop_fetch_anqp(wpa_s);
+ wpa_s->network_select = 0;
+ wpa_s->fetch_osu_info = 0;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+}
+
+
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
+{
+ hs20_osu_icon_fetch_result(wpa_s, -1);
+ eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+ eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
+}
+
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+ const char *url, u8 osu_method)
+{
+ if (url)
+ wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s",
+ osu_method, url);
+ else
+ wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
+}
+
+
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+ u16 reauth_delay, const char *url)
+{
+ if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled");
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s",
+ code, reauth_delay, url);
+
+ if (code == HS20_DEAUTH_REASON_CODE_BSS) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to blacklist");
+ wpa_blacklist_add(wpa_s, wpa_s->bssid);
+ /* TODO: For now, disable full ESS since some drivers may not
+ * support disabling per BSS. */
+ if (wpa_s->current_ssid) {
+ struct os_reltime now;
+ os_get_reltime(&now);
+ if (now.sec + reauth_delay <=
+ wpa_s->current_ssid->disabled_until.sec)
+ return;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)",
+ reauth_delay);
+ wpa_s->current_ssid->disabled_until.sec =
+ now.sec + reauth_delay;
+ }
+ }
+
+ if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) {
+ struct os_reltime now;
+ os_get_reltime(&now);
+ if (now.sec + reauth_delay <=
+ wpa_s->current_ssid->disabled_until.sec)
+ return;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds",
+ reauth_delay);
+ wpa_s->current_ssid->disabled_until.sec =
+ now.sec + reauth_delay;
+ }
+}
+
+
+void hs20_deinit(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+ hs20_free_osu_prov(wpa_s);
+}
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index 1c8481b..06739f5 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -8,15 +8,33 @@
#ifndef HS20_SUPPLICANT_H
#define HS20_SUPPLICANT_H
-void wpas_hs20_add_indication(struct wpabuf *buf);
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id);
int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
const u8 *payload, size_t payload_len);
struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
size_t payload_len);
+void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
+ struct wpabuf *buf);
void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
const u8 *sa, const u8 *data, size_t slen);
int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_bss *bss);
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s);
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+ const char *url, u8 osu_method);
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+ u16 reauth_delay, const char *url);
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s);
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s);
+void hs20_deinit(struct wpa_supplicant *wpa_s);
#endif /* HS20_SUPPLICANT_H */
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 47ef35e..3083dd8 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -590,7 +590,7 @@
peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
return ibss_rsn_auth_init(ibss_rsn, peer);
} else {
- os_get_time(&peer->own_auth_tx);
+ os_get_reltime(&peer->own_auth_tx);
eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL);
}
@@ -834,9 +834,9 @@
if (peer &&
peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) {
if (peer->own_auth_tx.sec) {
- struct os_time now, diff;
- os_get_time(&now);
- os_time_sub(&now, &peer->own_auth_tx, &diff);
+ struct os_reltime now, diff;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &peer->own_auth_tx, &diff);
if (diff.sec == 0 && diff.usec < 500000) {
wpa_printf(MSG_DEBUG, "RSN: Skip IBSS reinit since only %u usec from own Auth frame TX",
(int) diff.usec);
diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h
index 3089283..67fae2d 100644
--- a/wpa_supplicant/ibss_rsn.h
+++ b/wpa_supplicant/ibss_rsn.h
@@ -40,7 +40,7 @@
struct wpa_state_machine *auth;
int authentication_status;
- struct os_time own_auth_tx;
+ struct os_reltime own_auth_tx;
};
struct ibss_rsn {
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 36f75a1..19b6e38 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -1,6 +1,7 @@
/*
* Interworking (IEEE 802.11u)
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -18,12 +19,15 @@
#include "eap_common/eap_defs.h"
#include "eap_peer/eap.h"
#include "eap_peer/eap_methods.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "config.h"
#include "config_ssid.h"
#include "bss.h"
#include "scan.h"
#include "notify.h"
+#include "driver_i.h"
#include "gas_query.h"
#include "hs20_supplicant.h"
#include "interworking.h"
@@ -43,9 +47,28 @@
static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
static struct wpa_cred * interworking_credentials_available_realm(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded);
static struct wpa_cred * interworking_credentials_available_3gpp(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded);
+
+
+static int cred_prio_cmp(const struct wpa_cred *a, const struct wpa_cred *b)
+{
+ if (a->priority > b->priority)
+ return 1;
+ if (a->priority < b->priority)
+ return -1;
+ if (a->provisioning_sp == NULL || b->provisioning_sp == NULL ||
+ os_strcmp(a->provisioning_sp, b->provisioning_sp) != 0)
+ return 0;
+ if (a->sp_priority < b->sp_priority)
+ return 1;
+ if (a->sp_priority > b->sp_priority)
+ return -1;
+ return 0;
+}
static void interworking_reconnect(struct wpa_supplicant *wpa_s)
@@ -99,6 +122,9 @@
{
struct wpa_supplicant *wpa_s = ctx;
+ wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
+ " dialog_token=%u result=%d status_code=%u",
+ MAC2STR(dst), dialog_token, result, status_code);
anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
status_code);
interworking_next_anqp_fetch(wpa_s);
@@ -112,6 +138,8 @@
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
if (cred->roaming_consortium_len)
return 1;
+ if (cred->required_roaming_consortium_len)
+ return 1;
}
return 0;
}
@@ -150,13 +178,45 @@
struct wpa_cred *cred;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- if (cred->domain || cred->pcsc || cred->imsi)
+ if (cred->domain || cred->pcsc || cred->imsi ||
+ cred->roaming_partner)
return 1;
}
return 0;
}
+#ifdef CONFIG_HS20
+
+static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->min_dl_bandwidth_home ||
+ cred->min_ul_bandwidth_home ||
+ cred->min_dl_bandwidth_roaming ||
+ cred->min_ul_bandwidth_roaming)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->num_req_conn_capab)
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
static int additional_roaming_consortiums(struct wpa_bss *bss)
{
const u8 *ie;
@@ -201,8 +261,10 @@
info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
if (all || cred_with_nai_realm(wpa_s))
info_ids[num_info_ids++] = ANQP_NAI_REALM;
- if (all || cred_with_3gpp(wpa_s))
+ if (all || cred_with_3gpp(wpa_s)) {
info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
+ wpa_supplicant_scard_init(wpa_s, NULL);
+ }
if (all || cred_with_domain(wpa_s))
info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
@@ -222,13 +284,17 @@
wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
wpabuf_put_u8(extra, 0); /* Reserved */
wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
- if (all) {
+ if (all)
wpabuf_put_u8(extra,
HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+ if (all || cred_with_min_backhaul(wpa_s))
wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+ if (all || cred_with_conn_capab(wpa_s))
wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+ if (all)
wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
- }
+ if (all)
+ wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
gas_anqp_set_element_len(extra, len_pos);
}
#endif /* CONFIG_HS20 */
@@ -242,6 +308,7 @@
interworking_anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ wpabuf_free(buf);
ret = -1;
eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
NULL);
@@ -249,7 +316,6 @@
wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
"%u", res);
- wpabuf_free(buf);
return ret;
}
@@ -516,12 +582,13 @@
if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
return 0; /* method not supported */
- if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
+ if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
+ eap->method != EAP_TYPE_FAST) {
/* Only tunneled methods with username/password supported */
return 0;
}
- if (eap->method == EAP_TYPE_PEAP) {
+ if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) {
if (eap->inner_method &&
eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
return 0;
@@ -729,8 +796,8 @@
*pos++ = imsi[4];
*pos++ = imsi[5];
}
- pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
- imsi[0], imsi[1], imsi[2]);
+ os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+ imsi[0], imsi[1], imsi[2]);
return 0;
}
@@ -747,12 +814,85 @@
#endif /* INTERWORKING_3GPP */
+static int already_connected(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ struct wpa_ssid *ssid, *sel_ssid;
+ struct wpa_bss *selected;
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL)
+ return 0;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid->parent_cred != cred)
+ return 0;
+
+ if (ssid->ssid_len != bss->ssid_len ||
+ os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
+ return 0;
+
+ sel_ssid = NULL;
+ selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid);
+ if (selected && sel_ssid && sel_ssid->priority > ssid->priority)
+ return 0; /* higher priority network in scan results */
+
+ return 1;
+}
+
+
+static void remove_duplicate_network(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
+ struct wpa_bss *bss)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->parent_cred != cred)
+ continue;
+ if (ssid->ssid_len != bss->ssid_len ||
+ os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
+ continue;
+
+ break;
+ }
+
+ if (ssid == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Remove duplicate network entry for the same credential");
+
+ if (ssid == wpa_s->current_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
+
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+}
+
+
static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
- if (wpa_config_set(ssid, "key_mgmt",
- wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
- "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0)
+ const char *key_mgmt = NULL;
+#ifdef CONFIG_IEEE80211R
+ int res;
+ struct wpa_driver_capa capa;
+
+ res = wpa_drv_get_capa(wpa_s, &capa);
+ if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
+ key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+ "WPA-EAP WPA-EAP-SHA256 FT-EAP" :
+ "WPA-EAP FT-EAP";
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (!key_mgmt)
+ key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+ "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
+ if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0)
return -1;
if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
return -1;
@@ -768,7 +908,6 @@
{
#ifdef INTERWORKING_3GPP
struct wpa_ssid *ssid;
- const u8 *ie;
int eap_type;
int res;
char prefix;
@@ -776,12 +915,17 @@
if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
return -1;
- ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
- if (ie == NULL)
- return -1;
wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
MAC2STR(bss->bssid));
+ if (already_connected(wpa_s, cred, bss)) {
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+ MAC2STR(bss->bssid));
+ return 0;
+ }
+
+ remove_duplicate_network(wpa_s, cred, bss);
+
ssid = wpa_config_add_network(wpa_s->conf);
if (ssid == NULL)
return -1;
@@ -791,11 +935,12 @@
wpa_config_set_network_defaults(ssid);
ssid->priority = cred->priority;
ssid->temporary = 1;
- ssid->ssid = os_zalloc(ie[1] + 1);
+ ssid->ssid = os_zalloc(bss->ssid_len + 1);
if (ssid->ssid == NULL)
goto fail;
- os_memcpy(ssid->ssid, ie + 2, ie[1]);
- ssid->ssid_len = ie[1];
+ os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+ ssid->ssid_len = bss->ssid_len;
+ ssid->eap.sim_num = cred->sim_num;
if (interworking_set_hs20_params(wpa_s, ssid) < 0)
goto fail;
@@ -851,10 +996,7 @@
goto fail;
}
- if (cred->password && cred->password[0] &&
- wpa_config_set_quoted(ssid, "password", cred->password) < 0)
- goto fail;
-
+ wpa_s->next_ssid = ssid;
wpa_config_update_prio_list(wpa_s->conf);
interworking_reconnect(wpa_s);
@@ -944,6 +1086,27 @@
}
+static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ const u8 *ie;
+
+ if (cred->required_roaming_consortium_len == 0)
+ return 0;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+ if (ie == NULL &&
+ (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+ return 1;
+
+ return !roaming_consortium_match(ie,
+ bss->anqp ?
+ bss->anqp->roaming_consortium : NULL,
+ cred->required_roaming_consortium,
+ cred->required_roaming_consortium_len);
+}
+
+
static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
{
size_t i;
@@ -962,11 +1125,164 @@
}
+static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ int res;
+ unsigned int dl_bandwidth, ul_bandwidth;
+ const u8 *wan;
+ u8 wan_info, dl_load, ul_load;
+ u16 lmd;
+ u32 ul_speed, dl_speed;
+
+ if (!cred->min_dl_bandwidth_home &&
+ !cred->min_ul_bandwidth_home &&
+ !cred->min_dl_bandwidth_roaming &&
+ !cred->min_ul_bandwidth_roaming)
+ return 0; /* No bandwidth constraint specified */
+
+ if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL)
+ return 0; /* No WAN Metrics known - ignore constraint */
+
+ wan = wpabuf_head(bss->anqp->hs20_wan_metrics);
+ wan_info = wan[0];
+ if (wan_info & BIT(3))
+ return 1; /* WAN link at capacity */
+ lmd = WPA_GET_LE16(wan + 11);
+ if (lmd == 0)
+ return 0; /* Downlink/Uplink Load was not measured */
+ dl_speed = WPA_GET_LE32(wan + 1);
+ ul_speed = WPA_GET_LE32(wan + 5);
+ dl_load = wan[9];
+ ul_load = wan[10];
+
+ if (dl_speed >= 0xffffff)
+ dl_bandwidth = dl_speed / 255 * (255 - dl_load);
+ else
+ dl_bandwidth = dl_speed * (255 - dl_load) / 255;
+
+ if (ul_speed >= 0xffffff)
+ ul_bandwidth = ul_speed / 255 * (255 - ul_load);
+ else
+ ul_bandwidth = ul_speed * (255 - ul_load) / 255;
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res > 0) {
+ if (cred->min_dl_bandwidth_home > dl_bandwidth)
+ return 1;
+ if (cred->min_ul_bandwidth_home > ul_bandwidth)
+ return 1;
+ } else {
+ if (cred->min_dl_bandwidth_roaming > dl_bandwidth)
+ return 1;
+ if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ const u8 *ie;
+ int res;
+
+ if (!cred->max_bss_load)
+ return 0; /* No BSS Load constraint specified */
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD);
+ if (ie == NULL || ie[1] < 3)
+ return 0; /* No BSS Load advertised */
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res <= 0)
+ return 0; /* Not a home network */
+
+ return ie[4] > cred->max_bss_load;
+}
+
+
+static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
+{
+ while (pos + 4 <= end) {
+ if (pos[0] == proto && pos[3] == 1 /* Open */)
+ return 1;
+ pos += 4;
+ }
+
+ return 0;
+}
+
+
+static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
+ u16 port)
+{
+ while (pos + 4 <= end) {
+ if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
+ pos[3] == 1 /* Open */)
+ return 1;
+ pos += 4;
+ }
+
+ return 0;
+}
+
+
+static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ int res;
+ const u8 *capab, *end;
+ unsigned int i, j;
+ int *ports;
+
+ if (!cred->num_req_conn_capab)
+ return 0; /* No connection capability constraint specified */
+
+ if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL)
+ return 0; /* No Connection Capability known - ignore constraint
+ */
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res > 0)
+ return 0; /* No constraint in home network */
+
+ capab = wpabuf_head(bss->anqp->hs20_connection_capability);
+ end = capab + wpabuf_len(bss->anqp->hs20_connection_capability);
+
+ for (i = 0; i < cred->num_req_conn_capab; i++) {
+ ports = cred->req_conn_capab_port[i];
+ if (!ports) {
+ if (!has_proto_match(capab, end,
+ cred->req_conn_capab_proto[i]))
+ return 1;
+ } else {
+ for (j = 0; ports[j] > -1; j++) {
+ if (!has_proto_port_match(
+ capab, end,
+ cred->req_conn_capab_proto[i],
+ ports[j]))
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
static struct wpa_cred * interworking_credentials_available_roaming_consortium(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
{
struct wpa_cred *cred, *selected = NULL;
const u8 *ie;
+ int is_excluded = 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
@@ -989,14 +1305,33 @@
cred->roaming_consortium_len))
continue;
- if (cred_excluded_ssid(cred, bss))
+ if (cred_no_required_oi_match(cred, bss))
continue;
-
- if (selected == NULL ||
- selected->priority < cred->priority)
- selected = cred;
+ if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0) {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
}
+ if (excluded)
+ *excluded = is_excluded;
+
return selected;
}
@@ -1100,19 +1435,34 @@
wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
return -1;
+ if (cred->domain_suffix_match && cred->domain_suffix_match[0] &&
+ wpa_config_set_quoted(ssid, "domain_suffix_match",
+ cred->domain_suffix_match) < 0)
+ return -1;
+
+ ssid->eap.ocsp = cred->ocsp;
+
return 0;
}
static int interworking_connect_roaming_consortium(
struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
- struct wpa_bss *bss, const u8 *ssid_ie)
+ struct wpa_bss *bss)
{
struct wpa_ssid *ssid;
wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on "
"roaming consortium match", MAC2STR(bss->bssid));
+ if (already_connected(wpa_s, cred, bss)) {
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+ MAC2STR(bss->bssid));
+ return 0;
+ }
+
+ remove_duplicate_network(wpa_s, cred, bss);
+
ssid = wpa_config_add_network(wpa_s->conf);
if (ssid == NULL)
return -1;
@@ -1121,11 +1471,11 @@
wpa_config_set_network_defaults(ssid);
ssid->priority = cred->priority;
ssid->temporary = 1;
- ssid->ssid = os_zalloc(ssid_ie[1] + 1);
+ ssid->ssid = os_zalloc(bss->ssid_len + 1);
if (ssid->ssid == NULL)
goto fail;
- os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]);
- ssid->ssid_len = ssid_ie[1];
+ os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+ ssid->ssid_len = bss->ssid_len;
if (interworking_set_hs20_params(wpa_s, ssid) < 0)
goto fail;
@@ -1142,6 +1492,7 @@
cred->eap_method->method == EAP_TYPE_TTLS) < 0)
goto fail;
+ wpa_s->next_ssid = ssid;
wpa_config_update_prio_list(wpa_s->conf);
interworking_reconnect(wpa_s);
@@ -1154,7 +1505,8 @@
}
-int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, int allow_excluded)
{
struct wpa_cred *cred, *cred_rc, *cred_3gpp;
struct wpa_ssid *ssid;
@@ -1162,17 +1514,22 @@
struct nai_realm_eap *eap = NULL;
u16 count, i;
char buf[100];
- const u8 *ie;
+ int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
+ const char *name;
if (wpa_s->conf->cred == NULL || bss == NULL)
return -1;
- ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
- if (ie == NULL || ie[1] == 0) {
- wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
+ if (disallowed_bssid(wpa_s, bss->bssid) ||
+ disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Reject connection to disallowed BSS "
MACSTR, MAC2STR(bss->bssid));
return -1;
}
+ wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
+ " for connection (allow_excluded=%d)",
+ MAC2STR(bss->bssid), allow_excluded);
+
if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
/*
* We currently support only HS 2.0 networks and those are
@@ -1183,35 +1540,80 @@
return -1;
}
- cred_rc = interworking_credentials_available_roaming_consortium(wpa_s,
- bss);
+ cred_rc = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, 0, excl);
if (cred_rc) {
wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
- "consortium matching credential priority %d",
- cred_rc->priority);
+ "consortium matching credential priority %d "
+ "sp_priority %d",
+ cred_rc->priority, cred_rc->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
}
- cred = interworking_credentials_available_realm(wpa_s, bss);
+ cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
if (cred) {
wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
- "matching credential priority %d",
- cred->priority);
+ "matching credential priority %d sp_priority %d",
+ cred->priority, cred->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
}
- cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss);
+ cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
+ excl);
if (cred_3gpp) {
wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
- "credential priority %d", cred_3gpp->priority);
+ "credential priority %d sp_priority %d",
+ cred_3gpp->priority, cred_3gpp->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ if (!cred_rc && !cred && !cred_3gpp) {
+ wpa_printf(MSG_DEBUG, "Interworking: No full credential matches - consider options without BW(etc.) limits");
+ cred_rc = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, 1, excl);
+ if (cred_rc) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
+ "consortium matching credential priority %d "
+ "sp_priority %d (ignore BW)",
+ cred_rc->priority, cred_rc->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ cred = interworking_credentials_available_realm(wpa_s, bss, 1,
+ excl);
+ if (cred) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm "
+ "list matching credential priority %d "
+ "sp_priority %d (ignore BW)",
+ cred->priority, cred->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
+ 1, excl);
+ if (cred_3gpp) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP "
+ "matching credential priority %d "
+ "sp_priority %d (ignore BW)",
+ cred_3gpp->priority, cred_3gpp->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
}
if (cred_rc &&
- (cred == NULL || cred_rc->priority >= cred->priority) &&
- (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority))
+ (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) &&
+ (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0))
return interworking_connect_roaming_consortium(wpa_s, cred_rc,
- bss, ie);
+ bss);
if (cred_3gpp &&
- (cred == NULL || cred_3gpp->priority >= cred->priority)) {
+ (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
}
@@ -1248,6 +1650,15 @@
wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
MAC2STR(bss->bssid));
+ if (already_connected(wpa_s, cred, bss)) {
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+ MAC2STR(bss->bssid));
+ nai_realm_free(realm, count);
+ return 0;
+ }
+
+ remove_duplicate_network(wpa_s, cred, bss);
+
ssid = wpa_config_add_network(wpa_s->conf);
if (ssid == NULL) {
nai_realm_free(realm, count);
@@ -1258,11 +1669,11 @@
wpa_config_set_network_defaults(ssid);
ssid->priority = cred->priority;
ssid->temporary = 1;
- ssid->ssid = os_zalloc(ie[1] + 1);
+ ssid->ssid = os_zalloc(bss->ssid_len + 1);
if (ssid->ssid == NULL)
goto fail;
- os_memcpy(ssid->ssid, ie + 2, ie[1]);
- ssid->ssid_len = ie[1];
+ os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+ ssid->ssid_len = bss->ssid_len;
if (interworking_set_hs20_params(wpa_s, ssid) < 0)
goto fail;
@@ -1311,11 +1722,19 @@
}
break;
case EAP_TYPE_PEAP:
- os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
- eap_get_name(EAP_VENDOR_IETF,
- eap->inner_method ?
- eap->inner_method :
- EAP_TYPE_MSCHAPV2));
+ case EAP_TYPE_FAST:
+ if (wpa_config_set(ssid, "phase1", "\"fast_provisioning=2\"",
+ 0) < 0)
+ goto fail;
+ if (wpa_config_set(ssid, "pac_file",
+ "\"blob://pac_interworking\"", 0) < 0)
+ goto fail;
+ name = eap_get_name(EAP_VENDOR_IETF,
+ eap->inner_method ? eap->inner_method :
+ EAP_TYPE_MSCHAPV2);
+ if (name == NULL)
+ goto fail;
+ os_snprintf(buf, sizeof(buf), "\"auth=%s\"", name);
if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
goto fail;
break;
@@ -1329,6 +1748,7 @@
nai_realm_free(realm, count);
+ wpa_s->next_ssid = ssid;
wpa_config_update_prio_list(wpa_s->conf);
interworking_reconnect(wpa_s);
@@ -1342,17 +1762,67 @@
}
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ return interworking_connect_helper(wpa_s, bss, 1);
+}
+
+
+#ifdef PCSC_FUNCS
+static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s)
+{
+ size_t len;
+
+ if (wpa_s->imsi[0] && wpa_s->mnc_len)
+ return 0;
+
+ len = sizeof(wpa_s->imsi) - 1;
+ if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
+ scard_deinit(wpa_s->scard);
+ wpa_s->scard = NULL;
+ wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
+ return -1;
+ }
+ wpa_s->imsi[len] = '\0';
+ wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
+ wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+
+ return 0;
+}
+#endif /* PCSC_FUNCS */
+
+
static struct wpa_cred * interworking_credentials_available_3gpp(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
{
struct wpa_cred *selected = NULL;
#ifdef INTERWORKING_3GPP
struct wpa_cred *cred;
int ret;
+ int is_excluded = 0;
if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
return NULL;
+#ifdef CONFIG_EAP_PROXY
+ if (!wpa_s->imsi[0]) {
+ size_t len;
+ wpa_printf(MSG_DEBUG, "Interworking: IMSI not available - try to read again through eap_proxy");
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+ wpa_s->imsi,
+ &len);
+ if (wpa_s->mnc_len > 0) {
+ wpa_s->imsi[len] = '\0';
+ wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+ } else {
+ wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+ }
+ }
+#endif /* CONFIG_EAP_PROXY */
+
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
char *sep;
const char *imsi;
@@ -1361,8 +1831,9 @@
size_t msin_len;
#ifdef PCSC_FUNCS
- if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
- wpa_s->imsi[0]) {
+ if (cred->pcsc && wpa_s->scard) {
+ if (interworking_pcsc_read_imsi(wpa_s) < 0)
+ continue;
imsi = wpa_s->imsi;
mnc_len = wpa_s->mnc_len;
goto compare;
@@ -1377,7 +1848,8 @@
#endif /* CONFIG_EAP_PROXY */
if (cred->imsi == NULL || !cred->imsi[0] ||
- cred->milenage == NULL || !cred->milenage[0])
+ (!wpa_s->conf->external_sim &&
+ (cred->milenage == NULL || !cred->milenage[0])))
continue;
sep = os_strchr(cred->imsi, '-');
@@ -1402,24 +1874,49 @@
ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
if (ret) {
- if (cred_excluded_ssid(cred, bss))
+ if (cred_no_required_oi_match(cred, bss))
continue;
- if (selected == NULL ||
- selected->priority < cred->priority)
- selected = cred;
+ if (!ignore_bw &&
+ cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0) {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
}
}
+
+ if (excluded)
+ *excluded = is_excluded;
#endif /* INTERWORKING_3GPP */
return selected;
}
static struct wpa_cred * interworking_credentials_available_realm(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
{
struct wpa_cred *cred, *selected = NULL;
struct nai_realm *realm;
u16 count, i;
+ int is_excluded = 0;
if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
return NULL;
@@ -1444,11 +1941,32 @@
if (!nai_realm_match(&realm[i], cred->realm))
continue;
if (nai_realm_find_eap(cred, &realm[i])) {
- if (cred_excluded_ssid(cred, bss))
+ if (cred_no_required_oi_match(cred, bss))
continue;
- if (selected == NULL ||
- selected->priority < cred->priority)
- selected = cred;
+ if (!ignore_bw &&
+ cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0)
+ {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
break;
}
}
@@ -1456,35 +1974,77 @@
nai_realm_free(realm, count);
+ if (excluded)
+ *excluded = is_excluded;
+
return selected;
}
-static struct wpa_cred * interworking_credentials_available(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static struct wpa_cred * interworking_credentials_available_helper(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
{
struct wpa_cred *cred, *cred2;
+ int excluded1, excluded2;
- cred = interworking_credentials_available_realm(wpa_s, bss);
- cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
- if (cred && cred2 && cred2->priority >= cred->priority)
- cred = cred2;
- if (!cred)
- cred = cred2;
+ if (disallowed_bssid(wpa_s, bss->bssid) ||
+ disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Ignore disallowed BSS "
+ MACSTR, MAC2STR(bss->bssid));
+ return NULL;
+ }
- cred2 = interworking_credentials_available_roaming_consortium(wpa_s,
- bss);
- if (cred && cred2 && cred2->priority >= cred->priority)
+ cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
+ &excluded1);
+ cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
+ &excluded2);
+ if (cred && cred2 &&
+ (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
cred = cred2;
- if (!cred)
+ excluded1 = excluded2;
+ }
+ if (!cred) {
cred = cred2;
+ excluded1 = excluded2;
+ }
+ cred2 = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, ignore_bw, &excluded2);
+ if (cred && cred2 &&
+ (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
+ cred = cred2;
+ excluded1 = excluded2;
+ }
+ if (!cred) {
+ cred = cred2;
+ excluded1 = excluded2;
+ }
+
+ if (excluded)
+ *excluded = excluded1;
return cred;
}
-static int domain_name_list_contains(struct wpabuf *domain_names,
- const char *domain)
+static struct wpa_cred * interworking_credentials_available(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded)
+{
+ struct wpa_cred *cred;
+
+ if (excluded)
+ *excluded = 0;
+ cred = interworking_credentials_available_helper(wpa_s, bss, 0,
+ excluded);
+ if (cred)
+ return cred;
+ return interworking_credentials_available_helper(wpa_s, bss, 1,
+ excluded);
+}
+
+
+int domain_name_list_contains(struct wpabuf *domain_names,
+ const char *domain, int exact_match)
{
const u8 *pos, *end;
size_t len;
@@ -1502,6 +2062,12 @@
if (pos[0] == len &&
os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
return 1;
+ if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
+ const char *ap = (const char *) (pos + 1);
+ int offset = pos[0] - len;
+ if (os_strncasecmp(domain, ap + offset, len) == 0)
+ return 1;
+ }
pos += 1 + pos[0];
}
@@ -1514,6 +2080,8 @@
struct wpa_cred *cred,
struct wpabuf *domain_names)
{
+ size_t i;
+ int ret = -1;
#ifdef INTERWORKING_3GPP
char nai[100], *realm;
@@ -1521,13 +2089,20 @@
int mnc_len = 0;
if (cred->imsi)
imsi = cred->imsi;
-#ifdef CONFIG_PCSC
- else if (cred->pcsc && wpa_s->conf->pcsc_reader &&
- wpa_s->scard && wpa_s->imsi[0]) {
+#ifdef PCSC_FUNCS
+ else if (cred->pcsc && wpa_s->scard) {
+ if (interworking_pcsc_read_imsi(wpa_s) < 0)
+ return -1;
imsi = wpa_s->imsi;
mnc_len = wpa_s->mnc_len;
}
-#endif /* CONFIG_PCSC */
+#endif /* PCSC_FUNCS */
+#ifdef CONFIG_EAP_PROXY
+ else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
+ imsi = wpa_s->imsi;
+ mnc_len = wpa_s->mnc_len;
+ }
+#endif /* CONFIG_EAP_PROXY */
if (domain_names &&
imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
realm = os_strchr(nai, '@');
@@ -1536,18 +2111,22 @@
wpa_printf(MSG_DEBUG, "Interworking: Search for match "
"with SIM/USIM domain %s", realm);
if (realm &&
- domain_name_list_contains(domain_names, realm))
+ domain_name_list_contains(domain_names, realm, 1))
return 1;
+ if (realm)
+ ret = 0;
}
#endif /* INTERWORKING_3GPP */
if (domain_names == NULL || cred->domain == NULL)
- return 0;
+ return ret;
- wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
- "home SP FQDN %s", cred->domain);
- if (domain_name_list_contains(domain_names, cred->domain))
- return 1;
+ for (i = 0; i < cred->num_domain; i++) {
+ wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
+ "home SP FQDN %s", cred->domain[i]);
+ if (domain_name_list_contains(domain_names, cred->domain[i], 1))
+ return 1;
+ }
return 0;
}
@@ -1597,19 +2176,127 @@
}
+static int roaming_partner_match(struct wpa_supplicant *wpa_s,
+ struct roaming_partner *partner,
+ struct wpabuf *domain_names)
+{
+ wpa_printf(MSG_DEBUG, "Interworking: Comparing roaming_partner info fqdn='%s' exact_match=%d priority=%u country='%s'",
+ partner->fqdn, partner->exact_match, partner->priority,
+ partner->country);
+ wpa_hexdump_ascii(MSG_DEBUG, "Interworking: Domain names",
+ wpabuf_head(domain_names),
+ wpabuf_len(domain_names));
+ if (!domain_name_list_contains(domain_names, partner->fqdn,
+ partner->exact_match))
+ return 0;
+ /* TODO: match Country */
+ return 1;
+}
+
+
+static u8 roaming_prio(struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
+ struct wpa_bss *bss)
+{
+ size_t i;
+
+ if (bss->anqp == NULL || bss->anqp->domain_name == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: No ANQP domain name info -> use default roaming partner priority 128");
+ return 128; /* cannot check preference with domain name */
+ }
+
+ if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0)
+ {
+ wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority");
+ return 0; /* max preference for home SP network */
+ }
+
+ for (i = 0; i < cred->num_roaming_partner; i++) {
+ if (roaming_partner_match(wpa_s, &cred->roaming_partner[i],
+ bss->anqp->domain_name)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Roaming partner preference match - priority %u",
+ cred->roaming_partner[i].priority);
+ return cred->roaming_partner[i].priority;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "Interworking: No roaming partner preference match - use default roaming partner priority 128");
+ return 128;
+}
+
+
+static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_cred *cred)
+{
+ struct wpa_bss *bss;
+ u8 best_prio, prio;
+ struct wpa_cred *cred2;
+
+ /*
+ * Check if any other BSS is operated by a more preferred roaming
+ * partner.
+ */
+
+ best_prio = roaming_prio(wpa_s, cred, selected);
+ wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS "
+ MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid),
+ cred->id);
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss == selected)
+ continue;
+ cred2 = interworking_credentials_available(wpa_s, bss, NULL);
+ if (!cred2)
+ continue;
+ if (!wpa_bss_get_ie(bss, WLAN_EID_RSN))
+ continue;
+ prio = roaming_prio(wpa_s, cred2, bss);
+ wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS "
+ MACSTR " (cred=%d)", prio, MAC2STR(bss->bssid),
+ cred2->id);
+ if (prio < best_prio) {
+ int bh1, bh2, load1, load2, conn1, conn2;
+ bh1 = cred_below_min_backhaul(wpa_s, cred, selected);
+ load1 = cred_over_max_bss_load(wpa_s, cred, selected);
+ conn1 = cred_conn_capab_missing(wpa_s, cred, selected);
+ bh2 = cred_below_min_backhaul(wpa_s, cred2, bss);
+ load2 = cred_over_max_bss_load(wpa_s, cred2, bss);
+ conn2 = cred_conn_capab_missing(wpa_s, cred2, bss);
+ wpa_printf(MSG_DEBUG, "Interworking: old: %d %d %d new: %d %d %d",
+ bh1, load1, conn1, bh2, load2, conn2);
+ if (bh1 || load1 || conn1 || !(bh2 || load2 || conn2)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Better roaming partner " MACSTR " selected", MAC2STR(bss->bssid));
+ best_prio = prio;
+ selected = bss;
+ }
+ }
+ }
+
+ return selected;
+}
+
+
static void interworking_select_network(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
- int selected_prio = -999999, selected_home_prio = -999999;
+ struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
unsigned int count = 0;
const char *type;
int res;
- struct wpa_cred *cred;
+ struct wpa_cred *cred, *selected_cred = NULL;
+ struct wpa_cred *selected_home_cred = NULL;
+ struct wpa_cred *selected2_cred = NULL;
+ struct wpa_cred *selected2_home_cred = NULL;
wpa_s->network_select = 0;
+ wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)",
+ wpa_s->auto_select);
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
- cred = interworking_credentials_available(wpa_s, bss);
+ int excluded = 0;
+ int bh, bss_load, conn_capab;
+ cred = interworking_credentials_available(wpa_s, bss,
+ &excluded);
if (!cred)
continue;
if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
@@ -1622,7 +2309,8 @@
"RSN", MAC2STR(bss->bssid));
continue;
}
- count++;
+ if (!excluded)
+ count++;
res = interworking_home_sp(wpa_s, bss->anqp ?
bss->anqp->domain_name : NULL);
if (res > 0)
@@ -1631,29 +2319,75 @@
type = "roaming";
else
type = "unknown";
- wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
- MAC2STR(bss->bssid), type);
+ bh = cred_below_min_backhaul(wpa_s, cred, bss);
+ bss_load = cred_over_max_bss_load(wpa_s, cred, bss);
+ conn_capab = cred_conn_capab_missing(wpa_s, cred, bss);
+ wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d",
+ excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
+ MAC2STR(bss->bssid), type,
+ bh ? " below_min_backhaul=1" : "",
+ bss_load ? " over_max_bss_load=1" : "",
+ conn_capab ? " conn_capab_missing=1" : "",
+ cred->id, cred->priority, cred->sp_priority);
+ if (excluded)
+ continue;
if (wpa_s->auto_select ||
(wpa_s->conf->auto_interworking &&
wpa_s->auto_network_select)) {
- if (selected == NULL ||
- cred->priority > selected_prio) {
- selected = bss;
- selected_prio = cred->priority;
- }
- if (res > 0 &&
- (selected_home == NULL ||
- cred->priority > selected_home_prio)) {
- selected_home = bss;
- selected_home_prio = cred->priority;
+ if (bh || bss_load || conn_capab) {
+ if (selected2_cred == NULL ||
+ cred_prio_cmp(cred, selected2_cred) > 0) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2");
+ selected2 = bss;
+ selected2_cred = cred;
+ }
+ if (res > 0 &&
+ (selected2_home_cred == NULL ||
+ cred_prio_cmp(cred, selected2_home_cred) >
+ 0)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2_home");
+ selected2_home = bss;
+ selected2_home_cred = cred;
+ }
+ } else {
+ if (selected_cred == NULL ||
+ cred_prio_cmp(cred, selected_cred) > 0) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected");
+ selected = bss;
+ selected_cred = cred;
+ }
+ if (res > 0 &&
+ (selected_home_cred == NULL ||
+ cred_prio_cmp(cred, selected_home_cred) >
+ 0)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home");
+ selected_home = bss;
+ selected_home_cred = cred;
+ }
}
}
}
if (selected_home && selected_home != selected &&
- selected_home_prio >= selected_prio) {
+ selected_home_cred &&
+ (selected_cred == NULL ||
+ cred_prio_cmp(selected_home_cred, selected_cred) >= 0)) {
/* Prefer network operated by the Home SP */
+ wpa_printf(MSG_DEBUG, "Interworking: Overrided selected with selected_home");
selected = selected_home;
+ selected_cred = selected_home_cred;
+ }
+
+ if (!selected) {
+ if (selected2_home) {
+ wpa_printf(MSG_DEBUG, "Interworking: Use home BSS with BW limit mismatch since no other network could be selected");
+ selected = selected2_home;
+ selected_cred = selected2_home_cred;
+ } else if (selected2) {
+ wpa_printf(MSG_DEBUG, "Interworking: Use visited BSS with BW limit mismatch since no other network could be selected");
+ selected = selected2;
+ selected_cred = selected2_cred;
+ }
}
if (count == 0) {
@@ -1665,9 +2399,10 @@
if (interworking_find_network_match(wpa_s)) {
wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
"match for enabled network configurations");
- if (wpa_s->auto_select)
+ if (wpa_s->auto_select) {
interworking_reconnect(wpa_s);
- return;
+ return;
+ }
}
if (wpa_s->auto_network_select) {
@@ -1682,8 +2417,18 @@
"with matching credentials found");
}
- if (selected)
+ if (selected) {
+ wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR,
+ MAC2STR(selected->bssid));
+ selected = pick_best_roaming_partner(wpa_s, selected,
+ selected_cred);
+ wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR
+ " (after best roaming partner selection)",
+ MAC2STR(selected->bssid));
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
+ MAC2STR(selected->bssid));
interworking_connect(wpa_s, selected);
+ }
}
@@ -1730,8 +2475,21 @@
int found = 0;
const u8 *ie;
- if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
+ wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
+ "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
+ wpa_s->fetch_anqp_in_progress,
+ wpa_s->fetch_osu_icon_in_progress);
+
+ if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
+ wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
return;
+ }
+
+ if (wpa_s->fetch_osu_icon_in_progress) {
+ wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
+ hs20_next_osu_icon(wpa_s);
+ return;
+ }
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (!(bss->caps & IEEE80211_CAP_ESS))
@@ -1739,6 +2497,9 @@
ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
continue; /* AP does not support Interworking */
+ if (disallowed_bssid(wpa_s, bss->bssid) ||
+ disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len))
+ continue; /* Disallowed BSS */
if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
if (bss->anqp == NULL) {
@@ -1762,6 +2523,17 @@
}
if (found == 0) {
+ if (wpa_s->fetch_osu_info) {
+ if (wpa_s->num_prov_found == 0 &&
+ wpa_s->num_osu_scans < 3) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
+ hs20_start_osu_scan(wpa_s);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "Interworking: Next icon");
+ hs20_osu_icon_fetch(wpa_s);
+ return;
+ }
wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
wpa_s->fetch_anqp_in_progress = 0;
if (wpa_s->network_select)
@@ -1789,6 +2561,7 @@
wpa_s->network_select = 0;
wpa_s->fetch_all_anqp = 1;
+ wpa_s->fetch_osu_info = 0;
interworking_start_fetch_anqp(wpa_s);
@@ -1806,9 +2579,10 @@
int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
- u16 info_ids[], size_t num_ids)
+ u16 info_ids[], size_t num_ids, u32 subtypes)
{
struct wpabuf *buf;
+ struct wpabuf *hs20_buf = NULL;
int ret = 0;
int freq;
struct wpa_bss *bss;
@@ -1826,19 +2600,29 @@
wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
MAC2STR(dst), (unsigned int) num_ids);
- buf = anqp_build_req(info_ids, num_ids, NULL);
+#ifdef CONFIG_HS20
+ if (subtypes != 0) {
+ hs20_buf = wpabuf_alloc(100);
+ if (hs20_buf == NULL)
+ return -1;
+ hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
+ }
+#endif /* CONFIG_HS20 */
+
+ buf = anqp_build_req(info_ids, num_ids, hs20_buf);
+ wpabuf_free(hs20_buf);
if (buf == NULL)
return -1;
res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ wpabuf_free(buf);
ret = -1;
} else
wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
"%u", res);
- wpabuf_free(buf);
return ret;
}
@@ -1986,14 +2770,22 @@
u16 slen;
struct wpa_bss *bss = NULL, *tmp;
- if (result != GAS_QUERY_SUCCESS)
+ wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
+ " dialog_token=%u result=%d status_code=%u",
+ MAC2STR(dst), dialog_token, result, status_code);
+ if (result != GAS_QUERY_SUCCESS) {
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_icon_fetch_failed(wpa_s);
return;
+ }
pos = wpabuf_head(adv_proto);
if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
"Protocol in response");
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_icon_fetch_failed(wpa_s);
return;
}
@@ -2033,6 +2825,8 @@
slen);
pos += slen;
}
+
+ hs20_notify_parse_done(wpa_s);
}
@@ -2045,17 +2839,24 @@
}
-int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
+ int *freqs)
{
interworking_stop_fetch_anqp(wpa_s);
wpa_s->network_select = 1;
wpa_s->auto_network_select = 0;
wpa_s->auto_select = !!auto_select;
wpa_s->fetch_all_anqp = 0;
+ wpa_s->fetch_osu_info = 0;
wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
"selection");
wpa_s->scan_res_handler = interworking_scan_res_handler;
+ wpa_s->normal_scans = 0;
wpa_s->scan_req = MANUAL_SCAN_REQ;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = freqs;
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return 0;
@@ -2068,6 +2869,7 @@
const struct wpabuf *resp, u16 status_code)
{
struct wpa_supplicant *wpa_s = ctx;
+ struct wpabuf *n;
wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
" dialog_token=%d status_code=%d resp_len=%d",
@@ -2076,10 +2878,14 @@
if (!resp)
return;
- wpabuf_free(wpa_s->last_gas_resp);
- wpa_s->last_gas_resp = wpabuf_dup(resp);
- if (wpa_s->last_gas_resp == NULL)
+ n = wpabuf_dup(resp);
+ if (n == NULL)
return;
+ wpabuf_free(wpa_s->prev_gas_resp);
+ wpa_s->prev_gas_resp = wpa_s->last_gas_resp;
+ os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN);
+ wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token;
+ wpa_s->last_gas_resp = n;
os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
wpa_s->last_gas_dialog_token = dialog_token;
}
@@ -2095,7 +2901,7 @@
struct wpa_bss *bss;
int res;
size_t len;
- u8 query_resp_len_limit = 0, pame_bi = 0;
+ u8 query_resp_len_limit = 0;
freq = wpa_s->assoc_freq;
bss = wpa_bss_get_bssid(wpa_s, dst);
@@ -2119,8 +2925,7 @@
/* Advertisement Protocol IE */
wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
- wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
- (pame_bi ? 0x80 : 0));
+ wpabuf_put_u8(buf, query_resp_len_limit & 0x7f);
wpabuf_put_buf(buf, adv_proto);
/* GAS Query */
@@ -2133,11 +2938,11 @@
res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
+ wpabuf_free(buf);
ret = -1;
} else
wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
"%u", res);
- wpabuf_free(buf);
return ret;
}
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 4a4af82..38ef745 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -12,7 +12,7 @@
enum gas_query_result;
int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
- u16 info_ids[], size_t num_ids);
+ u16 info_ids[], size_t num_ids, u32 subtypes);
void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
@@ -22,11 +22,14 @@
const struct wpabuf *query);
int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
-int interworking_select(struct wpa_supplicant *wpa_s, int auto_select);
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
+ int *freqs);
int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s);
int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
struct wpa_cred *cred,
struct wpabuf *domain_names);
+int domain_name_list_contains(struct wpabuf *domain_names,
+ const char *domain, int exact_match);
#endif /* INTERWORKING_H */
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index 39b837e..d2e839d 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -16,24 +16,36 @@
#include "driver_i.h"
#include "p2p_supplicant.h"
-extern struct wpa_driver_ops *wpa_drivers[];
-
static void usage(void)
{
int i;
printf("%s\n\n%s\n"
"usage:\n"
- " wpa_supplicant [-BddhKLqqstuvW] [-P<pid file>] "
+ " wpa_supplicant [-BddhKLqq"
+#ifdef CONFIG_DEBUG_SYSLOG
+ "s"
+#endif /* CONFIG_DEBUG_SYSLOG */
+ "t"
+#ifdef CONFIG_DBUS
+ "u"
+#endif /* CONFIG_DBUS */
+ "vW] [-P<pid file>] "
"[-g<global ctrl>] \\\n"
" [-G<group>] \\\n"
" -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
"[-p<driver_param>] \\\n"
- " [-b<br_ifname>] [-f<debug file>] [-e<entropy file>] "
- "\\\n"
+ " [-b<br_ifname>] [-e<entropy file>]"
+#ifdef CONFIG_DEBUG_FILE
+ " [-f<debug file>]"
+#endif /* CONFIG_DEBUG_FILE */
+ " \\\n"
" [-o<override driver>] [-O<override ctrl>] \\\n"
" [-N -i<ifname> -c<conf> [-C<ctrl>] "
"[-D<driver>] \\\n"
+#ifdef CONFIG_P2P
+ " [-m<P2P Device config file>] \\\n"
+#endif /* CONFIG_P2P */
" [-p<driver_param>] [-b<br_ifname>] [-I<config file>] "
"...]\n"
"\n"
@@ -83,6 +95,9 @@
#endif /* CONFIG_DBUS */
printf(" -v = show version\n"
" -W = wait for a control interface monitor before starting\n"
+#ifdef CONFIG_P2P
+ " -m = Configuration file for the P2P Device interface\n"
+#endif /* CONFIG_P2P */
" -N = start describing new interface\n");
printf("example:\n"
@@ -160,7 +175,7 @@
for (;;) {
c = getopt(argc, argv,
- "b:Bc:C:D:de:f:g:G:hi:I:KLNo:O:p:P:qsTtuvW");
+ "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW");
if (c < 0)
break;
switch (c) {
@@ -220,6 +235,11 @@
license();
exitcode = 0;
goto out;
+#ifdef CONFIG_P2P
+ case 'm':
+ iface->conf_p2p_dev = optarg;
+ break;
+#endif /* CONFIG_P2P */
case 'o':
params.override_driver = optarg;
break;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index ab62bea..3b730cf 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -79,12 +79,10 @@
/* notify the new DBus API */
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
-#ifdef CONFIG_P2P
if (new_state == WPA_COMPLETED)
wpas_p2p_notif_connected(wpa_s);
else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
wpas_p2p_notif_disconnected(wpa_s);
-#endif /* CONFIG_P2P */
sme_state_changed(wpa_s);
@@ -96,7 +94,7 @@
MAC2STR(wpa_s->bssid),
wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
wpa_ssid_txt(wpa_s->current_ssid->ssid,
- wpa_s->current_ssid->ssid_len): "");
+ wpa_s->current_ssid->ssid_len) : "");
#endif /* ANDROID */
}
@@ -252,13 +250,13 @@
void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
+ if (wpa_s->next_ssid == ssid)
+ wpa_s->next_ssid = NULL;
if (wpa_s->wpa)
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
wpas_dbus_unregister_network(wpa_s, ssid->id);
-#ifdef CONFIG_P2P
wpas_p2p_network_removed(wpa_s, ssid);
-#endif /* CONFIG_P2P */
}
@@ -440,9 +438,9 @@
const struct wpa_ssid *ssid,
const char *role)
{
- wpas_dbus_unregister_p2p_group(wpa_s, ssid);
-
wpas_dbus_signal_p2p_group_removed(wpa_s, role);
+
+ wpas_dbus_unregister_p2p_group(wpa_s, ssid);
}
@@ -539,17 +537,10 @@
wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
/*
- * Register a group member object corresponding to this peer and
- * emit a PeerJoined signal. This will check if it really is a
- * P2P group.
- */
- wpas_dbus_register_p2p_groupmember(wpa_s, sta);
-
- /*
* Create 'peer-joined' signal on group object -- will also
* check P2P itself.
*/
- wpas_dbus_signal_p2p_peer_joined(wpa_s, sta);
+ wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr);
#endif /* CONFIG_P2P */
/* Notify listeners a new station has been authorized */
@@ -558,20 +549,15 @@
static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
- const u8 *sta)
+ const u8 *sta,
+ const u8 *p2p_dev_addr)
{
#ifdef CONFIG_P2P
/*
- * Unregister a group member object corresponding to this peer
- * if this is a P2P group.
- */
- wpas_dbus_unregister_p2p_groupmember(wpa_s, sta);
-
- /*
* Create 'peer-disconnected' signal on group object if this
* is a P2P group.
*/
- wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta);
+ wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr);
#endif /* CONFIG_P2P */
/* Notify listeners a station has been deauthorized */
@@ -586,7 +572,7 @@
if (authorized)
wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
else
- wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr);
+ wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
}
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index d94407c..17689c5 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -12,6 +12,7 @@
#include "common.h"
#include "utils/eloop.h"
#include "wpa_supplicant_i.h"
+#include "p2p_supplicant.h"
#include "driver_i.h"
#include "offchannel.h"
@@ -55,11 +56,12 @@
without_roc = wpa_s->pending_action_without_roc;
wpa_s->pending_action_without_roc = 0;
- wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback "
- "(without_roc=%d pending_action_tx=%p)",
- without_roc, wpa_s->pending_action_tx);
+ wpa_printf(MSG_DEBUG,
+ "Off-channel: Send Action callback (without_roc=%d pending_action_tx=%p pending_action_tx_done=%d)",
+ without_roc, wpa_s->pending_action_tx,
+ !!wpa_s->pending_action_tx_done);
- if (wpa_s->pending_action_tx == NULL)
+ if (wpa_s->pending_action_tx == NULL || wpa_s->pending_action_tx_done)
return;
/*
@@ -159,6 +161,21 @@
return;
}
+ /* Accept report only if the contents of the frame matches */
+ if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 ||
+ os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx)) != 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+ "mismatching contents with pending frame");
+ wpa_hexdump(MSG_MSGDUMP, "TX status frame data",
+ data, data_len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+ wpa_s->pending_action_tx);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
+
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
@@ -172,6 +189,12 @@
wpa_s->pending_action_bssid,
data, data_len, result);
}
+
+ if (wpa_s->p2p_long_listen > 0) {
+ /* Continue the listen */
+ wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+ wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+ }
}
@@ -220,6 +243,7 @@
MAC2STR(wpa_s->pending_action_dst));
wpabuf_free(wpa_s->pending_action_tx);
}
+ wpa_s->pending_action_tx_done = 0;
wpa_s->pending_action_tx = wpabuf_alloc(len);
if (wpa_s->pending_action_tx == NULL) {
wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
@@ -236,18 +260,22 @@
if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
struct wpa_supplicant *iface;
+ int ret;
iface = wpas_get_tx_interface(wpa_s,
wpa_s->pending_action_src);
wpa_s->action_tx_wait_time = wait_time;
- return wpa_drv_send_action(
+ ret = wpa_drv_send_action(
iface, wpa_s->pending_action_freq,
wait_time, wpa_s->pending_action_dst,
wpa_s->pending_action_src, wpa_s->pending_action_bssid,
wpabuf_head(wpa_s->pending_action_tx),
wpabuf_len(wpa_s->pending_action_tx),
wpa_s->pending_action_no_cck);
+ if (ret == 0)
+ wpa_s->pending_action_tx_done = 1;
+ return ret;
}
if (freq) {
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index d7b3189..3eea9e8 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1,6 +1,7 @@
/*
* wpa_supplicant - P2P
* Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -19,6 +20,7 @@
#include "ap/ap_config.h"
#include "ap/sta_info.h"
#include "ap/ap_drv_ops.h"
+#include "ap/wps_hostapd.h"
#include "ap/p2p_hostapd.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "rsn_supp/wpa.h"
@@ -33,6 +35,7 @@
#include "offchannel.h"
#include "wps_supplicant.h"
#include "p2p_supplicant.h"
+#include "wifi_display.h"
/*
@@ -48,17 +51,14 @@
* How many seconds to try to reconnect to the GO when connection in P2P client
* role has been lost.
*/
-#ifdef ANDROID_P2P
-#define P2P_MAX_CLIENT_IDLE 20
-#else
#define P2P_MAX_CLIENT_IDLE 10
-#endif /* ANDROID_P2P */
#endif /* P2P_MAX_CLIENT_IDLE */
#ifndef P2P_MAX_INITIAL_CONN_WAIT
/*
* How many seconds to wait for initial 4-way handshake to get completed after
- * WPS provisioning step.
+ * WPS provisioning step or after the re-invocation of a persistent group on a
+ * P2P Client.
*/
#define P2P_MAX_INITIAL_CONN_WAIT 10
#endif /* P2P_MAX_INITIAL_CONN_WAIT */
@@ -73,9 +73,14 @@
#define P2P_MAX_INITIAL_CONN_WAIT_GO 10
#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */
-#ifndef P2P_CONCURRENT_SEARCH_DELAY
-#define P2P_CONCURRENT_SEARCH_DELAY 500
-#endif /* P2P_CONCURRENT_SEARCH_DELAY */
+#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE
+/*
+ * How many seconds to wait for initial 4-way handshake to get completed after
+ * re-invocation of a persistent group on the GO when the client is expected
+ * to connect automatically (no user interaction).
+ */
+#define P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE 15
+#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE */
#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-"
@@ -88,9 +93,7 @@
P2P_GROUP_REMOVAL_UNAVAILABLE,
P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
P2P_GROUP_REMOVAL_PSK_FAILURE,
-#ifdef ANDROID_P2P
P2P_GROUP_REMOVAL_FREQ_CONFLICT
-#endif
};
@@ -98,24 +101,28 @@
static struct wpa_supplicant *
wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
int go);
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
-static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *ssid, size_t ssid_len);
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *ssid, size_t ssid_len);
static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
const u8 *dev_addr, enum p2p_wps_method wps_method,
- int auto_join);
+ int auto_join, int freq,
+ const u8 *ssid, size_t ssid_len);
static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
void *timeout_ctx);
-#ifdef ANDROID_P2P
static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
-#endif
static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
int group_added);
static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
+static void wpas_stop_listen(void *ctx);
+static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
+static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
/*
@@ -125,7 +132,7 @@
static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s)
{
int *freqs;
- int num;
+ int num, unused;
freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
if (!freqs)
@@ -135,7 +142,9 @@
wpa_s->num_multichan_concurrent);
os_free(freqs);
- return wpa_s->num_multichan_concurrent - num;
+ unused = wpa_s->num_multichan_concurrent - num;
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused);
+ return unused;
}
@@ -143,28 +152,33 @@
* Get the frequencies that are currently in use by one or more of the virtual
* interfaces, and that are also valid for P2P operation.
*/
-static int wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s,
- int *p2p_freqs, unsigned int len)
+static unsigned int
+wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *p2p_freqs,
+ unsigned int len)
{
- int *freqs;
+ struct wpa_used_freq_data *freqs;
unsigned int num, i, j;
- freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+ freqs = os_calloc(wpa_s->num_multichan_concurrent,
+ sizeof(struct wpa_used_freq_data));
if (!freqs)
- return -1;
+ return 0;
- num = get_shared_radio_freqs(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ num = get_shared_radio_freqs_data(wpa_s, freqs,
+ wpa_s->num_multichan_concurrent);
- os_memset(p2p_freqs, 0, sizeof(int) * len);
+ os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len);
for (i = 0, j = 0; i < num && j < len; i++) {
- if (p2p_supported_freq(wpa_s->global->p2p, freqs[i]))
+ if (p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
p2p_freqs[j++] = freqs[i];
}
os_free(freqs);
+ dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j);
+
return j;
}
@@ -174,10 +188,13 @@
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
- if (freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
- wpas_p2p_num_unused_channels(wpa_s) > 0 &&
- wpa_s->parent->conf->p2p_ignore_shared_freq)
+ if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
+ freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
+ wpas_p2p_num_unused_channels(wpa_s) > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration",
+ freq);
freq = 0;
+ }
p2p_set_own_freq_preference(wpa_s->global->p2p, freq);
}
@@ -187,6 +204,12 @@
{
size_t i;
+ if (wpa_s->p2p_scan_work) {
+ struct wpa_radio_work *work = wpa_s->p2p_scan_work;
+ wpa_s->p2p_scan_work = NULL;
+ radio_work_done(work);
+ }
+
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
@@ -195,14 +218,29 @@
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
- struct os_time time_tmp_age, entry_ts;
+ struct os_reltime time_tmp_age, entry_ts;
+ const u8 *ies;
+ size_t ies_len;
+
time_tmp_age.sec = bss->age / 1000;
time_tmp_age.usec = (bss->age % 1000) * 1000;
- os_time_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
+ os_reltime_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
+
+ ies = (const u8 *) (bss + 1);
+ ies_len = bss->ie_len;
+ if (bss->beacon_ie_len > 0 &&
+ !wpa_scan_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+ wpa_scan_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+ wpa_printf(MSG_DEBUG, "P2P: Use P2P IE(s) from Beacon frame since no P2P IE(s) in Probe Response frames received for "
+ MACSTR, MAC2STR(bss->bssid));
+ ies = ies + ies_len;
+ ies_len = bss->beacon_ie_len;
+ }
+
+
if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
bss->freq, &entry_ts, bss->level,
- (const u8 *) (bss + 1),
- bss->ie_len) > 0)
+ ies, ies_len) > 0)
break;
}
@@ -210,94 +248,130 @@
}
+static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct wpa_driver_scan_params *params = work->ctx;
+ int ret;
+
+ if (deinit) {
+ if (!work->started) {
+ wpa_scan_free_params(params);
+ return;
+ }
+
+ wpa_s->p2p_scan_work = NULL;
+ return;
+ }
+
+ ret = wpa_drv_scan(wpa_s, params);
+ wpa_scan_free_params(params);
+ work->ctx = NULL;
+ if (ret) {
+ radio_work_done(work);
+ return;
+ }
+
+ os_get_reltime(&wpa_s->scan_trigger_time);
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+ wpa_s->own_scan_requested = 1;
+ wpa_s->p2p_scan_work = work;
+}
+
+
static int wpas_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)
{
struct wpa_supplicant *wpa_s = ctx;
- struct wpa_supplicant *ifs;
- struct wpa_driver_scan_params params;
- int ret;
+ struct wpa_driver_scan_params *params = NULL;
struct wpabuf *wps_ie, *ies;
- int social_channels[] = { 2412, 2437, 2462, 0, 0 };
size_t ielen;
+ u8 *n;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
- for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
- if (ifs->sta_scan_pending &&
- (wpas_scan_scheduled(ifs) || ifs->scanning) &&
- wpas_p2p_in_progress(wpa_s) == 2) {
- wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow "
- "pending station mode scan to be "
- "completed on interface %s", ifs->ifname);
- wpa_s->global->p2p_cb_on_scan_complete = 1;
- wpa_supplicant_req_scan(ifs, 0, 0);
- return 1;
- }
+ if (wpa_s->p2p_scan_work) {
+ wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending");
+ return -1;
}
- os_memset(¶ms, 0, sizeof(params));
+ params = os_zalloc(sizeof(*params));
+ if (params == NULL)
+ return -1;
/* P2P Wildcard SSID */
- params.num_ssids = 1;
- params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
- params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+ params->num_ssids = 1;
+ n = os_malloc(P2P_WILDCARD_SSID_LEN);
+ if (n == NULL)
+ goto fail;
+ os_memcpy(n, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+ params->ssids[0].ssid = n;
+ params->ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
wpa_s->wps->dev.p2p = 1;
wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
wpa_s->wps->uuid, WPS_REQ_ENROLLEE,
num_req_dev_types, req_dev_types);
if (wps_ie == NULL)
- return -1;
+ goto fail;
ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
if (ies == NULL) {
wpabuf_free(wps_ie);
- return -1;
+ goto fail;
}
wpabuf_put_buf(ies, wps_ie);
wpabuf_free(wps_ie);
p2p_scan_ie(wpa_s->global->p2p, ies, dev_id);
- params.p2p_probe = 1;
- params.extra_ies = wpabuf_head(ies);
- params.extra_ies_len = wpabuf_len(ies);
+ params->p2p_probe = 1;
+ n = os_malloc(wpabuf_len(ies));
+ if (n == NULL) {
+ wpabuf_free(ies);
+ goto fail;
+ }
+ os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies));
+ params->extra_ies = n;
+ params->extra_ies_len = wpabuf_len(ies);
+ wpabuf_free(ies);
switch (type) {
case P2P_SCAN_SOCIAL:
- params.freqs = social_channels;
+ params->freqs = os_malloc(4 * sizeof(int));
+ if (params->freqs == NULL)
+ goto fail;
+ params->freqs[0] = 2412;
+ params->freqs[1] = 2437;
+ params->freqs[2] = 2462;
+ params->freqs[3] = 0;
break;
case P2P_SCAN_FULL:
break;
case P2P_SCAN_SOCIAL_PLUS_ONE:
- social_channels[3] = freq;
- params.freqs = social_channels;
+ params->freqs = os_malloc(5 * sizeof(int));
+ if (params->freqs == NULL)
+ goto fail;
+ params->freqs[0] = 2412;
+ params->freqs[1] = 2437;
+ params->freqs[2] = 2462;
+ params->freqs[3] = freq;
+ params->freqs[4] = 0;
break;
}
- ret = wpa_drv_scan(wpa_s, ¶ms);
+ radio_remove_works(wpa_s, "p2p-scan", 0);
+ if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb,
+ params) < 0)
+ goto fail;
+ return 0;
- wpabuf_free(ies);
-
- if (ret) {
- for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
- if (ifs->scanning ||
- ifs->scan_res_handler == wpas_p2p_scan_res_handler) {
- wpa_s->global->p2p_cb_on_scan_complete = 1;
- ret = 1;
- break;
- }
- }
- } else {
- os_get_time(&wpa_s->scan_trigger_time);
- wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
- }
-
- return ret;
+fail:
+ wpa_scan_free_params(params);
+ return -1;
}
@@ -405,11 +479,9 @@
case P2P_GROUP_REMOVAL_PSK_FAILURE:
reason = " reason=PSK_FAILURE";
break;
-#ifdef ANDROID_P2P
case P2P_GROUP_REMOVAL_FREQ_CONFLICT:
reason = " reason=FREQ_CONFLICT";
break;
-#endif
default:
reason = "";
break;
@@ -420,9 +492,8 @@
wpa_s->ifname, gtype, reason);
}
-#ifdef ANDROID_P2P
- eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
-#endif
+ if (eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL) > 0)
+ wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group freq_conflict timeout");
if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
@@ -432,6 +503,14 @@
wpa_s->p2p_in_provisioning = 0;
}
+ wpa_s->p2p_in_invitation = 0;
+
+ /*
+ * Make sure wait for the first client does not remain active after the
+ * group has been removed.
+ */
+ wpa_s->global->p2p_go_wait_client.sec = 0;
+
if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
@@ -452,6 +531,17 @@
return 1;
}
+ if (!wpa_s->p2p_go_group_formation_completed) {
+ wpa_s->global->p2p_group_formation = NULL;
+ wpa_s->p2p_in_provisioning = 0;
+ }
+
+ wpa_s->show_group_started = 0;
+ os_free(wpa_s->go_params);
+ wpa_s->go_params = NULL;
+
+ wpa_s->waiting_presence_resp = 0;
+
wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
if (ssid && (ssid->p2p_group ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
@@ -474,7 +564,6 @@
wpa_config_remove_network(wpa_s->conf, id);
wpa_supplicant_clear_status(wpa_s);
wpa_supplicant_cancel_sched_scan(wpa_s);
- wpa_s->sta_scan_pending = 0;
} else {
wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
"found");
@@ -504,6 +593,10 @@
bssid = wpa_s->bssid;
bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+ if (bss == NULL && wpa_s->go_params &&
+ !is_zero_ether_addr(wpa_s->go_params->peer_device_addr))
+ bss = wpa_bss_get_p2p_dev_addr(
+ wpa_s, wpa_s->go_params->peer_device_addr);
if (bss == NULL) {
u8 iface_addr[ETH_ALEN];
if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
@@ -518,6 +611,9 @@
}
p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p == NULL)
+ p2p = wpa_bss_get_vendor_ie_multi_beacon(bss,
+ P2P_IE_VENDOR_TYPE);
if (p2p == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
"group is persistent - BSS " MACSTR
@@ -630,12 +726,10 @@
changed = 1;
}
-#ifndef CONFIG_NO_CONFIG_WRITE
if (changed && wpa_s->conf->update_config &&
wpa_config_write(wpa_s->confname, wpa_s->conf)) {
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
}
-#endif /* CONFIG_NO_CONFIG_WRITE */
return s->id;
}
@@ -692,7 +786,7 @@
os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
s->p2p_client_list = n;
s->num_p2p_clients++;
- } else if (!found) {
+ } else if (!found && s->p2p_client_list) {
/* Not enough room for an additional entry - drop the oldest
* entry */
os_memmove(s->p2p_client_list,
@@ -703,11 +797,54 @@
addr, ETH_ALEN);
}
-#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->parent->conf->update_config &&
wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s,
+ int go, struct wpa_ssid *ssid, int freq,
+ const u8 *psk, const char *passphrase,
+ const u8 *go_dev_addr, int persistent,
+ const char *extra)
+{
+ const char *ssid_txt;
+ char psk_txt[65];
+
+ if (psk)
+ wpa_snprintf_hex(psk_txt, sizeof(psk_txt), psk, 32);
+ else
+ psk_txt[0] = '\0';
+
+ if (ssid)
+ ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+ else
+ ssid_txt = "";
+
+ if (passphrase && passphrase[0] == '\0')
+ passphrase = NULL;
+
+ /*
+ * Include PSK/passphrase only in the control interface message and
+ * leave it out from the debug log entry.
+ */
+ wpa_msg_global_ctrl(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_GROUP_STARTED
+ "%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr="
+ MACSTR "%s%s",
+ wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
+ psk ? " psk=" : "", psk_txt,
+ passphrase ? " passphrase=\"" : "",
+ passphrase ? passphrase : "",
+ passphrase ? "\"" : "",
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "", extra);
+ wpa_printf(MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s %s ssid=\"%s\" freq=%d go_dev_addr=" MACSTR "%s%s",
+ wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
+ MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "",
+ extra);
}
@@ -715,7 +852,6 @@
int success)
{
struct wpa_ssid *ssid;
- const char *ssid_txt;
int client;
int persistent;
u8 go_dev_addr[ETH_ALEN];
@@ -728,8 +864,11 @@
*/
if (wpa_s->global->p2p_group_formation)
wpa_s = wpa_s->global->p2p_group_formation;
- wpa_s->global->p2p_group_formation = NULL;
- wpa_s->p2p_in_provisioning = 0;
+ if (wpa_s->p2p_go_group_formation_completed) {
+ wpa_s->global->p2p_group_formation = NULL;
+ wpa_s->p2p_in_provisioning = 0;
+ }
+ wpa_s->p2p_in_invitation = 0;
if (!success) {
wpa_msg_global(wpa_s->parent, MSG_INFO,
@@ -751,7 +890,6 @@
persistent = 0;
if (ssid) {
- ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
client = ssid->mode == WPAS_MODE_INFRA;
if (ssid->mode == WPAS_MODE_P2P_GO) {
persistent = ssid->p2p_persistent_group;
@@ -763,7 +901,6 @@
ssid->ssid,
ssid->ssid_len);
} else {
- ssid_txt = "";
client = wpa_s->p2p_group_interface ==
P2P_GROUP_INTERFACE_CLIENT;
os_memset(go_dev_addr, 0, ETH_ALEN);
@@ -777,33 +914,13 @@
* packets.
*/
wpa_s->show_group_started = 1;
-#ifdef ANDROID_P2P
- /* For client Second phase of Group formation (4-way handshake) can be still pending
- * So we need to restore wpa_s->global->p2p_group_formation */
- wpa_printf(MSG_INFO, "Restoring back wpa_s->global->p2p_group_formation to wpa_s %p\n", wpa_s);
- wpa_s->global->p2p_group_formation = wpa_s;
-#endif
-
- } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
- char psk[65];
- wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
- wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
- "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr="
- MACSTR "%s",
- wpa_s->ifname, ssid_txt, ssid->frequency, psk,
- MAC2STR(go_dev_addr),
- persistent ? " [PERSISTENT]" : "");
- wpas_p2p_cross_connect_setup(wpa_s);
- wpas_p2p_set_group_idle_timeout(wpa_s);
} else {
- wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
- "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
- "go_dev_addr=" MACSTR "%s",
- wpa_s->ifname, ssid_txt,
- ssid ? ssid->frequency : 0,
- ssid && ssid->passphrase ? ssid->passphrase : "",
- MAC2STR(go_dev_addr),
- persistent ? " [PERSISTENT]" : "");
+ wpas_p2p_group_started(wpa_s, 1, ssid,
+ ssid ? ssid->frequency : 0,
+ ssid && ssid->passphrase == NULL &&
+ ssid->psk_set ? ssid->psk : NULL,
+ ssid ? ssid->passphrase : NULL,
+ go_dev_addr, persistent, "");
wpas_p2p_cross_connect_setup(wpa_s);
wpas_p2p_set_group_idle_timeout(wpa_s);
}
@@ -819,7 +936,60 @@
network_id = ssid->id;
if (!client) {
wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
- os_get_time(&wpa_s->global->p2p_go_wait_client);
+ os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+ }
+}
+
+
+struct send_action_work {
+ unsigned int freq;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ size_t len;
+ unsigned int wait_time;
+ u8 buf[0];
+};
+
+
+static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (!wpa_s->p2p_send_action_work)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
+ os_free(wpa_s->p2p_send_action_work->ctx);
+ radio_work_done(wpa_s->p2p_send_action_work);
+ wpa_s->p2p_send_action_work = NULL;
+}
+
+
+static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_send_action_work) {
+ struct send_action_work *awork;
+ awork = wpa_s->p2p_send_action_work->ctx;
+ if (awork->wait_time == 0) {
+ os_free(awork);
+ radio_work_done(wpa_s->p2p_send_action_work);
+ wpa_s->p2p_send_action_work = NULL;
+ } else {
+ /*
+ * In theory, this should not be needed, but number of
+ * places in the P2P code is still using non-zero wait
+ * time for the last Action frame in the sequence and
+ * some of these do not call send_action_done().
+ */
+ eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+ wpa_s, NULL);
+ eloop_register_timeout(
+ 0, awork->wait_time * 1000,
+ wpas_p2p_send_action_work_timeout,
+ wpa_s, NULL);
+ }
}
}
@@ -834,10 +1004,10 @@
{
enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
+ wpas_p2p_action_tx_clear(wpa_s);
+
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
return;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return;
switch (result) {
case OFFCHANNEL_SEND_ACTION_SUCCESS:
@@ -867,11 +1037,87 @@
}
+static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct send_action_work *awork = work->ctx;
+
+ if (deinit) {
+ if (work->started) {
+ eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+ wpa_s, NULL);
+ wpa_s->p2p_send_action_work = NULL;
+ offchannel_send_action_done(wpa_s);
+ }
+ os_free(awork);
+ return;
+ }
+
+ if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src,
+ awork->bssid, awork->buf, awork->len,
+ awork->wait_time,
+ wpas_p2p_send_action_tx_status, 1) < 0) {
+ os_free(awork);
+ radio_work_done(work);
+ return;
+ }
+ wpa_s->p2p_send_action_work = work;
+}
+
+
+static int wpas_send_action_work(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time)
+{
+ struct send_action_work *awork;
+
+ if (wpa_s->p2p_send_action_work) {
+ wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending");
+ return -1;
+ }
+
+ awork = os_zalloc(sizeof(*awork) + len);
+ if (awork == NULL)
+ return -1;
+
+ awork->freq = freq;
+ os_memcpy(awork->dst, dst, ETH_ALEN);
+ os_memcpy(awork->src, src, ETH_ALEN);
+ os_memcpy(awork->bssid, bssid, ETH_ALEN);
+ awork->len = len;
+ awork->wait_time = wait_time;
+ os_memcpy(awork->buf, buf, len);
+
+ if (radio_add_work(wpa_s, freq, "p2p-send-action", 0,
+ wpas_send_action_cb, awork) < 0) {
+ os_free(awork);
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int wpas_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)
{
struct wpa_supplicant *wpa_s = ctx;
+ int listen_freq = -1, send_freq = -1;
+
+ if (wpa_s->p2p_listen_work)
+ listen_freq = wpa_s->p2p_listen_work->freq;
+ if (wpa_s->p2p_send_action_work)
+ send_freq = wpa_s->p2p_send_action_work->freq;
+ if (listen_freq != (int) freq && send_freq != (int) freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d)",
+ listen_freq, send_freq);
+ return wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf,
+ len, wait_time);
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX");
return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
wait_time,
wpas_p2p_send_action_tx_status, 1);
@@ -881,6 +1127,15 @@
static void wpas_send_action_done(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->p2p_send_action_work) {
+ eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+ wpa_s, NULL);
+ os_free(wpa_s->p2p_send_action_work->ctx);
+ radio_work_done(wpa_s->p2p_send_action_work);
+ wpa_s->p2p_send_action_work = NULL;
+ }
+
offchannel_send_action_done(wpa_s);
}
@@ -901,15 +1156,29 @@
static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *res)
{
- wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR,
- MAC2STR(res->peer_interface_addr));
+ wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR
+ " dev_addr " MACSTR " wps_method %d",
+ MAC2STR(res->peer_interface_addr),
+ MAC2STR(res->peer_device_addr), res->wps_method);
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
res->ssid, res->ssid_len);
wpa_supplicant_ap_deinit(wpa_s);
wpas_copy_go_neg_results(wpa_s, res);
- if (res->wps_method == WPS_PBC)
+ if (res->wps_method == WPS_PBC) {
wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
- else {
+#ifdef CONFIG_WPS_NFC
+ } else if (res->wps_method == WPS_NFC) {
+ wpas_wps_start_nfc(wpa_s, res->peer_device_addr,
+ res->peer_interface_addr,
+ wpa_s->parent->p2p_oob_dev_pw,
+ wpa_s->parent->p2p_oob_dev_pw_id, 1,
+ wpa_s->parent->p2p_oob_dev_pw_id ==
+ DEV_PW_NFC_CONNECTION_HANDOVER ?
+ wpa_s->parent->p2p_peer_oob_pubkey_hash :
+ NULL,
+ NULL, 0, 0);
+#endif /* CONFIG_WPS_NFC */
+ } else {
u16 dev_pw_id = DEV_PW_DEFAULT;
if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
@@ -969,33 +1238,14 @@
wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
if (wpa_s->global->p2p_group_formation == wpa_s)
wpa_s->global->p2p_group_formation = NULL;
- if (os_strlen(params->passphrase) > 0) {
- wpa_msg_global(wpa_s->parent, MSG_INFO,
- P2P_EVENT_GROUP_STARTED
- "%s GO ssid=\"%s\" freq=%d "
- "passphrase=\"%s\" go_dev_addr=" MACSTR
- "%s", wpa_s->ifname,
- wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
- ssid->frequency, params->passphrase,
- MAC2STR(wpa_s->global->p2p_dev_addr),
- params->persistent_group ?
- " [PERSISTENT]" : "");
- } else {
- char psk[65];
- wpa_snprintf_hex(psk, sizeof(psk), params->psk,
- sizeof(params->psk));
- wpa_msg_global(wpa_s->parent, MSG_INFO,
- P2P_EVENT_GROUP_STARTED
- "%s GO ssid=\"%s\" freq=%d psk=%s "
- "go_dev_addr=" MACSTR "%s",
- wpa_s->ifname,
- wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
- ssid->frequency, psk,
- MAC2STR(wpa_s->global->p2p_dev_addr),
- params->persistent_group ?
- " [PERSISTENT]" : "");
- }
+ wpas_p2p_group_started(wpa_s, 1, ssid, ssid->frequency,
+ params->passphrase[0] == '\0' ?
+ params->psk : NULL,
+ params->passphrase,
+ wpa_s->global->p2p_dev_addr,
+ params->persistent_group, "");
+ os_get_reltime(&wpa_s->global->p2p_go_wait_client);
if (params->persistent_group) {
network_id = wpas_p2p_store_persistent_group(
wpa_s->parent, ssid,
@@ -1007,6 +1257,21 @@
wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
wpas_p2p_cross_connect_setup(wpa_s);
wpas_p2p_set_group_idle_timeout(wpa_s);
+
+ if (wpa_s->p2p_first_connection_timeout) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Start group formation timeout of %d seconds until first data connection on GO",
+ wpa_s->p2p_first_connection_timeout);
+ wpa_s->p2p_go_group_formation_completed = 0;
+ wpa_s->global->p2p_group_formation = wpa_s;
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ eloop_register_timeout(
+ wpa_s->p2p_first_connection_timeout, 0,
+ wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ }
+
return;
}
@@ -1017,10 +1282,24 @@
"filtering");
return;
}
- if (params->wps_method == WPS_PBC)
+ if (params->wps_method == WPS_PBC) {
wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
params->peer_device_addr);
- else if (wpa_s->p2p_pin[0])
+#ifdef CONFIG_WPS_NFC
+ } else if (params->wps_method == WPS_NFC) {
+ if (wpa_s->parent->p2p_oob_dev_pw_id !=
+ DEV_PW_NFC_CONNECTION_HANDOVER &&
+ !wpa_s->parent->p2p_oob_dev_pw) {
+ wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+ return;
+ }
+ wpas_ap_wps_add_nfc_pw(
+ wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+ wpa_s->parent->p2p_oob_dev_pw,
+ wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+ wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+#endif /* CONFIG_WPS_NFC */
+ } else if (wpa_s->p2p_pin[0])
wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
wpa_s->p2p_pin, NULL, 0, 0);
os_free(wpa_s->go_params);
@@ -1057,6 +1336,7 @@
WPAS_MODE_P2P_GO;
ssid->frequency = params->freq;
ssid->ht40 = params->ht40;
+ ssid->vht = params->vht;
ssid->ssid = os_zalloc(params->ssid_len + 1);
if (ssid->ssid) {
os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -1086,6 +1366,7 @@
wpa_s->ap_configured_cb = p2p_go_configured;
wpa_s->ap_configured_cb_ctx = wpa_s;
wpa_s->ap_configured_cb_data = wpa_s->go_params;
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_s->connect_without_scan = ssid;
wpa_s->reassociate = 1;
wpa_s->disconnected = 0;
@@ -1125,7 +1406,14 @@
d->pbc_in_m1 = s->pbc_in_m1;
d->ignore_old_scan_res = s->ignore_old_scan_res;
d->beacon_int = s->beacon_int;
+ d->dtim_period = s->dtim_period;
d->disassoc_low_ack = s->disassoc_low_ack;
+ d->disable_scan_offload = s->disable_scan_offload;
+
+ if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) {
+ d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
+ d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
+ }
}
@@ -1143,8 +1431,7 @@
if (os_strlen(ifname) >= IFNAMSIZ &&
os_strlen(wpa_s->ifname) < IFNAMSIZ) {
/* Try to avoid going over the IFNAMSIZ length limit */
- os_snprintf(ifname, sizeof(ifname), "p2p-%d",
- wpa_s->p2p_group_idx);
+ os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
}
}
@@ -1277,12 +1564,21 @@
wpa_s->parent, NULL);
if (wpa_s->global->p2p)
p2p_group_formation_failed(wpa_s->global->p2p);
- else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- wpa_drv_p2p_group_formation_failed(wpa_s);
wpas_group_formation_completed(wpa_s, 0);
}
+static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure");
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ wpa_s->global->p2p_fail_on_wps_complete = 0;
+}
+
+
void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->global->p2p_group_formation != wpa_s)
@@ -1295,7 +1591,7 @@
}
-void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -1316,6 +1612,8 @@
if (wpa_s->p2p_go_ht40)
res->ht40 = 1;
+ if (wpa_s->p2p_go_vht)
+ res->vht = 1;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
"freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
@@ -1345,6 +1643,9 @@
wpas_p2p_init_group_interface(wpa_s, res->role_go);
if (group_wpa_s == NULL) {
wpas_p2p_remove_pending_group_interface(wpa_s);
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout,
+ wpa_s, NULL);
+ wpas_p2p_group_formation_failed(wpa_s);
return;
}
if (group_wpa_s != wpa_s) {
@@ -1380,7 +1681,7 @@
}
-void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
@@ -1390,33 +1691,34 @@
}
-void wpas_dev_found(void *ctx, const u8 *addr,
- const struct p2p_peer_info *info,
- int new_device)
+static void wpas_dev_found(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
struct wpa_supplicant *wpa_s = ctx;
char devtype[WPS_DEV_TYPE_BUFSIZE];
-#define WFD_DEV_INFO_SIZE 9
- char wfd_dev_info_hex[2 * WFD_DEV_INFO_SIZE + 1];
- os_memset(wfd_dev_info_hex, 0, sizeof(wfd_dev_info_hex));
+ char *wfd_dev_info_hex = NULL;
+
#ifdef CONFIG_WIFI_DISPLAY
- if (info->wfd_subelems) {
- wpa_snprintf_hex(wfd_dev_info_hex, sizeof(wfd_dev_info_hex),
- wpabuf_head(info->wfd_subelems),
- WFD_DEV_INFO_SIZE);
- }
+ wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems,
+ WFD_SUBELEM_DEVICE_INFO);
#endif /* CONFIG_WIFI_DISPLAY */
+
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
" p2p_dev_addr=" MACSTR
" pri_dev_type=%s name='%s' config_methods=0x%x "
- "dev_capab=0x%x group_capab=0x%x%s%s",
+ "dev_capab=0x%x group_capab=0x%x%s%s%s",
MAC2STR(addr), MAC2STR(info->p2p_device_addr),
wps_dev_type_bin2str(info->pri_dev_type, devtype,
sizeof(devtype)),
info->device_name, info->config_methods,
info->dev_capab, info->group_capab,
- wfd_dev_info_hex[0] ? " wfd_dev_info=0x" : "", wfd_dev_info_hex);
+ wfd_dev_info_hex ? " wfd_dev_info=0x" : "",
+ wfd_dev_info_hex ? wfd_dev_info_hex : "",
+ info->vendor_elems ? " vendor_elems=1" : "");
+
+ os_free(wfd_dev_info_hex);
#endif /* CONFIG_NO_STDOUT_DEBUG */
wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
@@ -1441,32 +1743,108 @@
}
+struct wpas_p2p_listen_work {
+ unsigned int freq;
+ unsigned int duration;
+ struct wpabuf *probe_resp_ie;
+};
+
+
+static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork)
+{
+ if (lwork == NULL)
+ return;
+ wpabuf_free(lwork->probe_resp_ie);
+ os_free(lwork);
+}
+
+
+static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_p2p_listen_work *lwork;
+
+ if (!wpa_s->p2p_listen_work)
+ return;
+
+ lwork = wpa_s->p2p_listen_work->ctx;
+ wpas_p2p_listen_work_free(lwork);
+ radio_work_done(wpa_s->p2p_listen_work);
+ wpa_s->p2p_listen_work = NULL;
+}
+
+
+static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct wpas_p2p_listen_work *lwork = work->ctx;
+
+ if (deinit) {
+ if (work->started) {
+ wpa_s->p2p_listen_work = NULL;
+ wpas_stop_listen(wpa_s);
+ }
+ wpas_p2p_listen_work_free(lwork);
+ return;
+ }
+
+ wpa_s->p2p_listen_work = work;
+
+ wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->probe_resp_ie, NULL);
+
+ if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
+ "report received Probe Request frames");
+ wpas_p2p_listen_work_done(wpa_s);
+ return;
+ }
+
+ wpa_s->pending_listen_freq = lwork->freq;
+ wpa_s->pending_listen_duration = lwork->duration;
+
+ if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, lwork->duration) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
+ "to remain on channel (%u MHz) for Listen "
+ "state", lwork->freq);
+ wpas_p2p_listen_work_done(wpa_s);
+ wpa_s->pending_listen_freq = 0;
+ return;
+ }
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = lwork->freq;
+}
+
+
static int wpas_start_listen(void *ctx, unsigned int freq,
unsigned int duration,
const struct wpabuf *probe_resp_ie)
{
struct wpa_supplicant *wpa_s = ctx;
+ struct wpas_p2p_listen_work *lwork;
- wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL);
-
- if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
- wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
- "report received Probe Request frames");
+ if (wpa_s->p2p_listen_work) {
+ wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists");
return -1;
}
- wpa_s->pending_listen_freq = freq;
- wpa_s->pending_listen_duration = duration;
+ lwork = os_zalloc(sizeof(*lwork));
+ if (lwork == NULL)
+ return -1;
+ lwork->freq = freq;
+ lwork->duration = duration;
+ if (probe_resp_ie) {
+ lwork->probe_resp_ie = wpabuf_dup(probe_resp_ie);
+ if (lwork->probe_resp_ie == NULL) {
+ wpas_p2p_listen_work_free(lwork);
+ return -1;
+ }
+ }
- if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) {
- wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
- "to remain on channel (%u MHz) for Listen "
- "state", freq);
- wpa_s->pending_listen_freq = 0;
+ if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb,
+ lwork) < 0) {
+ wpas_p2p_listen_work_free(lwork);
return -1;
}
- wpa_s->off_channel_freq = 0;
- wpa_s->roc_waiting_drv_freq = freq;
return 0;
}
@@ -1482,6 +1860,7 @@
}
wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
wpa_drv_probe_req_report(wpa_s, 0);
+ wpas_p2p_listen_work_done(wpa_s);
}
@@ -1982,8 +2361,8 @@
#endif /* CONFIG_WIFI_DISPLAY */
-void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
- u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len)
{
struct wpa_supplicant *wpa_s = ctx;
const u8 *pos = tlvs;
@@ -2100,8 +2479,8 @@
}
-void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
- const u8 *tlvs, size_t tlvs_len)
+static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
{
struct wpa_supplicant *wpa_s = ctx;
const u8 *pos = tlvs;
@@ -2168,8 +2547,6 @@
u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
const struct wpabuf *tlvs)
{
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return wpa_drv_p2p_sd_request(wpa_s, dst, tlvs);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return 0;
return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
@@ -2201,8 +2578,6 @@
static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
const struct wpabuf *tlvs)
{
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return 0;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return 0;
return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
@@ -2280,8 +2655,6 @@
int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
{
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return wpa_drv_p2p_sd_cancel_request(wpa_s, req);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
return p2p_sd_cancel_request(wpa_s->global->p2p,
@@ -2293,11 +2666,6 @@
const u8 *dst, u8 dialog_token,
const struct wpabuf *resp_tlvs)
{
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
- wpa_drv_p2p_sd_response(wpa_s, freq, dst, dialog_token,
- resp_tlvs);
- return;
- }
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
@@ -2307,10 +2675,6 @@
void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
{
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
- wpa_drv_p2p_service_update(wpa_s);
- return;
- }
if (wpa_s->global->p2p)
p2p_sd_service_update(wpa_s->global->p2p);
}
@@ -2435,11 +2799,11 @@
}
-void wpas_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)
+static void wpas_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)
{
struct wpa_supplicant *wpa_s = ctx;
char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -2492,7 +2856,7 @@
}
-void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
{
struct wpa_supplicant *wpa_s = ctx;
unsigned int generated_pin = 0;
@@ -2504,7 +2868,7 @@
wpa_s->pending_pd_before_join = 0;
wpa_printf(MSG_DEBUG, "P2P: Starting pending "
"join-existing-group operation");
- wpas_p2p_join_start(wpa_s);
+ wpas_p2p_join_start(wpa_s, 0, NULL, 0);
return;
}
@@ -2548,7 +2912,7 @@
wpa_printf(MSG_DEBUG, "P2P: Starting pending "
"join-existing-group operation (no ACK for PD "
"Req attempts)");
- wpas_p2p_join_start(wpa_s);
+ wpas_p2p_join_start(wpa_s, 0, NULL, 0);
return;
}
@@ -2570,20 +2934,62 @@
}
+/**
+ * Pick the best frequency to use from all the currently used frequencies.
+ */
+static int wpas_p2p_pick_best_used_freq(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs,
+ unsigned int num)
+{
+ unsigned int i, c;
+
+ /* find a candidate freq that is supported by P2P */
+ for (c = 0; c < num; c++)
+ if (p2p_supported_freq(wpa_s->global->p2p, freqs[c].freq))
+ break;
+
+ if (c == num)
+ return 0;
+
+ /* once we have a candidate, try to find a 'better' one */
+ for (i = c + 1; i < num; i++) {
+ if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
+ continue;
+
+ /*
+ * 1. Infrastructure station interfaces have higher preference.
+ * 2. P2P Clients have higher preference.
+ * 3. All others.
+ */
+ if (freqs[i].flags & WPA_FREQ_USED_BY_INFRA_STATION) {
+ c = i;
+ break;
+ }
+
+ if ((freqs[i].flags & WPA_FREQ_USED_BY_P2P_CLIENT))
+ c = i;
+ }
+ return freqs[c].freq;
+}
+
+
static u8 wpas_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)
+ const struct p2p_channels *channels,
+ int dev_pw_id)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
- int res;
+ struct wpa_used_freq_data *freqs;
struct wpa_supplicant *grp;
+ int best_freq;
if (!persistent_group) {
wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
- " to join an active group", MAC2STR(sa));
+ " to join an active group (SSID: %s)",
+ MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len));
if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
(os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
== 0 ||
@@ -2592,6 +2998,21 @@
"authorized invitation");
goto accept_inv;
}
+
+#ifdef CONFIG_WPS_NFC
+ if (dev_pw_id >= 0 && wpa_s->parent->p2p_nfc_tag_enabled &&
+ dev_pw_id == wpa_s->parent->p2p_oob_dev_pw_id) {
+ wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
+ wpa_s->parent->p2p_wps_method = WPS_NFC;
+ wpa_s->parent->pending_join_wps_method = WPS_NFC;
+ os_memcpy(wpa_s->parent->pending_join_dev_addr,
+ go_dev_addr, ETH_ALEN);
+ os_memcpy(wpa_s->parent->pending_join_iface_addr,
+ bssid, ETH_ALEN);
+ goto accept_inv;
+ }
+#endif /* CONFIG_WPS_NFC */
+
/*
* Do not accept the invitation automatically; notify user and
* request approval.
@@ -2612,6 +3033,7 @@
os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated "
"invitation to re-invoke a persistent group");
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
} else if (!wpa_s->conf->persistent_reconnect)
return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -2654,11 +3076,26 @@
accept_inv:
wpas_p2p_set_own_freq_preference(wpa_s, 0);
+ best_freq = 0;
+ freqs = os_calloc(wpa_s->num_multichan_concurrent,
+ sizeof(struct wpa_used_freq_data));
+ if (freqs) {
+ int num_channels = wpa_s->num_multichan_concurrent;
+ int num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, num_channels);
+ best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+ os_free(freqs);
+ }
+
/* Get one of the frequencies currently in use */
- if (wpas_p2p_valid_oper_freqs(wpa_s, &res, 1) > 0) {
- wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match a channel already used by one of the interfaces");
- *force_freq = res;
- wpas_p2p_set_own_freq_preference(wpa_s, res);
+ if (best_freq > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces");
+ wpas_p2p_set_own_freq_preference(wpa_s, best_freq);
+
+ if (wpa_s->num_multichan_concurrent < 2 ||
+ wpas_p2p_num_unused_channels(wpa_s) < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces");
+ *force_freq = best_freq;
+ }
}
if (*force_freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
@@ -2701,16 +3138,18 @@
if (status == P2P_SC_SUCCESS) {
wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
- " was accepted; op_freq=%d MHz",
- MAC2STR(sa), op_freq);
+ " was accepted; op_freq=%d MHz, SSID=%s",
+ MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len));
if (s) {
int go = s->mode == WPAS_MODE_P2P_GO;
wpas_p2p_group_add_persistent(
- wpa_s, s, go, go ? op_freq : 0, 0, NULL);
+ wpa_s, s, go, 0, op_freq, 0, 0, NULL,
+ go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
} else if (bssid) {
wpa_s->user_initiated_pd = 0;
wpas_p2p_join(wpa_s, bssid, go_dev_addr,
- wpa_s->p2p_wps_method, 0);
+ wpa_s->p2p_wps_method, 0, op_freq,
+ ssid, ssid_len);
}
return;
}
@@ -2765,7 +3204,7 @@
ETH_ALEN) == 0)
break;
}
- if (i >= ssid->num_p2p_clients) {
+ if (i >= ssid->num_p2p_clients || !ssid->p2p_client_list) {
if (ssid->mode != WPAS_MODE_P2P_GO &&
os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d "
@@ -2785,11 +3224,9 @@
ssid->p2p_client_list + (i + 1) * ETH_ALEN,
(ssid->num_p2p_clients - i - 1) * ETH_ALEN);
ssid->num_p2p_clients--;
-#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->parent->conf->update_config &&
wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
}
@@ -2813,10 +3250,12 @@
static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
const struct p2p_channels *channels,
- const u8 *peer)
+ const u8 *peer, int neg_freq,
+ int peer_oper_freq)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *ssid;
+ int freq;
if (bssid) {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
@@ -2868,38 +3307,39 @@
* the persistent group so that we will remain on the current channel to
* acknowledge any possible retransmission from the peer.
*/
-#ifndef ANDROID_P2P
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before "
"starting persistent group");
os_sleep(0, 50000);
-#else
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 100 ms wait on current channel before "
- "starting persistent group");
- os_sleep(0, 100000);
-#endif
+ if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
+ freq_included(channels, neg_freq))
+ freq = neg_freq;
+ else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
+ freq_included(channels, peer_oper_freq))
+ freq = peer_oper_freq;
+ else
+ freq = 0;
+
+ wpa_printf(MSG_DEBUG, "P2P: Persistent group invitation success - op_freq=%d MHz SSID=%s",
+ freq, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
wpas_p2p_group_add_persistent(wpa_s, ssid,
ssid->mode == WPAS_MODE_P2P_GO,
wpa_s->p2p_persistent_go_freq,
- wpa_s->p2p_go_ht40, channels);
+ freq,
+ wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
+ channels,
+ ssid->mode == WPAS_MODE_P2P_GO ?
+ P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
+ 0);
}
static int wpas_p2p_disallowed_freq(struct wpa_global *global,
unsigned int freq)
{
- unsigned int i;
-
- if (global->p2p_disallow_freq == NULL)
- return 0;
-
- for (i = 0; i < global->num_p2p_disallow_freq; i++) {
- if (freq >= global->p2p_disallow_freq[i].min &&
- freq <= global->p2p_disallow_freq[i].max)
- return 1;
- }
-
- return 0;
+ if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
+ return 1;
+ return freq_range_list_includes(&global->p2p_disallow_freq, freq);
}
@@ -2911,10 +3351,13 @@
static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
- struct p2p_channels *chan)
+ struct p2p_channels *chan,
+ struct p2p_channels *cli_chan)
{
int i, cla = 0;
+ os_memset(cli_chan, 0, sizeof(*cli_chan));
+
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
"band");
@@ -2982,6 +3425,10 @@
}
+enum chan_allowed {
+ NOT_ALLOWED, PASSIVE_ONLY, ALLOWED
+};
+
static int has_channel(struct wpa_global *global,
struct hostapd_hw_modes *mode, u8 chan, int *flags)
{
@@ -2991,21 +3438,25 @@
freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
chan * 5;
if (wpas_p2p_disallowed_freq(global, freq))
- return 0;
+ return NOT_ALLOWED;
for (i = 0; i < mode->num_channels; i++) {
if (mode->channels[i].chan == chan) {
if (flags)
*flags = mode->channels[i].flag;
- return !(mode->channels[i].flag &
- (HOSTAPD_CHAN_DISABLED |
- HOSTAPD_CHAN_PASSIVE_SCAN |
- HOSTAPD_CHAN_NO_IBSS |
- HOSTAPD_CHAN_RADAR));
+ if (mode->channels[i].flag &
+ (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR))
+ return NOT_ALLOWED;
+ if (mode->channels[i].flag &
+ (HOSTAPD_CHAN_PASSIVE_SCAN |
+ HOSTAPD_CHAN_NO_IBSS))
+ return PASSIVE_ONLY;
+ return ALLOWED;
}
}
- return 0;
+ return NOT_ALLOWED;
}
@@ -3015,7 +3466,7 @@
u8 min_chan;
u8 max_chan;
u8 inc;
- enum { BW20, BW40PLUS, BW40MINUS } bw;
+ enum { BW20, BW40PLUS, BW40MINUS, BW80 } bw;
};
static struct p2p_oper_class_map op_class[] = {
@@ -3030,73 +3481,171 @@
{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS },
+
+ /*
+ * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
+ * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
+ * 80 MHz, but currently use the following definition for simplicity
+ * (these center frequencies are not actual channels, which makes
+ * has_channel() fail). wpas_p2p_verify_80mhz() should take care of
+ * removing invalid channels.
+ */
+ { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 },
{ -1, 0, 0, 0, 0, BW20 }
};
-static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
- struct hostapd_hw_modes *mode,
- u8 channel, u8 bw)
+static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel)
{
- int flag;
+ u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
+ unsigned int i;
- if (!has_channel(wpa_s->global, mode, channel, &flag))
- return -1;
- if (bw == BW40MINUS &&
- (!(flag & HOSTAPD_CHAN_HT40MINUS) ||
- !has_channel(wpa_s->global, mode, channel - 4, NULL)))
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
return 0;
- if (bw == BW40PLUS &&
- (!(flag & HOSTAPD_CHAN_HT40PLUS) ||
- !has_channel(wpa_s->global, mode, channel + 4, NULL)))
- return 0;
- return 1;
+
+ for (i = 0; i < ARRAY_SIZE(center_channels); i++)
+ /*
+ * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
+ * so the center channel is 6 channels away from the start/end.
+ */
+ if (channel >= center_channels[i] - 6 &&
+ channel <= center_channels[i] + 6)
+ return center_channels[i];
+
+ return 0;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel, u8 bw)
+{
+ u8 center_chan;
+ int i, flags;
+ enum chan_allowed res, ret = ALLOWED;
+
+ center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel);
+ if (!center_chan)
+ return NOT_ALLOWED;
+ if (center_chan >= 58 && center_chan <= 138)
+ return NOT_ALLOWED; /* Do not allow DFS channels for P2P */
+
+ /* check all the channels are available */
+ for (i = 0; i < 4; i++) {
+ int adj_chan = center_chan - 6 + i * 4;
+
+ res = has_channel(wpa_s->global, mode, adj_chan, &flags);
+ if (res == NOT_ALLOWED)
+ return NOT_ALLOWED;
+ if (res == PASSIVE_ONLY)
+ ret = PASSIVE_ONLY;
+
+ if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70))
+ return NOT_ALLOWED;
+ if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50))
+ return NOT_ALLOWED;
+ if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30))
+ return NOT_ALLOWED;
+ if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))
+ return NOT_ALLOWED;
+ }
+
+ return ret;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel, u8 bw)
+{
+ int flag = 0;
+ enum chan_allowed res, res2;
+
+ res2 = res = has_channel(wpa_s->global, mode, channel, &flag);
+ if (bw == BW40MINUS) {
+ if (!(flag & HOSTAPD_CHAN_HT40MINUS))
+ return NOT_ALLOWED;
+ res2 = has_channel(wpa_s->global, mode, channel - 4, NULL);
+ } else if (bw == BW40PLUS) {
+ if (!(flag & HOSTAPD_CHAN_HT40PLUS))
+ return NOT_ALLOWED;
+ res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
+ } else if (bw == BW80) {
+ res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
+ }
+
+ if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
+ return NOT_ALLOWED;
+ if (res == PASSIVE_ONLY || res2 == PASSIVE_ONLY)
+ return PASSIVE_ONLY;
+ return res;
}
static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
- struct p2p_channels *chan)
+ struct p2p_channels *chan,
+ struct p2p_channels *cli_chan)
{
struct hostapd_hw_modes *mode;
- int cla, op;
+ int cla, op, cli_cla;
if (wpa_s->hw.modes == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
"of all supported channels; assume dualband "
"support");
- return wpas_p2p_default_channels(wpa_s, chan);
+ return wpas_p2p_default_channels(wpa_s, chan, cli_chan);
}
- cla = 0;
+ cla = cli_cla = 0;
for (op = 0; op_class[op].op_class; op++) {
struct p2p_oper_class_map *o = &op_class[op];
u8 ch;
- struct p2p_reg_class *reg = NULL;
+ struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
if (mode == NULL)
continue;
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
- if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1)
- continue;
- if (reg == NULL) {
- wpa_printf(MSG_DEBUG, "P2P: Add operating "
- "class %u", o->op_class);
- reg = &chan->reg_class[cla];
- cla++;
- reg->reg_class = o->op_class;
+ enum chan_allowed res;
+ res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
+ if (res == ALLOWED) {
+ if (reg == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Add operating class %u",
+ o->op_class);
+ reg = &chan->reg_class[cla];
+ cla++;
+ reg->reg_class = o->op_class;
+ }
+ reg->channel[reg->channels] = ch;
+ reg->channels++;
+ } else if (res == PASSIVE_ONLY &&
+ wpa_s->conf->p2p_add_cli_chan) {
+ if (cli_reg == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)",
+ o->op_class);
+ cli_reg = &cli_chan->reg_class[cli_cla];
+ cli_cla++;
+ cli_reg->reg_class = o->op_class;
+ }
+ cli_reg->channel[cli_reg->channels] = ch;
+ cli_reg->channels++;
}
- reg->channel[reg->channels] = ch;
- reg->channels++;
}
if (reg) {
wpa_hexdump(MSG_DEBUG, "P2P: Channels",
reg->channel, reg->channels);
}
+ if (cli_reg) {
+ wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)",
+ cli_reg->channel, cli_reg->channels);
+ }
}
chan->reg_classes = cla;
+ cli_chan->reg_classes = cli_cla;
return 0;
}
@@ -3105,7 +3654,8 @@
int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel)
{
- int op, ret;
+ int op;
+ enum chan_allowed ret;
for (op = 0; op_class[op].op_class; op++) {
struct p2p_oper_class_map *o = &op_class[op];
@@ -3116,18 +3666,24 @@
o->bw == BW20 || ch != channel)
continue;
ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
- if (ret < 0)
- continue;
- else if (ret > 0)
+ if (ret == ALLOWED)
return (o->bw == BW40MINUS) ? -1 : 1;
- else
- return 0;
}
}
return 0;
}
+int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel)
+{
+ if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW80))
+ return 0;
+
+ return wpas_p2p_get_center_80mhz(wpa_s, mode, channel);
+}
+
+
static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
size_t buf_len)
{
@@ -3144,10 +3700,31 @@
}
-static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
+ const u8 *ssid, size_t ssid_len)
{
- struct wpa_supplicant *wpa_s = ctx;
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ struct wpa_ssid *s = wpa_s->current_ssid;
+ if (s == NULL)
+ continue;
+ if (s->mode != WPAS_MODE_P2P_GO &&
+ s->mode != WPAS_MODE_AP &&
+ s->mode != WPAS_MODE_P2P_GROUP_FORMATION)
+ continue;
+ if (s->ssid_len != ssid_len ||
+ os_memcmp(ssid, s->ssid, ssid_len) != 0)
+ continue;
+ return wpa_s;
+ }
+ return NULL;
+
+}
+
+
+struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
+ const u8 *peer_dev_addr)
+{
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid == NULL)
@@ -3157,10 +3734,33 @@
if (wpa_s->wpa_state != WPA_COMPLETED &&
wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
continue;
- if (os_memcmp(wpa_s->go_dev_addr, dev_addr, ETH_ALEN) == 0)
- return 1;
+ if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
+ return wpa_s;
}
+ return NULL;
+}
+
+
+static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpas_get_p2p_client_iface(wpa_s, dev_addr) != NULL;
+}
+
+
+static int wpas_is_concurrent_session_active(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_supplicant *ifs;
+
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ if (ifs == wpa_s)
+ continue;
+ if (ifs->wpa_state > WPA_ASSOCIATED)
+ return 1;
+ }
return 0;
}
@@ -3198,7 +3798,20 @@
iface.ifname = wpa_s->pending_interface_name;
iface.driver = wpa_s->driver->name;
iface.driver_param = wpa_s->conf->driver_param;
- iface.confname = wpa_s->confname;
+
+ /*
+ * If a P2P Device configuration file was given, use it as the interface
+ * configuration file (instead of using parent's configuration file.
+ */
+ if (wpa_s->conf_p2p_dev) {
+ iface.confname = wpa_s->conf_p2p_dev;
+ iface.ctrl_interface = NULL;
+ } else {
+ iface.confname = wpa_s->confname;
+ iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+ }
+ iface.conf_p2p_dev = NULL;
+
p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
if (!p2pdev_wpa_s) {
wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface");
@@ -3211,6 +3824,35 @@
}
+static void wpas_presence_resp(void *ctx, const u8 *src, u8 status,
+ const u8 *noa, size_t noa_len)
+{
+ struct wpa_supplicant *wpa_s, *intf = ctx;
+ char hex[100];
+
+ for (wpa_s = intf->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_s->waiting_presence_resp)
+ break;
+ }
+ if (!wpa_s) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No group interface was waiting for presence response");
+ return;
+ }
+ wpa_s->waiting_presence_resp = 0;
+
+ wpa_snprintf_hex(hex, sizeof(hex), noa, noa_len);
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PRESENCE_RESPONSE "src=" MACSTR
+ " status=%u noa=%s", MAC2STR(src), status, hex);
+}
+
+
+static int _wpas_p2p_in_progress(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpas_p2p_in_progress(wpa_s);
+}
+
+
/**
* wpas_p2p_init - Initialize P2P module for %wpa_supplicant
* @global: Pointer to global data from wpa_supplicant_init()
@@ -3232,25 +3874,6 @@
if (global->p2p)
return 0;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
- struct p2p_params params;
-
- wpa_printf(MSG_DEBUG, "P2P: Use driver-based P2P management");
- os_memset(¶ms, 0, sizeof(params));
- params.dev_name = wpa_s->conf->device_name;
- os_memcpy(params.pri_dev_type, wpa_s->conf->device_type,
- WPS_DEV_TYPE_LEN);
- params.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
- os_memcpy(params.sec_dev_type,
- wpa_s->conf->sec_device_type,
- params.num_sec_dev_types * WPS_DEV_TYPE_LEN);
-
- if (wpa_drv_p2p_set_params(wpa_s, ¶ms) < 0)
- return -1;
-
- return 0;
- }
-
os_memset(&p2p, 0, sizeof(p2p));
p2p.cb_ctx = wpa_s;
p2p.debug_print = wpas_p2p_debug_print;
@@ -3275,6 +3898,9 @@
p2p.invitation_result = wpas_invitation_result;
p2p.get_noa = wpas_get_noa;
p2p.go_connected = wpas_go_connected;
+ p2p.presence_resp = wpas_presence_resp;
+ p2p.is_concurrent_session_active = wpas_is_concurrent_session_active;
+ p2p.is_p2p_in_progress = _wpas_p2p_in_progress;
os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -3292,6 +3918,7 @@
wpa_s->conf->p2p_listen_channel) {
p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
p2p.channel = wpa_s->conf->p2p_listen_channel;
+ p2p.channel_forced = 1;
} else {
p2p.reg_class = 81;
/*
@@ -3300,6 +3927,7 @@
*/
os_get_random((u8 *) &r, sizeof(r));
p2p.channel = 1 + (r % 3) * 5;
+ p2p.channel_forced = 0;
}
wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel);
@@ -3335,7 +3963,7 @@
} else
os_memcpy(p2p.country, "XX\x04", 3);
- if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) {
+ if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels)) {
wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
"channel list");
return -1;
@@ -3366,6 +3994,12 @@
p2p.max_listen = wpa_s->max_remain_on_chan;
+ if (wpa_s->conf->p2p_passphrase_len >= 8 &&
+ wpa_s->conf->p2p_passphrase_len <= 63)
+ p2p.passphrase_len = wpa_s->conf->p2p_passphrase_len;
+ else
+ p2p.passphrase_len = 8;
+
global->p2p = p2p_init(&p2p);
if (global->p2p == NULL)
return -1;
@@ -3378,6 +4012,8 @@
global->p2p, wpa_s->conf->wps_vendor_ext[i]);
}
+ p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq);
+
return 0;
}
@@ -3402,15 +4038,24 @@
os_free(wpa_s->go_params);
wpa_s->go_params = NULL;
-#ifdef ANDROID_P2P
- eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
-#endif
+ eloop_cancel_timeout(wpas_p2p_psk_failure_removal, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
wpa_s->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
wpas_p2p_remove_pending_group_interface(wpa_s);
+ eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
+ wpas_p2p_listen_work_done(wpa_s);
+ if (wpa_s->p2p_send_action_work) {
+ os_free(wpa_s->p2p_send_action_work->ctx);
+ radio_work_done(wpa_s->p2p_send_action_work);
+ wpa_s->p2p_send_action_work = NULL;
+ }
+ eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL);
+
+ wpabuf_free(wpa_s->p2p_oob_dev_pw);
+ wpa_s->p2p_oob_dev_pw = NULL;
/* TODO: remove group interface from the driver if this wpa_s instance
* is on top of a P2P group interface */
@@ -3423,7 +4068,7 @@
*
* This function deinitializes the global (per device) P2P module.
*/
-void wpas_p2p_deinit_global(struct wpa_global *global)
+static void wpas_p2p_deinit_global(struct wpa_global *global)
{
struct wpa_supplicant *wpa_s, *tmp;
@@ -3495,12 +4140,6 @@
if (persistent_group && wpa_s->conf->persistent_reconnect)
persistent_group = 2;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
- return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method,
- go_intent, own_interface_addr,
- force_freq, persistent_group);
- }
-
/*
* Increase GO config timeout if HT40 is used since it takes some time
* to scan channels for coex purposes before the BSS can be started.
@@ -3512,7 +4151,9 @@
go_intent, own_interface_addr, force_freq,
persistent_group, ssid ? ssid->ssid : NULL,
ssid ? ssid->ssid_len : 0,
- wpa_s->p2p_pd_before_go_neg, pref_freq);
+ wpa_s->p2p_pd_before_go_neg, pref_freq,
+ wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+ 0);
}
@@ -3526,13 +4167,12 @@
if (persistent_group && wpa_s->conf->persistent_reconnect)
persistent_group = 2;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return -1;
-
return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
go_intent, own_interface_addr, force_freq,
persistent_group, ssid ? ssid->ssid : NULL,
- ssid ? ssid->ssid_len : 0, pref_freq);
+ ssid ? ssid->ssid_len : 0, pref_freq,
+ wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+ 0);
}
@@ -3562,26 +4202,25 @@
static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
{
- int *freqs, res, num, i;
+ int res;
+ unsigned int num, i;
+ struct wpa_used_freq_data *freqs;
if (wpas_p2p_num_unused_channels(wpa_s) > 0) {
/* Multiple channels are supported and not all are in use */
return 0;
}
- freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+ freqs = os_calloc(wpa_s->num_multichan_concurrent,
+ sizeof(struct wpa_used_freq_data));
if (!freqs)
return 1;
num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
wpa_s->num_multichan_concurrent);
- if (num < 0) {
- res = 1;
- goto exit_free;
- }
for (i = 0; i < num; i++) {
- if (freqs[i] == freq) {
+ if (freqs[i].freq == freq) {
wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used",
freq);
res = 0;
@@ -3589,6 +4228,7 @@
}
}
+ wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies");
res = 1;
exit_free:
@@ -3612,7 +4252,8 @@
return 0;
}
- updated = os_time_before(&wpa_s->p2p_auto_started, &bss->last_update);
+ updated = os_reltime_before(&wpa_s->p2p_auto_started,
+ &bss->last_update);
wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at "
"%ld.%06ld (%supdated in last scan)",
bss->last_update.sec, bss->last_update.usec,
@@ -3625,7 +4266,7 @@
static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
- struct wpa_bss *bss;
+ struct wpa_bss *bss = NULL;
int freq;
u8 iface_addr[ETH_ALEN];
@@ -3657,7 +4298,7 @@
MAC2STR(wpa_s->
pending_join_dev_addr),
freq);
- wpas_p2p_join_scan_req(wpa_s, freq);
+ wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0);
return;
}
}
@@ -3695,7 +4336,8 @@
wpa_s->p2p_connect_freq,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
- wpa_s->p2p_go_ht40);
+ wpa_s->p2p_go_ht40,
+ wpa_s->p2p_go_vht);
return;
}
@@ -3729,11 +4371,27 @@
wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
"from P2P peer table: %d MHz", freq);
}
- bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
+ if (wpa_s->p2p_join_ssid_len) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
+ MACSTR " and SSID %s",
+ MAC2STR(wpa_s->pending_join_iface_addr),
+ wpa_ssid_txt(wpa_s->p2p_join_ssid,
+ wpa_s->p2p_join_ssid_len));
+ bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
+ wpa_s->p2p_join_ssid,
+ wpa_s->p2p_join_ssid_len);
+ }
+ if (!bss) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
+ MACSTR, MAC2STR(wpa_s->pending_join_iface_addr));
+ bss = wpa_bss_get_bssid_latest(wpa_s,
+ wpa_s->pending_join_iface_addr);
+ }
if (bss) {
freq = bss->freq;
wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
- "from BSS table: %d MHz", freq);
+ "from BSS table: %d MHz (SSID %s)", freq,
+ wpa_ssid_txt(bss->ssid, bss->ssid_len));
}
if (freq > 0) {
u16 method;
@@ -3801,34 +4459,33 @@
start:
/* Start join operation immediately */
- wpas_p2p_join_start(wpa_s);
+ wpas_p2p_join_start(wpa_s, 0, NULL, 0);
}
-static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *ssid, size_t ssid_len)
{
int ret;
struct wpa_driver_scan_params params;
struct wpabuf *wps_ie, *ies;
size_t ielen;
int freqs[2] = { 0, 0 };
-#ifdef ANDROID_P2P
- int oper_freq;
- /* If freq is not provided, check the operating freq of the GO and do a
- * a directed scan to save time
- */
- if(!freq) {
- freq = (oper_freq = p2p_get_oper_freq(wpa_s->global->p2p,
- wpa_s->pending_join_iface_addr) == -1) ? 0 : oper_freq;
- }
-#endif
os_memset(¶ms, 0, sizeof(params));
/* P2P Wildcard SSID */
params.num_ssids = 1;
- params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
- params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+ if (ssid && ssid_len) {
+ params.ssids[0].ssid = ssid;
+ params.ssids[0].ssid_len = ssid_len;
+ os_memcpy(wpa_s->p2p_join_ssid, ssid, ssid_len);
+ wpa_s->p2p_join_ssid_len = ssid_len;
+ } else {
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+ wpa_s->p2p_join_ssid_len = 0;
+ }
wpa_s->wps->dev.p2p = 1;
wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
@@ -3854,6 +4511,18 @@
params.p2p_probe = 1;
params.extra_ies = wpabuf_head(ies);
params.extra_ies_len = wpabuf_len(ies);
+
+ if (!freq) {
+ int oper_freq;
+ /*
+ * If freq is not provided, check the operating freq of the GO
+ * and use a single channel scan on if possible.
+ */
+ oper_freq = p2p_get_oper_freq(wpa_s->global->p2p,
+ wpa_s->pending_join_iface_addr);
+ if (oper_freq > 0)
+ freq = oper_freq;
+ }
if (freq > 0) {
freqs[0] = freq;
params.freqs = freqs;
@@ -3865,8 +4534,9 @@
*/
ret = wpa_drv_scan(wpa_s, ¶ms);
if (!ret) {
- os_get_time(&wpa_s->scan_trigger_time);
+ os_get_reltime(&wpa_s->scan_trigger_time);
wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
+ wpa_s->own_scan_requested = 1;
}
wpabuf_free(ies);
@@ -3884,18 +4554,23 @@
static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
- wpas_p2p_join_scan_req(wpa_s, 0);
+ wpas_p2p_join_scan_req(wpa_s, 0, NULL, 0);
}
static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
const u8 *dev_addr, enum p2p_wps_method wps_method,
- int auto_join)
+ int auto_join, int op_freq,
+ const u8 *ssid, size_t ssid_len)
{
wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
- MACSTR " dev " MACSTR ")%s",
- MAC2STR(iface_addr), MAC2STR(dev_addr),
+ MACSTR " dev " MACSTR " op_freq=%d)%s",
+ MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq,
auto_join ? " (auto_join)" : "");
+ if (ssid && ssid_len) {
+ wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s",
+ wpa_ssid_txt(ssid, ssid_len));
+ }
wpa_s->p2p_auto_pd = 0;
wpa_s->p2p_auto_join = !!auto_join;
@@ -3907,12 +4582,13 @@
wpas_p2p_stop_find(wpa_s);
wpa_s->p2p_join_scan_count = 0;
- wpas_p2p_join_scan(wpa_s, NULL);
+ wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len);
return 0;
}
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *ssid, size_t ssid_len)
{
struct wpa_supplicant *group;
struct p2p_go_neg_results res;
@@ -3940,14 +4616,25 @@
group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
os_memset(&res, 0, sizeof(res));
+ os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
ETH_ALEN);
res.wps_method = wpa_s->pending_join_wps_method;
- bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
- if (bss) {
- res.freq = bss->freq;
- res.ssid_len = bss->ssid_len;
- os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+ if (freq && ssid && ssid_len) {
+ res.freq = freq;
+ res.ssid_len = ssid_len;
+ os_memcpy(res.ssid, ssid, ssid_len);
+ } else {
+ bss = wpa_bss_get_bssid_latest(wpa_s,
+ wpa_s->pending_join_iface_addr);
+ if (bss) {
+ res.freq = bss->freq;
+ res.ssid_len = bss->ssid_len;
+ os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+ wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
+ bss->freq,
+ wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ }
}
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
@@ -3973,23 +4660,38 @@
static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
- int *force_freq, int *pref_freq)
+ int *force_freq, int *pref_freq, int go)
{
- int *freqs, res;
+ struct wpa_used_freq_data *freqs;
+ int res, best_freq, num_unused;
unsigned int freq_in_use = 0, num, i;
- freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+ freqs = os_calloc(wpa_s->num_multichan_concurrent,
+ sizeof(struct wpa_used_freq_data));
if (!freqs)
return -1;
- num = get_shared_radio_freqs(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+ wpa_s->num_multichan_concurrent);
+
+ /*
+ * It is possible that the total number of used frequencies is bigger
+ * than the number of frequencies used for P2P, so get the system wide
+ * number of unused frequencies.
+ */
+ num_unused = wpas_p2p_num_unused_channels(wpa_s);
+
wpa_printf(MSG_DEBUG,
- "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u",
- freq, wpa_s->num_multichan_concurrent, num);
+ "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u num_unused=%d",
+ freq, wpa_s->num_multichan_concurrent, num, num_unused);
if (freq > 0) {
- if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ int ret;
+ if (go)
+ ret = p2p_supported_freq(wpa_s->global->p2p, freq);
+ else
+ ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
+ if (!ret) {
wpa_printf(MSG_DEBUG, "P2P: The forced channel "
"(%u MHz) is not supported for P2P uses",
freq);
@@ -3998,11 +4700,11 @@
}
for (i = 0; i < num; i++) {
- if (freqs[i] == freq)
+ if (freqs[i].freq == freq)
freq_in_use = 1;
}
- if (num == wpa_s->num_multichan_concurrent && !freq_in_use) {
+ if (num_unused <= 0 && !freq_in_use) {
wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels",
freq);
res = -2;
@@ -4014,38 +4716,28 @@
goto exit_ok;
}
- for (i = 0; i < num; i++) {
- if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i]))
- continue;
+ best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
-#ifndef ANDROID_P2P
- wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use",
- *force_freq);
- *force_freq = freqs[i];
-#endif
-
- if (*pref_freq == 0 && num < wpa_s->num_multichan_concurrent) {
- wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency we are already using");
- *pref_freq = freqs[i];
-#ifdef ANDROID_P2P
+ /* We have a candidate frequency to use */
+ if (best_freq > 0) {
+ if (*pref_freq == 0 && num_unused > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using",
+ best_freq);
+ *pref_freq = best_freq;
} else {
wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use",
- *force_freq);
- *force_freq = freqs[i];
-#endif
+ best_freq);
+ *force_freq = best_freq;
}
- break;
- }
-
- if (i == num) {
- if (num < wpa_s->num_multichan_concurrent) {
- wpa_printf(MSG_DEBUG, "P2P: Current operating channels are not available for P2P. Try to use another channel");
- *force_freq = 0;
- } else {
- wpa_printf(MSG_DEBUG, "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group");
- res = -2;
- goto exit_free;
- }
+ } else if (num_unused > 0) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Current operating channels are not available for P2P. Try to use another channel");
+ *force_freq = 0;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group");
+ res = -2;
+ goto exit_free;
}
exit_ok:
@@ -4074,6 +4766,7 @@
* @pd: Whether to send Provision Discovery prior to GO Negotiation as an
* interoperability workaround when initiating group formation
* @ht40: Start GO with 40 MHz channel width
+ * @vht: Start GO with VHT support
* Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
* failure, -2 on failure due to channel not currently available,
* -3 if forced channel is not supported
@@ -4082,7 +4775,7 @@
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join, int auth,
int go_intent, int freq, int persistent_id, int pd,
- int ht40)
+ int ht40, int vht)
{
int force_freq = 0, pref_freq = 0;
int ret = 0, res;
@@ -4103,6 +4796,8 @@
os_free(wpa_s->global->add_psk);
wpa_s->global->add_psk = NULL;
+ wpa_s->global->p2p_fail_on_wps_complete = 0;
+
if (go_intent < 0)
go_intent = wpa_s->conf->p2p_go_intent;
@@ -4117,6 +4812,7 @@
wpa_s->p2p_fallback_to_go_neg = 0;
wpa_s->p2p_pd_before_go_neg = !!pd;
wpa_s->p2p_go_ht40 = !!ht40;
+ wpa_s->p2p_go_vht = !!vht;
if (pin)
os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
@@ -4146,7 +4842,7 @@
dev_addr);
}
if (auto_join) {
- os_get_time(&wpa_s->p2p_auto_started);
+ os_get_reltime(&wpa_s->p2p_auto_started);
wpa_printf(MSG_DEBUG, "P2P: Auto join started at "
"%ld.%06ld",
wpa_s->p2p_auto_started.sec,
@@ -4154,15 +4850,17 @@
}
wpa_s->user_initiated_pd = 1;
if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
- auto_join) < 0)
+ auto_join, freq, NULL, 0) < 0)
return -1;
return ret;
}
- res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq);
+ res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+ go_intent == 15);
if (res)
return res;
- wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
+ wpas_p2p_set_own_freq_preference(wpa_s,
+ force_freq ? force_freq : pref_freq);
wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
@@ -4219,20 +4917,20 @@
p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
wpa_s->pending_listen_duration);
wpa_s->pending_listen_freq = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d freq=%u duration=%u)",
+ wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
+ freq, duration);
}
}
-static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s,
- unsigned int timeout)
+int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout)
{
/* Limit maximum Listen state time based on driver limitation. */
if (timeout > wpa_s->max_remain_on_chan)
timeout = wpa_s->max_remain_on_chan;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return wpa_drv_p2p_listen(wpa_s, timeout);
-
return p2p_listen(wpa_s->global->p2p, timeout);
}
@@ -4252,14 +4950,15 @@
wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
"(p2p_long_listen=%d ms pending_action_tx=%p)",
wpa_s->p2p_long_listen, offchannel_pending_action_tx(wpa_s));
+ wpas_p2p_listen_work_done(wpa_s);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
+ if (wpa_s->p2p_long_listen > 0)
+ wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
return; /* P2P module started a new operation */
if (offchannel_pending_action_tx(wpa_s))
return;
- if (wpa_s->p2p_long_listen > 0)
- wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
if (wpa_s->p2p_long_listen > 0) {
wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
@@ -4322,8 +5021,8 @@
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
"band");
if (wpa_s->best_24_freq > 0 &&
- p2p_supported_freq(wpa_s->global->p2p,
- wpa_s->best_24_freq)) {
+ p2p_supported_freq_go(wpa_s->global->p2p,
+ wpa_s->best_24_freq)) {
freq = wpa_s->best_24_freq;
wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
"channel: %d MHz", freq);
@@ -4339,7 +5038,7 @@
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
"band");
if (wpa_s->best_5_freq > 0 &&
- p2p_supported_freq(wpa_s->global->p2p,
+ p2p_supported_freq_go(wpa_s->global->p2p,
wpa_s->best_5_freq)) {
freq = wpa_s->best_5_freq;
wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
@@ -4347,7 +5046,7 @@
} else {
os_get_random((u8 *) &r, sizeof(r));
freq = 5180 + (r % 4) * 20;
- if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
wpa_printf(MSG_DEBUG, "P2P: Could not select "
"5 GHz channel for P2P group");
return -1;
@@ -4357,7 +5056,7 @@
}
}
- if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
"(%u MHz) is not supported for P2P uses",
freq);
@@ -4368,18 +5067,52 @@
}
+static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ const struct p2p_channels *channels)
+{
+ unsigned int i, r;
+
+ /* first try some random selection of the social channels */
+ os_get_random((u8 *) &r, sizeof(r));
+
+ for (i = 0; i < 3; i++) {
+ params->freq = 2412 + ((r + i) % 3) * 25;
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+ freq_included(channels, params->freq))
+ goto out;
+ }
+
+ /* try all channels in reg. class 81 */
+ for (i = 0; i < 11; i++) {
+ params->freq = 2412 + i * 5;
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+ freq_included(channels, params->freq))
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel allowed");
+ return -1;
+out:
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
+ params->freq);
+ return 0;
+}
+
+
static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params,
- int freq, int ht40,
+ int freq, int ht40, int vht,
const struct p2p_channels *channels)
{
- int res, *freqs;
- unsigned int pref_freq;
+ struct wpa_used_freq_data *freqs;
+ unsigned int pref_freq, cand_freq;
unsigned int num, i;
os_memset(params, 0, sizeof(*params));
params->role_go = 1;
params->ht40 = ht40;
+ params->vht = vht;
if (freq) {
if (!freq_included(channels, freq)) {
wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
@@ -4410,24 +5143,24 @@
"frequency %d MHz", params->freq);
} else if (wpa_s->conf->p2p_oper_channel == 0 &&
wpa_s->best_overall_freq > 0 &&
- p2p_supported_freq(wpa_s->global->p2p,
- wpa_s->best_overall_freq) &&
+ p2p_supported_freq_go(wpa_s->global->p2p,
+ wpa_s->best_overall_freq) &&
freq_included(channels, wpa_s->best_overall_freq)) {
params->freq = wpa_s->best_overall_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
"channel %d MHz", params->freq);
} else if (wpa_s->conf->p2p_oper_channel == 0 &&
wpa_s->best_24_freq > 0 &&
- p2p_supported_freq(wpa_s->global->p2p,
- wpa_s->best_24_freq) &&
+ p2p_supported_freq_go(wpa_s->global->p2p,
+ wpa_s->best_24_freq) &&
freq_included(channels, wpa_s->best_24_freq)) {
params->freq = wpa_s->best_24_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
"channel %d MHz", params->freq);
} else if (wpa_s->conf->p2p_oper_channel == 0 &&
wpa_s->best_5_freq > 0 &&
- p2p_supported_freq(wpa_s->global->p2p,
- wpa_s->best_5_freq) &&
+ p2p_supported_freq_go(wpa_s->global->p2p,
+ wpa_s->best_5_freq) &&
freq_included(channels, wpa_s->best_5_freq)) {
params->freq = wpa_s->best_5_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
@@ -4438,47 +5171,37 @@
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
"channels", params->freq);
} else {
- int chan;
- for (chan = 0; chan < 11; chan++) {
- params->freq = 2412 + chan * 5;
- if (!wpas_p2p_disallowed_freq(wpa_s->global,
- params->freq) &&
- freq_included(channels, params->freq))
- break;
- }
- if (chan == 11) {
- wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel "
- "allowed");
+ /* no preference, select some channel */
+ if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
return -1;
- }
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
- "known)", params->freq);
}
- freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+ freqs = os_calloc(wpa_s->num_multichan_concurrent,
+ sizeof(struct wpa_used_freq_data));
if (!freqs)
return -1;
- res = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+ num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
wpa_s->num_multichan_concurrent);
- if (res < 0) {
- os_free(freqs);
- return -1;
- }
- num = res;
- if (!freq) {
+ cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+
+ /* First try the best used frequency if possible */
+ if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) {
+ params->freq = cand_freq;
+ } else if (!freq) {
+ /* Try any of the used frequencies */
for (i = 0; i < num; i++) {
- if (freq_included(channels, freqs[i])) {
+ if (freq_included(channels, freqs[i].freq)) {
wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)",
- freqs[i]);
- params->freq = freqs[i];
+ freqs[i].freq);
+ params->freq = freqs[i].freq;
break;
}
}
if (i == num) {
- if (num == wpa_s->num_multichan_concurrent) {
+ if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using");
os_free(freqs);
return -1;
@@ -4488,20 +5211,22 @@
}
} else {
for (i = 0; i < num; i++) {
- if (freqs[i] == freq)
+ if (freqs[i].freq == freq)
break;
}
if (i == num) {
- if (num == wpa_s->num_multichan_concurrent) {
- wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
+ if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+ if (freq)
+ wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
os_free(freqs);
return -1;
} else {
- wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels");
+ wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels");
}
}
}
+
os_free(freqs);
return 0;
}
@@ -4516,6 +5241,7 @@
if (!wpas_p2p_create_iface(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group "
"operations");
+ wpa_s->p2p_first_connection_timeout = 0;
return wpa_s;
}
@@ -4535,6 +5261,7 @@
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
group_wpa_s->ifname);
+ group_wpa_s->p2p_first_connection_timeout = 0;
return group_wpa_s;
}
@@ -4544,13 +5271,15 @@
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @persistent_group: Whether to create a persistent group
* @freq: Frequency for the group or 0 to indicate no hardcoding
+ * @ht40: Start GO with 40 MHz channel width
+ * @vht: Start GO with VHT support
* Returns: 0 on success, -1 on failure
*
* This function creates a new P2P group with the local end as the Group Owner,
* i.e., without using Group Owner Negotiation.
*/
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
- int freq, int ht40)
+ int freq, int ht40, int vht)
{
struct p2p_go_neg_results params;
@@ -4568,10 +5297,10 @@
if (freq < 0)
return -1;
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, NULL))
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, NULL))
return -1;
if (params.freq &&
- !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
+ !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
"(%u MHz) is not supported for P2P uses",
params.freq);
@@ -4590,7 +5319,8 @@
static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *params, int addr_allocated)
+ struct wpa_ssid *params, int addr_allocated,
+ int freq)
{
struct wpa_ssid *ssid;
@@ -4626,9 +5356,16 @@
if (params->passphrase)
ssid->passphrase = os_strdup(params->passphrase);
- wpa_supplicant_select_network(wpa_s, ssid);
-
wpa_s->show_group_started = 1;
+ wpa_s->p2p_in_invitation = 1;
+ wpa_s->p2p_invite_go_freq = freq;
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+ NULL);
+ eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+ wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ wpa_supplicant_select_network(wpa_s, ssid);
return 0;
}
@@ -4636,11 +5373,12 @@
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
- int freq, int ht40,
- const struct p2p_channels *channels)
+ int force_freq, int neg_freq, int ht40,
+ int vht, const struct p2p_channels *channels,
+ int connection_timeout)
{
struct p2p_go_neg_results params;
- int go = 0;
+ int go = 0, freq;
if (ssid->disabled != 2 || ssid->ssid == NULL)
return -1;
@@ -4660,17 +5398,23 @@
wpa_s->p2p_fallback_to_go_neg = 0;
+ if (force_freq > 0) {
+ freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
+ if (freq < 0)
+ return -1;
+ } else {
+ freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
+ if (freq < 0 || (freq > 0 && !freq_included(channels, freq)))
+ freq = 0;
+ }
+
if (ssid->mode == WPAS_MODE_INFRA)
- return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
+ return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq);
if (ssid->mode != WPAS_MODE_P2P_GO)
return -1;
- freq = wpas_p2p_select_go_freq(wpa_s, freq);
- if (freq < 0)
- return -1;
-
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, channels))
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels))
return -1;
params.role_go = 1;
@@ -4694,6 +5438,7 @@
if (wpa_s == NULL)
return -1;
+ wpa_s->p2p_first_connection_timeout = connection_timeout;
wpas_start_wps_go(wpa_s, ¶ms, 0);
return 0;
@@ -4731,9 +5476,14 @@
if (!wpa_s->ap_iface)
return;
wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
- if (idle)
+ if (idle) {
+ if (wpa_s->global->p2p_fail_on_wps_complete &&
+ wpa_s->p2p_in_provisioning) {
+ wpas_p2p_grpform_fail_after_wps(wpa_s);
+ return;
+ }
wpas_p2p_set_group_idle_timeout(wpa_s);
- else
+ } else
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
}
@@ -4744,8 +5494,6 @@
struct p2p_group *group;
struct p2p_group_config *cfg;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return NULL;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return NULL;
@@ -4765,6 +5513,7 @@
cfg->max_clients = wpa_s->conf->max_num_sta;
os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len);
cfg->ssid_len = ssid->ssid_len;
+ cfg->freq = ssid->frequency;
cfg->cb_ctx = wpa_s;
cfg->ie_update = wpas_p2p_ie_update;
cfg->idle_update = wpas_p2p_idle_update;
@@ -4801,6 +5550,7 @@
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
NULL);
+ wpa_s->p2p_go_group_formation_completed = 1;
if (ssid && ssid->mode == WPAS_MODE_INFRA) {
/*
* Use a separate timeout for initial data connection to
@@ -4808,14 +5558,31 @@
* something goes wrong in this step before the P2P group idle
* timeout mechanism is taken into use.
*/
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Re-start group formation timeout (%d seconds) as client for initial connection",
+ P2P_MAX_INITIAL_CONN_WAIT);
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
wpas_p2p_group_formation_timeout,
wpa_s->parent, NULL);
+ } else if (ssid) {
+ /*
+ * Use a separate timeout for initial data connection to
+ * complete to allow the group to be removed automatically if
+ * the client does not complete data connection successfully.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Re-start group formation timeout (%d seconds) as GO for initial connection",
+ P2P_MAX_INITIAL_CONN_WAIT_GO);
+ eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0,
+ wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ /*
+ * Complete group formation on first successful data connection
+ */
+ wpa_s->p2p_go_group_formation_completed = 0;
}
if (wpa_s->global->p2p)
p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
- else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- wpa_drv_wps_success_cb(wpa_s, peer_addr);
wpas_group_formation_completed(wpa_s, 1);
}
@@ -4836,6 +5603,31 @@
}
wpas_notify_p2p_wps_failed(wpa_s, fail);
+
+ if (wpa_s == wpa_s->global->p2p_group_formation) {
+ /*
+ * Allow some time for the failed WPS negotiation exchange to
+ * complete, but remove the group since group formation cannot
+ * succeed after provisioning failure.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: WPS step failed during group formation - reject connection from timeout");
+ wpa_s->global->p2p_fail_on_wps_complete = 1;
+ eloop_deplete_timeout(0, 50000,
+ wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ }
+}
+
+
+int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->global->p2p_fail_on_wps_complete ||
+ !wpa_s->p2p_in_provisioning)
+ return 0;
+
+ wpas_p2p_grpform_fail_after_wps(wpa_s);
+
+ return 1;
}
@@ -4868,7 +5660,7 @@
wpa_s->auto_pd_scan_retry = 0;
wpas_p2p_stop_find(wpa_s);
wpa_s->p2p_join_scan_count = 0;
- os_get_time(&wpa_s->p2p_auto_started);
+ os_get_reltime(&wpa_s->p2p_auto_started);
wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld",
wpa_s->p2p_auto_started.sec,
wpa_s->p2p_auto_started.usec);
@@ -4876,12 +5668,6 @@
return 0;
}
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
- return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr,
- config_methods,
- use == WPAS_P2P_PD_FOR_JOIN);
- }
-
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
return -1;
@@ -4903,6 +5689,8 @@
if (!offchannel_pending_action_tx(wpa_s))
return;
+ wpas_p2p_action_tx_clear(wpa_s);
+
wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
"operation request");
offchannel_clear_pending_action_tx(wpa_s);
@@ -4917,9 +5705,6 @@
wpas_p2p_clear_pending_action_tx(wpa_s);
wpa_s->p2p_long_listen = 0;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return wpa_drv_p2p_find(wpa_s, timeout, type);
-
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
wpa_s->p2p_in_provisioning)
return -1;
@@ -4938,12 +5723,6 @@
wpa_s->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
- wpa_s->global->p2p_cb_on_scan_complete = 0;
-
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
- wpa_drv_p2p_stop_find(wpa_s);
- return 1;
- }
if (wpa_s->global->p2p)
p2p_stop_find(wpa_s->global->p2p);
@@ -5081,7 +5860,7 @@
}
-void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
{
p2p_group_deinit(wpa_s->p2p_group);
wpa_s->p2p_group = NULL;
@@ -5097,9 +5876,6 @@
{
wpa_s->p2p_long_listen = 0;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return wpa_drv_p2p_reject(wpa_s, addr);
-
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
@@ -5110,12 +5886,13 @@
/* Invite to reinvoke a persistent group */
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
- int ht40, int pref_freq)
+ int ht40, int vht, int pref_freq)
{
enum p2p_invite_role role;
u8 *bssid = NULL;
int force_freq = 0;
int res;
+ int no_pref_freq_given = pref_freq == 0;
wpa_s->global->p2p_invite_group = NULL;
if (peer_addr)
@@ -5149,21 +5926,26 @@
}
wpa_s->pending_invite_ssid_id = ssid->id;
- res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq);
+ res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+ role == P2P_INVITE_ROLE_GO);
if (res)
return res;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
- ssid->ssid, ssid->ssid_len,
- go_dev_addr, 1);
-
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
+ if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
+ no_pref_freq_given && pref_freq > 0 &&
+ wpa_s->num_multichan_concurrent > 1 &&
+ wpas_p2p_num_unused_channels(wpa_s) > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz for invitation due to p2p_ignore_shared_freq=1 configuration",
+ pref_freq);
+ pref_freq = 0;
+ }
+
return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
- 1, pref_freq);
+ 1, pref_freq, -1);
}
@@ -5176,11 +5958,12 @@
u8 *bssid = NULL;
struct wpa_ssid *ssid;
int persistent;
- int force_freq = 0, pref_freq = 0;
+ int freq = 0, force_freq = 0, pref_freq = 0;
int res;
wpa_s->p2p_persistent_go_freq = 0;
wpa_s->p2p_go_ht40 = 0;
+ wpa_s->p2p_go_vht = 0;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, ifname) == 0)
@@ -5208,6 +5991,7 @@
bssid = wpa_s->own_addr;
if (go_dev_addr == NULL)
go_dev_addr = wpa_s->global->p2p_dev_addr;
+ freq = ssid->frequency;
} else {
role = P2P_INVITE_ROLE_CLIENT;
if (wpa_s->wpa_state < WPA_ASSOCIATED) {
@@ -5219,36 +6003,35 @@
if (go_dev_addr == NULL &&
!is_zero_ether_addr(wpa_s->go_dev_addr))
go_dev_addr = wpa_s->go_dev_addr;
+ freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
+ (int) wpa_s->assoc_freq;
}
wpa_s->parent->pending_invite_ssid_id = -1;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
- ssid->ssid, ssid->ssid_len,
- go_dev_addr, persistent);
-
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
- res = wpas_p2p_setup_freqs(wpa_s, 0, &force_freq, &pref_freq);
+ res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+ role == P2P_INVITE_ROLE_ACTIVE_GO);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
ssid->ssid, ssid->ssid_len, force_freq,
- go_dev_addr, persistent, pref_freq);
+ go_dev_addr, persistent, pref_freq, -1);
}
void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
- const char *ssid_txt;
u8 go_dev_addr[ETH_ALEN];
int network_id = -1;
int persistent;
int freq;
+ u8 ip[3 * 4];
+ char ip_addr[100];
if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
@@ -5256,11 +6039,10 @@
}
if (!wpa_s->show_group_started || !ssid)
- goto done;
+ return;
wpa_s->show_group_started = 0;
- ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
os_memset(go_dev_addr, 0, ETH_ALEN);
if (ssid->bssid_set)
os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
@@ -5273,42 +6055,36 @@
freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
(int) wpa_s->assoc_freq;
- if (ssid->passphrase == NULL && ssid->psk_set) {
- char psk[65];
- wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
- wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
- "%s client ssid=\"%s\" freq=%d psk=%s "
- "go_dev_addr=" MACSTR "%s",
- wpa_s->ifname, ssid_txt, freq, psk,
- MAC2STR(go_dev_addr),
- persistent ? " [PERSISTENT]" : "");
- } else {
- wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
- "%s client ssid=\"%s\" freq=%d "
- "passphrase=\"%s\" go_dev_addr=" MACSTR "%s",
- wpa_s->ifname, ssid_txt, freq,
- ssid->passphrase ? ssid->passphrase : "",
- MAC2STR(go_dev_addr),
- persistent ? " [PERSISTENT]" : "");
+
+ ip_addr[0] = '\0';
+ if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) {
+ os_snprintf(ip_addr, sizeof(ip_addr), " ip_addr=%u.%u.%u.%u "
+ "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u",
+ ip[0], ip[1], ip[2], ip[3],
+ ip[4], ip[5], ip[6], ip[7],
+ ip[8], ip[9], ip[10], ip[11]);
}
+ wpas_p2p_group_started(wpa_s, 0, ssid, freq,
+ ssid->passphrase == NULL && ssid->psk_set ?
+ ssid->psk : NULL,
+ ssid->passphrase, go_dev_addr, persistent,
+ ip_addr);
+
if (persistent)
network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
ssid, go_dev_addr);
if (network_id < 0)
network_id = ssid->id;
wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
-
-done:
- wpas_p2p_continue_after_scan(wpa_s);
}
int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
u32 interval1, u32 duration2, u32 interval2)
{
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return -1;
+ int ret;
+
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
@@ -5317,18 +6093,19 @@
wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
return -1;
- return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
- wpa_s->own_addr, wpa_s->assoc_freq,
- duration1, interval1, duration2, interval2);
+ ret = p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->assoc_freq,
+ duration1, interval1, duration2, interval2);
+ if (ret == 0)
+ wpa_s->waiting_presence_resp = 1;
+
+ return ret;
}
int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
unsigned int interval)
{
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return -1;
-
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
@@ -5430,8 +6207,6 @@
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return 0;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return 0;
if (!locally_generated)
p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
@@ -5459,8 +6234,6 @@
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return;
if (!locally_generated)
p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
@@ -5537,10 +6310,13 @@
u8 reg_class, channel;
int ret;
unsigned int r;
+ u8 channel_forced;
+
if (wpa_s->conf->p2p_listen_reg_class &&
wpa_s->conf->p2p_listen_channel) {
reg_class = wpa_s->conf->p2p_listen_reg_class;
channel = wpa_s->conf->p2p_listen_channel;
+ channel_forced = 1;
} else {
reg_class = 81;
/*
@@ -5549,8 +6325,10 @@
*/
os_get_random((u8 *) &r, sizeof(r));
channel = 1 + (r % 3) * 5;
+ channel_forced = 0;
}
- ret = p2p_set_listen_channel(p2p, reg_class, channel);
+ ret = p2p_set_listen_channel(p2p, reg_class, channel,
+ channel_forced);
if (ret)
wpa_printf(MSG_ERROR, "P2P: Own listen channel update "
"failed: %d", ret);
@@ -5587,7 +6365,15 @@
wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
"update failed");
}
+
+ if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: No GO channel list "
+ "update failed");
+ }
}
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PASSPHRASE_LEN)
+ p2p_set_passphrase_len(p2p, wpa_s->conf->p2p_passphrase_len);
}
@@ -5605,8 +6391,6 @@
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return -1;
wpa_s->global->cross_connection = enabled;
p2p_set_cross_connect(wpa_s->global->p2p, enabled);
@@ -5756,21 +6540,52 @@
}
+void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpas_p2p_notif_pbc_overlap(wpa_s);
+}
+
+
void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
{
- struct p2p_channels chan;
+ struct p2p_channels chan, cli_chan;
+ struct wpa_supplicant *ifs;
if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
return;
os_memset(&chan, 0, sizeof(chan));
- if (wpas_p2p_setup_channels(wpa_s, &chan)) {
+ os_memset(&cli_chan, 0, sizeof(cli_chan));
+ if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) {
wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
"channel list");
return;
}
- p2p_update_channel_list(wpa_s->global->p2p, &chan);
+ p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
+
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ int freq;
+ if (!ifs->current_ssid ||
+ !ifs->current_ssid->p2p_group ||
+ (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
+ ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
+ continue;
+ freq = ifs->current_ssid->frequency;
+ if (freq_included(&chan, freq)) {
+ wpa_dbg(ifs, MSG_DEBUG,
+ "P2P GO operating frequency %d MHz in valid range",
+ freq);
+ continue;
+ }
+
+ wpa_dbg(ifs, MSG_DEBUG,
+ "P2P GO operating in invalid frequency %d MHz", freq);
+ /* TODO: Consider using CSA or removing the group within
+ * wpa_supplicant */
+ wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+ }
}
@@ -5836,6 +6651,11 @@
wpas_p2p_group_delete(wpa_s,
P2P_GROUP_REMOVAL_REQUESTED);
break;
+ } else if (wpa_s->p2p_in_invitation) {
+ wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling",
+ wpa_s->ifname);
+ found = 1;
+ wpas_p2p_group_formation_failed(wpa_s);
}
}
@@ -5863,7 +6683,7 @@
int freq_24, int freq_5, int freq_overall)
{
struct p2p_data *p2p = wpa_s->global->p2p;
- if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+ if (p2p == NULL)
return;
p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
}
@@ -5874,7 +6694,7 @@
u8 peer[ETH_ALEN];
struct p2p_data *p2p = wpa_s->global->p2p;
- if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+ if (p2p == NULL)
return -1;
if (hwaddr_aton(addr, peer))
@@ -5931,10 +6751,10 @@
}
if (!ret && wpa_s->global->p2p_go_wait_client.sec) {
- struct os_time now;
- os_get_time(&now);
- if (now.sec > wpa_s->global->p2p_go_wait_client.sec +
- P2P_MAX_INITIAL_CONN_WAIT_GO) {
+ struct os_reltime now;
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &wpa_s->global->p2p_go_wait_client,
+ P2P_MAX_INITIAL_CONN_WAIT_GO)) {
/* Wait for the first client has expired */
wpa_s->global->p2p_go_wait_client.sec = 0;
} else {
@@ -6007,6 +6827,26 @@
void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
const u8 *addr)
{
+ if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL) > 0) {
+ /*
+ * This can happen if WPS provisioning step is not terminated
+ * cleanly (e.g., P2P Client does not send WSC_Done). Since the
+ * peer was able to connect, there is no need to time out group
+ * formation after this, though. In addition, this is used with
+ * the initial connection wait on the GO as a separate formation
+ * timeout and as such, expected to be hit after the initial WPS
+ * provisioning step.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection");
+ }
+ if (!wpa_s->p2p_go_group_formation_completed) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection");
+ wpa_s->p2p_go_group_formation_completed = 1;
+ wpa_s->global->p2p_group_formation = NULL;
+ wpa_s->p2p_in_provisioning = 0;
+ wpa_s->p2p_in_invitation = 0;
+ }
wpa_s->global->p2p_go_wait_client.sec = 0;
if (addr == NULL)
return;
@@ -6030,7 +6870,8 @@
0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
- wpa_s->p2p_go_ht40);
+ wpa_s->p2p_go_ht40,
+ wpa_s->p2p_go_vht);
}
@@ -6053,35 +6894,24 @@
unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
{
- const char *rn, *rn2;
struct wpa_supplicant *ifs;
if (wpa_s->wpa_state > WPA_SCANNING) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to "
"concurrent operation",
- P2P_CONCURRENT_SEARCH_DELAY);
- return P2P_CONCURRENT_SEARCH_DELAY;
+ wpa_s->conf->p2p_search_delay);
+ return wpa_s->conf->p2p_search_delay;
}
- if (!wpa_s->driver->get_radio_name)
- return 0;
- rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
- if (rn == NULL || rn[0] == '\0')
- return 0;
-
- for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
- if (ifs == wpa_s || !ifs->driver->get_radio_name)
- continue;
-
- rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
- if (!rn2 || os_strcmp(rn, rn2) != 0)
- continue;
- if (ifs->wpa_state > WPA_SCANNING) {
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (ifs != wpa_s && ifs->wpa_state > WPA_SCANNING) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search "
"delay due to concurrent operation on "
"interface %s",
- P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname);
- return P2P_CONCURRENT_SEARCH_DELAY;
+ wpa_s->conf->p2p_search_delay,
+ ifs->ifname);
+ return wpa_s->conf->p2p_search_delay;
}
}
@@ -6089,30 +6919,6 @@
}
-void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s)
-{
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Station mode scan operation not "
- "pending anymore (sta_scan_pending=%d "
- "p2p_cb_on_scan_complete=%d)", wpa_s->sta_scan_pending,
- wpa_s->global->p2p_cb_on_scan_complete);
- wpa_s->sta_scan_pending = 0;
-
- if (!wpa_s->global->p2p_cb_on_scan_complete)
- return;
- wpa_s->global->p2p_cb_on_scan_complete = 0;
-
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return;
-
- if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
- "continued after successful connection");
- p2p_increase_search_delay(wpa_s->global->p2p,
- wpas_p2p_search_delay(wpa_s));
- }
-}
-
-
static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s,
struct wpa_ssid *s, const u8 *addr,
int iface_addr)
@@ -6146,7 +6952,7 @@
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct wpa_ssid *persistent;
- struct psk_list_entry *p;
+ struct psk_list_entry *p, *last;
if (psk_len != sizeof(p->psk))
return;
@@ -6206,10 +7012,9 @@
}
os_memcpy(p->psk, psk, psk_len);
- if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS) {
- struct psk_list_entry *last;
- last = dl_list_last(&persistent->psk_list,
- struct psk_list_entry, list);
+ if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS &&
+ (last = dl_list_last(&persistent->psk_list,
+ struct psk_list_entry, list))) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for "
MACSTR " (p2p=%u) to make room for a new one",
MAC2STR(last->addr), last->p2p);
@@ -6229,11 +7034,9 @@
}
dl_list_add(&persistent->psk_list, &p->list);
-#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->parent->conf->update_config &&
wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
}
@@ -6244,14 +7047,10 @@
int res;
res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr);
- if (res > 0) {
-#ifndef CONFIG_NO_CONFIG_WRITE
- if (wpa_s->conf->update_config &&
- wpa_config_write(wpa_s->confname, wpa_s->conf))
- wpa_dbg(wpa_s, MSG_DEBUG,
- "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
- }
+ if (res > 0 && wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf))
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Failed to update configuration");
}
@@ -6336,6 +7135,63 @@
}
+static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group");
+ wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT);
+}
+
+
+int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq,
+ struct wpa_ssid *ssid)
+{
+ struct wpa_supplicant *iface;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+ if (!iface->current_ssid ||
+ iface->current_ssid->frequency == freq ||
+ (iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
+ !iface->current_ssid->p2p_group))
+ continue;
+
+ /* Remove the connection with least priority */
+ if (!wpas_is_p2p_prioritized(iface)) {
+ /* STA connection has priority over existing
+ * P2P connection, so remove the interface. */
+ wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to single channel concurrent mode frequency conflict");
+ eloop_register_timeout(0, 0,
+ wpas_p2p_group_freq_conflict,
+ iface, NULL);
+ /* If connection in progress is P2P connection, do not
+ * proceed for the connection. */
+ if (wpa_s == iface)
+ return -1;
+ else
+ return 0;
+ } else {
+ /* P2P connection has priority, disable the STA network
+ */
+ wpa_supplicant_disable_network(wpa_s->global->ifaces,
+ ssid);
+ wpa_msg(wpa_s->global->ifaces, MSG_INFO,
+ WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id);
+ os_memset(wpa_s->global->ifaces->pending_bssid, 0,
+ ETH_ALEN);
+ /* If P2P connection is in progress, continue
+ * connecting...*/
+ if (wpa_s == iface)
+ return 0;
+ else
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
@@ -6383,72 +7239,655 @@
return 0;
}
-#ifdef ANDROID_P2P
-static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx)
-{
- struct wpa_supplicant *wpa_s = eloop_ctx;
- wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group");
- wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT);
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
+ struct wpabuf *p2p)
+{
+ struct wpabuf *ret;
+ size_t wsc_len;
+
+ if (p2p == NULL) {
+ wpabuf_free(wsc);
+ wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover");
+ return NULL;
+ }
+
+ wsc_len = wsc ? wpabuf_len(wsc) : 0;
+ ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p));
+ if (ret == NULL) {
+ wpabuf_free(wsc);
+ wpabuf_free(p2p);
+ return NULL;
+ }
+
+ wpabuf_put_be16(ret, wsc_len);
+ if (wsc)
+ wpabuf_put_buf(ret, wsc);
+ wpabuf_put_be16(ret, wpabuf_len(p2p));
+ wpabuf_put_buf(ret, p2p);
+
+ wpabuf_free(wsc);
+ wpabuf_free(p2p);
+ wpa_hexdump_buf(MSG_DEBUG,
+ "P2P: Generated NFC connection handover message", ret);
+
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_p2p(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request");
+ return NULL;
+ }
+ ret = tmp;
+ }
+
+ return ret;
}
-int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq,
- struct wpa_ssid *ssid)
+
+static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid **ssid, u8 *go_dev_addr)
{
- struct wpa_supplicant *iface = NULL;
- struct p2p_data *p2p = wpa_s->global->p2p;
+ struct wpa_supplicant *iface;
+ if (go_dev_addr)
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ if (ssid)
+ *ssid = NULL;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
- if ((iface->current_ssid) &&
- (iface->current_ssid->frequency != freq) &&
- ((iface->p2p_group_interface) ||
- (iface->current_ssid->p2p_group))) {
-
- if ((iface->p2p_group_interface == P2P_GROUP_INTERFACE_GO) ||
- (iface->current_ssid->mode == WPAS_MODE_P2P_GO)) {
- /* Try to see whether we can move the GO. If it
- * is not possible, remove the GO interface
- */
- if (wpa_drv_switch_channel(iface, freq) == 0) {
- wpa_printf(MSG_ERROR, "P2P: GO Moved to freq(%d)", freq);
- iface->current_ssid->frequency = freq;
- continue;
- }
- }
-
- /* If GO cannot be moved or if the conflicting interface is a
- * P2P Client, remove the interface depending up on the connection
- * priority */
- if(!wpas_is_p2p_prioritized(iface)) {
- /* STA connection has priority over existing
- * P2P connection. So remove the interface */
- wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to Single channel"
- "concurrent mode frequency conflict");
- eloop_register_timeout(0, 0, wpas_p2p_group_freq_conflict,
- iface, NULL);
- /* If connection in progress is p2p connection, do not proceed for the connection */
- if (wpa_s == iface)
- return -1;
- else
- /* If connection in progress is STA connection, proceed for the connection */
- return 0;
- } else {
- /* P2p connection has priority, disable the STA network*/
- wpa_supplicant_disable_network(wpa_s->global->ifaces, ssid);
- wpa_msg(wpa_s->global->ifaces, MSG_INFO, WPA_EVENT_FREQ_CONFLICT
- " id=%d", ssid->id);
- os_memset(wpa_s->global->ifaces->pending_bssid, 0, ETH_ALEN);
- if (wpa_s == iface) {
- /* p2p connection is in progress, continue connecting...*/
- return 0;
- }
- else {
- /* STA connection is in progress, do not allow to continue */
- return -1;
- }
- }
- }
+ if (iface->wpa_state < WPA_ASSOCIATING ||
+ iface->current_ssid == NULL || iface->assoc_freq == 0 ||
+ !iface->current_ssid->p2p_group ||
+ iface->current_ssid->mode != WPAS_MODE_INFRA)
+ continue;
+ if (ssid)
+ *ssid = iface->current_ssid;
+ if (go_dev_addr)
+ os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN);
+ return iface->assoc_freq;
}
return 0;
}
-#endif
+
+
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+ int ndef)
+{
+ struct wpabuf *wsc, *p2p;
+ struct wpa_ssid *ssid;
+ u8 go_dev_addr[ETH_ALEN];
+ int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request");
+ return NULL;
+ }
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+ wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request");
+ return NULL;
+ }
+
+ if (cli_freq == 0) {
+ wsc = wps_build_nfc_handover_req_p2p(
+ wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
+ } else
+ wsc = NULL;
+ p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq,
+ go_dev_addr, ssid ? ssid->ssid : NULL,
+ ssid ? ssid->ssid_len : 0);
+
+ return wpas_p2p_nfc_handover(ndef, wsc, p2p);
+}
+
+
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef, int tag)
+{
+ struct wpabuf *wsc, *p2p;
+ struct wpa_ssid *ssid;
+ u8 go_dev_addr[ETH_ALEN];
+ int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return NULL;
+
+ if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+ wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+ return NULL;
+
+ if (cli_freq == 0) {
+ wsc = wps_build_nfc_handover_sel_p2p(
+ wpa_s->parent->wps,
+ tag ? wpa_s->conf->wps_nfc_dev_pw_id :
+ DEV_PW_NFC_CONNECTION_HANDOVER,
+ wpa_s->conf->wps_nfc_dh_pubkey,
+ tag ? wpa_s->conf->wps_nfc_dev_pw : NULL);
+ } else
+ wsc = NULL;
+ p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq,
+ go_dev_addr, ssid ? ssid->ssid : NULL,
+ ssid ? ssid->ssid_len : 0);
+
+ return wpas_p2p_nfc_handover(ndef, wsc, p2p);
+}
+
+
+static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
+ struct p2p_nfc_params *params)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC "
+ "connection handover (freq=%d)",
+ params->go_freq);
+
+ if (params->go_freq && params->go_ssid_len) {
+ wpa_s->p2p_wps_method = WPS_NFC;
+ wpa_s->pending_join_wps_method = WPS_NFC;
+ os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN);
+ os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr,
+ ETH_ALEN);
+ return wpas_p2p_join_start(wpa_s, params->go_freq,
+ params->go_ssid,
+ params->go_ssid_len);
+ }
+
+ return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+ WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
+ params->go_freq, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
+ struct p2p_nfc_params *params, int tag)
+{
+ int res, persistent;
+ struct wpa_ssid *ssid;
+
+ wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC "
+ "connection handover");
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL)
+ continue;
+ if (ssid->mode != WPAS_MODE_P2P_GO)
+ continue;
+ if (wpa_s->ap_iface == NULL)
+ continue;
+ break;
+ }
+ if (wpa_s == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface");
+ return -1;
+ }
+
+ if (wpa_s->parent->p2p_oob_dev_pw_id !=
+ DEV_PW_NFC_CONNECTION_HANDOVER &&
+ !wpa_s->parent->p2p_oob_dev_pw) {
+ wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+ return -1;
+ }
+ res = wpas_ap_wps_add_nfc_pw(
+ wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+ wpa_s->parent->p2p_oob_dev_pw,
+ wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+ wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+ if (res)
+ return res;
+
+ if (!tag) {
+ wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation");
+ return 0;
+ }
+
+ if (!params->peer ||
+ !(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE))
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR
+ " to join", MAC2STR(params->peer->p2p_device_addr));
+
+ wpa_s->global->p2p_invite_group = wpa_s;
+ persistent = ssid->p2p_persistent_group &&
+ wpas_p2p_get_persistent(wpa_s->parent,
+ params->peer->p2p_device_addr,
+ ssid->ssid, ssid->ssid_len);
+ wpa_s->parent->pending_invite_ssid_id = -1;
+
+ return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr,
+ P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
+ ssid->ssid, ssid->ssid_len, ssid->frequency,
+ wpa_s->global->p2p_dev_addr, persistent, 0,
+ wpa_s->parent->p2p_oob_dev_pw_id);
+}
+
+
+static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
+ struct p2p_nfc_params *params,
+ int forced_freq)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC "
+ "connection handover");
+ return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+ WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
+ forced_freq, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
+ struct p2p_nfc_params *params,
+ int forced_freq)
+{
+ int res;
+
+ wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC "
+ "connection handover");
+ res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+ WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
+ forced_freq, -1, 0, 1, 1);
+ if (res)
+ return res;
+
+ res = wpas_p2p_listen(wpa_s, 60);
+ if (res) {
+ p2p_unauthorize(wpa_s->global->p2p,
+ params->peer->p2p_device_addr);
+ }
+
+ return res;
+}
+
+
+static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data,
+ int sel, int tag, int forced_freq)
+{
+ const u8 *pos, *end;
+ u16 len, id;
+ struct p2p_nfc_params params;
+ int res;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.sel = sel;
+
+ wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data);
+
+ pos = wpabuf_head(data);
+ end = pos + wpabuf_len(data);
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC "
+ "attributes");
+ return -1;
+ }
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + len > end) {
+ wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC "
+ "attributes");
+ return -1;
+ }
+ params.wsc_attr = pos;
+ params.wsc_len = len;
+ pos += len;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P "
+ "attributes");
+ return -1;
+ }
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + len > end) {
+ wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P "
+ "attributes");
+ return -1;
+ }
+ params.p2p_attr = pos;
+ params.p2p_len = len;
+ pos += len;
+
+ wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes",
+ params.wsc_attr, params.wsc_len);
+ wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes",
+ params.p2p_attr, params.p2p_len);
+ if (pos < end) {
+ wpa_hexdump(MSG_DEBUG,
+ "P2P: Ignored extra data after P2P attributes",
+ pos, end - pos);
+ }
+
+ res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, ¶ms);
+ if (res)
+ return res;
+
+ if (params.next_step == NO_ACTION)
+ return 0;
+
+ if (params.next_step == BOTH_GO) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR,
+ MAC2STR(params.peer->p2p_device_addr));
+ return 0;
+ }
+
+ if (params.next_step == PEER_CLIENT) {
+ if (!is_zero_ether_addr(params.go_dev_addr)) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+ "peer=" MACSTR " freq=%d go_dev_addr=" MACSTR
+ " ssid=\"%s\"",
+ MAC2STR(params.peer->p2p_device_addr),
+ params.go_freq,
+ MAC2STR(params.go_dev_addr),
+ wpa_ssid_txt(params.go_ssid,
+ params.go_ssid_len));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+ "peer=" MACSTR " freq=%d",
+ MAC2STR(params.peer->p2p_device_addr),
+ params.go_freq);
+ }
+ return 0;
+ }
+
+ if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer="
+ MACSTR, MAC2STR(params.peer->p2p_device_addr));
+ return 0;
+ }
+
+ wpabuf_free(wpa_s->p2p_oob_dev_pw);
+ wpa_s->p2p_oob_dev_pw = NULL;
+
+ if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+ wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw "
+ "received");
+ return -1;
+ }
+
+ id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id);
+ wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash",
+ params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+ os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash,
+ params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_s->p2p_peer_oob_pk_hash_known = 1;
+
+ if (tag) {
+ if (id < 0x10) {
+ wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
+ "peer OOB Device Password Id %u", id);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB "
+ "Device Password Id %u", id);
+ wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password",
+ params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+ params.oob_dev_pw_len -
+ WPS_OOB_PUBKEY_HASH_LEN - 2);
+ wpa_s->p2p_oob_dev_pw_id = id;
+ wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy(
+ params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+ params.oob_dev_pw_len -
+ WPS_OOB_PUBKEY_HASH_LEN - 2);
+ if (wpa_s->p2p_oob_dev_pw == NULL)
+ return -1;
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+ wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake "
+ "without Device Password");
+ wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+ }
+
+ switch (params.next_step) {
+ case NO_ACTION:
+ case BOTH_GO:
+ case PEER_CLIENT:
+ /* already covered above */
+ return 0;
+ case JOIN_GROUP:
+ return wpas_p2p_nfc_join_group(wpa_s, ¶ms);
+ case AUTH_JOIN:
+ return wpas_p2p_nfc_auth_join(wpa_s, ¶ms, tag);
+ case INIT_GO_NEG:
+ return wpas_p2p_nfc_init_go_neg(wpa_s, ¶ms, forced_freq);
+ case RESP_GO_NEG:
+ /* TODO: use own OOB Dev Pw */
+ return wpas_p2p_nfc_resp_go_neg(wpa_s, ¶ms, forced_freq);
+ }
+
+ return -1;
+}
+
+
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data, int forced_freq)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq);
+}
+
+
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+ const struct wpabuf *req,
+ const struct wpabuf *sel, int forced_freq)
+{
+ struct wpabuf *tmp;
+ int ret;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported");
+
+ wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req",
+ wpabuf_head(req), wpabuf_len(req));
+ wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel",
+ wpabuf_head(sel), wpabuf_len(sel));
+ if (forced_freq)
+ wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq);
+ tmp = ndef_parse_p2p(init ? sel : req);
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF");
+ return -1;
+ }
+
+ ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0,
+ forced_freq);
+ wpabuf_free(tmp);
+
+ return ret;
+}
+
+
+int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
+{
+ const u8 *if_addr;
+ int go_intent = wpa_s->conf->p2p_go_intent;
+ struct wpa_supplicant *iface;
+
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag");
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+ {
+ if (!iface->ap_iface)
+ continue;
+ hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]);
+ }
+ p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0,
+ 0, NULL);
+ if (wpa_s->p2p_nfc_tag_enabled)
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ wpa_s->p2p_nfc_tag_enabled = 0;
+ return 0;
+ }
+
+ if (wpa_s->global->p2p_disabled)
+ return -1;
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+ wpa_s->conf->wps_nfc_dh_privkey == NULL ||
+ wpa_s->conf->wps_nfc_dev_pw == NULL ||
+ wpa_s->conf->wps_nfc_dev_pw_id < 0x10) {
+ wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured "
+ "to allow static handover cases");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag");
+
+ wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+ wpabuf_free(wpa_s->p2p_oob_dev_pw);
+ wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+ if (wpa_s->p2p_oob_dev_pw == NULL)
+ return -1;
+ wpa_s->p2p_peer_oob_pk_hash_known = 0;
+
+ if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
+ wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) {
+ /*
+ * P2P Group Interface present and the command came on group
+ * interface, so enable the token for the current interface.
+ */
+ wpa_s->create_p2p_iface = 0;
+ } else {
+ wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+ }
+
+ if (wpa_s->create_p2p_iface) {
+ enum wpa_driver_if_type iftype;
+ /* Prepare to add a new interface for the group */
+ iftype = WPA_IF_P2P_GROUP;
+ if (go_intent == 15)
+ iftype = WPA_IF_P2P_GO;
+ if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+ "interface for the group");
+ return -1;
+ }
+
+ if_addr = wpa_s->pending_interface_addr;
+ } else
+ if_addr = wpa_s->own_addr;
+
+ wpa_s->p2p_nfc_tag_enabled = enabled;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+ struct hostapd_data *hapd;
+ if (iface->ap_iface == NULL)
+ continue;
+ hapd = iface->ap_iface->bss[0];
+ wpabuf_free(hapd->conf->wps_nfc_dh_pubkey);
+ hapd->conf->wps_nfc_dh_pubkey =
+ wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+ wpabuf_free(hapd->conf->wps_nfc_dh_privkey);
+ hapd->conf->wps_nfc_dh_privkey =
+ wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+ wpabuf_free(hapd->conf->wps_nfc_dev_pw);
+ hapd->conf->wps_nfc_dev_pw =
+ wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+ hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+
+ if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) {
+ wpa_dbg(iface, MSG_DEBUG,
+ "P2P: Failed to enable NFC Tag for GO");
+ }
+ }
+ p2p_set_authorized_oob_dev_pw_id(
+ wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent,
+ if_addr);
+
+ return 0;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs,
+ unsigned int num)
+{
+ u8 curr_chan, cand, chan;
+ unsigned int i;
+
+ curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
+ for (i = 0, cand = 0; i < num; i++) {
+ ieee80211_freq_to_chan(freqs[i].freq, &chan);
+ if (curr_chan == chan) {
+ cand = 0;
+ break;
+ }
+
+ if (chan == 1 || chan == 6 || chan == 11)
+ cand = chan;
+ }
+
+ if (cand) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Update Listen channel to %u baased on operating channel",
+ cand);
+ p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0);
+ }
+}
+
+
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_used_freq_data *freqs;
+ unsigned int num = wpa_s->num_multichan_concurrent;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+
+ /*
+ * If possible, optimize the Listen channel to be a channel that is
+ * already used by one of the other interfaces.
+ */
+ if (!wpa_s->conf->p2p_optimize_listen_chan)
+ return;
+
+ if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+ return;
+
+ freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+ if (!freqs)
+ return;
+
+ num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
+ wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+ os_free(freqs);
+}
+
+
+void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s == wpa_s->parent)
+ wpas_p2p_group_remove(wpa_s, "*");
+ if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
+ "the management interface is being removed");
+ wpas_p2p_deinit_global(wpa_s->global);
+ }
+}
+
+
+void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->ap_iface->bss)
+ wpa_s->ap_iface->bss[0]->p2p_group = NULL;
+ wpas_p2p_group_deinit(wpa_s);
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 65ccbf9..d79ec03 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -16,34 +16,28 @@
struct p2p_channels;
struct wps_event_fail;
-int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
-void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
-void wpas_p2p_deinit_global(struct wpa_global *global);
int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s);
+struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
+ const u8 *ssid, size_t ssid_len);
+struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
+ const u8 *peer_dev_addr);
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join,
int auth, int go_intent, int freq, int persistent_id,
- int pd, int ht40);
-void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
- unsigned int freq, unsigned int duration);
-void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
- unsigned int freq);
-#ifdef ANDROID_P2P
+ int pd, int ht40, int vht);
int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
int freq, struct wpa_ssid *ssid);
-#endif
int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
- int freq, int ht40);
+ int freq, int ht40, int vht);
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
- int freq, int ht40,
- const struct p2p_channels *channels);
+ int force_freq, int neg_freq, int ht40,
+ int vht, const struct p2p_channels *channels,
+ int connection_timeout);
struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
-void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
- int registrar);
enum wpas_p2p_prov_disc_use {
WPAS_P2P_PD_FOR_GO_NEG,
WPAS_P2P_PD_FOR_JOIN,
@@ -64,33 +58,11 @@
const u8 *dev_id, unsigned int search_delay);
void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
+int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout);
int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
u8 *buf, size_t len, int p2p_group);
-int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
- const u8 *dst, const u8 *bssid,
- const u8 *ie, size_t ie_len,
- int ssi_signal);
-void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
- const u8 *sa, const u8 *bssid,
- u8 category, const u8 *data, size_t len, int freq);
void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
-void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
-void wpas_dev_found(void *ctx, const u8 *addr,
- const struct p2p_peer_info *info,
- int new_device);
void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s);
-void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res);
-void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id);
-void wpas_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);
-void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods);
-void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
- u16 update_indic, const u8 *tlvs, size_t tlvs_len);
-void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
- const u8 *tlvs, size_t tlvs_len);
u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
const struct wpabuf *tlvs);
u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
@@ -114,10 +86,9 @@
int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
- int ht40, int pref_freq);
+ int ht40, int vht, int pref_freq);
int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
const u8 *peer_addr, const u8 *go_dev_addr);
-void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
u32 interval1, u32 duration2, u32 interval2);
int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
@@ -128,25 +99,12 @@
void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
u16 reason_code, const u8 *ie, size_t ie_len,
int locally_generated);
-void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
int duration);
int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled);
-void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
-void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
-int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
-void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
-void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
- int freq_24, int freq_5, int freq_overall);
int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
-void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
- struct wps_event_fail *fail);
-int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
-void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid);
struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
const u8 *addr, const u8 *ssid,
size_t ssid_len);
@@ -155,20 +113,143 @@
int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel);
+int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel);
unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
const u8 *p2p_dev_addr,
const u8 *psk, size_t psk_len);
void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
int iface_addr);
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+ int ndef);
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef, int tag);
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data, int forced_freq);
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+ const struct wpabuf *req,
+ const struct wpabuf *sel, int forced_freq);
+int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled);
+void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx);
#ifdef CONFIG_P2P
-void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s);
+
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal);
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ int registrar);
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+ int freq_24, int freq_5, int freq_overall);
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+ const u8 *sa, const u8 *bssid,
+ u8 category, const u8 *data, size_t len, int freq);
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration);
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s);
+void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
+int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s);
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+
#else /* CONFIG_P2P */
-static inline void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s)
+
+static inline int
+wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
{
+ return 0;
+}
+
+static inline void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s,
+ const u8 *addr,
+ const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal)
+{
+ return 0;
+}
+
+static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int registrar)
+{
+}
+
+static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+ int freq_24, int freq_5,
+ int freq_overall)
+{
+}
+
+static inline void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s,
+ const u8 *da,
+ const u8 *sa, const u8 *bssid,
+ u8 category, const u8 *data, size_t len,
+ int freq)
+{
+}
+
+static inline void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq,
+ unsigned int duration)
+{
+}
+
+static inline void
+wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+}
+
+static inline void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+ return 0;
}
static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
@@ -179,6 +260,39 @@
static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
{
}
+
+static inline void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+}
+
+static inline int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+}
+
#endif /* CONFIG_P2P */
#endif /* P2P_SUPPLICANT_H */
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index ff2ae74..ed57085 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -27,9 +27,6 @@
#include "drivers/driver.h"
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
struct wpa_driver_ops *wpa_drivers[] = { NULL };
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index a0f51d0..40eb8d8 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - Scanning
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -141,71 +141,43 @@
}
-static int int_array_len(const int *a)
+static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
{
- int i;
- for (i = 0; a && a[i]; i++)
- ;
- return i;
-}
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct wpa_driver_scan_params *params = work->ctx;
+ int ret;
-
-static 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;
-}
-
-
-static 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;
+ if (deinit) {
+ if (!work->started) {
+ wpa_scan_free_params(params);
+ return;
}
- a[++i] = a[j++];
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+ wpas_notify_scan_done(wpa_s, 0);
+ wpa_s->scan_work = NULL;
+ return;
}
- if (a[i])
- i++;
- a[i] = 0;
+
+ wpa_supplicant_notify_scanning(wpa_s, 1);
+
+ if (wpa_s->clear_driver_scan_cache)
+ params->only_new_results = 1;
+ ret = wpa_drv_scan(wpa_s, params);
+ wpa_scan_free_params(params);
+ work->ctx = NULL;
+ if (ret) {
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+ wpas_notify_scan_done(wpa_s, 0);
+ radio_work_done(work);
+ return;
+ }
+
+ os_get_reltime(&wpa_s->scan_trigger_time);
+ wpa_s->scan_runs++;
+ wpa_s->normal_scans++;
+ wpa_s->own_scan_requested = 1;
+ wpa_s->clear_driver_scan_cache = 0;
+ wpa_s->scan_work = work;
}
@@ -218,21 +190,24 @@
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
- int ret;
+ struct wpa_driver_scan_params *ctx;
- wpa_supplicant_notify_scanning(wpa_s, 1);
-
- ret = wpa_drv_scan(wpa_s, params);
- if (ret) {
- wpa_supplicant_notify_scanning(wpa_s, 0);
- wpas_notify_scan_done(wpa_s, 0);
- } else {
- os_get_time(&wpa_s->scan_trigger_time);
- wpa_s->scan_runs++;
- wpa_s->normal_scans++;
+ if (wpa_s->scan_work) {
+ wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
+ return -1;
}
- return ret;
+ ctx = wpa_scan_clone_params(params);
+ if (ctx == NULL)
+ return -1;
+
+ if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
+ {
+ wpa_scan_free_params(ctx);
+ return -1;
+ }
+
+ return 0;
}
@@ -260,10 +235,9 @@
}
-static int
-wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params,
- int interval)
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ int interval)
{
int ret;
@@ -271,15 +245,14 @@
ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
if (ret)
wpa_supplicant_notify_scanning(wpa_s, 0);
- else {
+ else
wpa_s->sched_scanning = 1;
- }
return ret;
}
-static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
{
int ret;
@@ -353,6 +326,32 @@
}
wpa_s->p2p_in_provisioning++;
}
+
+ if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
+ /*
+ * Optimize scan based on GO information during persistent
+ * group reinvocation
+ */
+ if (wpa_s->p2p_in_invitation < 5 &&
+ wpa_s->p2p_invite_go_freq > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
+ wpa_s->p2p_invite_go_freq);
+ params->freqs = os_zalloc(2 * sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->p2p_invite_go_freq;
+ }
+ wpa_s->p2p_in_invitation++;
+ if (wpa_s->p2p_in_invitation > 20) {
+ /*
+ * This should not really happen since the variable is
+ * cleared on group removal, but if it does happen, make
+ * sure we do not get stuck in special invitation scan
+ * mode.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation");
+ wpa_s->p2p_in_invitation = 0;
+ }
+ }
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS
@@ -367,7 +366,8 @@
if (params->freqs)
params->freqs[0] = wpa_s->wps_freq;
wpa_s->after_wps--;
- }
+ } else if (wpa_s->after_wps)
+ wpa_s->after_wps--;
if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq)
{
@@ -391,11 +391,17 @@
return;
wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
- wpabuf_put_u8(buf, 4);
+ wpabuf_put_u8(buf, 6);
wpabuf_put_u8(buf, 0x00);
wpabuf_put_u8(buf, 0x00);
wpabuf_put_u8(buf, 0x00);
wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */
+ wpabuf_put_u8(buf, 0x00);
+#ifdef CONFIG_HS20
+ wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */
+#else /* CONFIG_HS20 */
+ wpabuf_put_u8(buf, 0x00);
+#endif /* CONFIG_HS20 */
wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
@@ -451,7 +457,7 @@
#ifdef CONFIG_HS20
if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0)
- wpas_hs20_add_indication(extra_ie);
+ wpas_hs20_add_indication(extra_ie, -1);
#endif /* CONFIG_HS20 */
return extra_ie;
@@ -542,11 +548,51 @@
}
+static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ size_t max_ssids)
+{
+ unsigned int i;
+ struct wpa_ssid *ssid;
+
+ for (i = 0; i < wpa_s->scan_id_count; i++) {
+ unsigned int j;
+
+ ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
+ if (!ssid || !ssid->scan_ssid)
+ continue;
+
+ for (j = 0; j < params->num_ssids; j++) {
+ if (params->ssids[j].ssid_len == ssid->ssid_len &&
+ params->ssids[j].ssid &&
+ os_memcmp(params->ssids[j].ssid, ssid->ssid,
+ ssid->ssid_len) == 0)
+ break;
+ }
+ if (j < params->num_ssids)
+ continue; /* already in the list */
+
+ if (params->num_ssids + 1 > max_ssids) {
+ wpa_printf(MSG_DEBUG,
+ "Over max scan SSIDs for manual request");
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ params->ssids[params->num_ssids].ssid = ssid->ssid;
+ params->ssids[params->num_ssids].ssid_len = ssid->ssid_len;
+ params->num_ssids++;
+ }
+
+ wpa_s->scan_id_count = 0;
+}
+
+
static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_ssid *ssid;
- enum scan_req_type scan_req = NORMAL_SCAN_REQ;
int ret;
struct wpabuf *extra_ie = NULL;
struct wpa_driver_scan_params params;
@@ -554,30 +600,36 @@
size_t max_ssids;
enum wpa_states prev_state;
+ if (wpa_s->pno || wpa_s->pno_sched_pending) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress");
+ return;
+ }
+
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
- wpas_p2p_continue_after_scan(wpa_s);
return;
}
if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
- wpas_p2p_continue_after_scan(wpa_s);
return;
}
-#ifdef ANDROID
+
if (wpa_s->scanning) {
- /* If we are already in scanning state, we shall ignore this new scan request*/
- wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - already scanning");
+ /*
+ * If we are already in scanning state, we shall reschedule the
+ * the incoming scan request.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req");
+ wpa_supplicant_req_scan(wpa_s, 1, 0);
return;
}
-#endif
+
if (!wpa_supplicant_enabled_networks(wpa_s) &&
wpa_s->scan_req == NORMAL_SCAN_REQ) {
wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
- wpas_p2p_continue_after_scan(wpa_s);
return;
}
@@ -594,22 +646,11 @@
return;
}
-#ifdef CONFIG_P2P
- if (wpas_p2p_in_progress(wpa_s) || wpas_wpa_is_in_progress(wpa_s)) {
- if (wpa_s->sta_scan_pending &&
- wpas_p2p_in_progress(wpa_s) == 2 &&
- wpa_s->global->p2p_cb_on_scan_complete) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station "
- "mode scan during P2P search");
- } else {
- wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan "
- "while P2P operation is in progress");
- wpa_s->sta_scan_pending = 1;
- wpa_supplicant_req_scan(wpa_s, 5, 0);
- return;
- }
+ if (wpas_p2p_in_progress(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
+ wpa_supplicant_req_scan(wpa_s, 5, 0);
+ return;
}
-#endif /* CONFIG_P2P */
if (wpa_s->conf->ap_scan == 2)
max_ssids = 1;
@@ -619,7 +660,7 @@
max_ssids = WPAS_MAX_SCAN_SSIDS;
}
- scan_req = wpa_s->scan_req;
+ wpa_s->last_scan_req = wpa_s->scan_req;
wpa_s->scan_req = NORMAL_SCAN_REQ;
os_memset(¶ms, 0, sizeof(params));
@@ -637,7 +678,8 @@
goto scan;
}
- if (scan_req != MANUAL_SCAN_REQ && wpa_s->connect_without_scan) {
+ if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+ wpa_s->connect_without_scan) {
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid == wpa_s->connect_without_scan)
break;
@@ -654,13 +696,27 @@
#ifdef CONFIG_P2P
if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
wpa_s->go_params) {
- wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during "
- "P2P group formation");
+ wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
+ wpa_s->p2p_in_provisioning,
+ wpa_s->show_group_started);
params.ssids[0].ssid = wpa_s->go_params->ssid;
params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
params.num_ssids = 1;
goto ssid_list_set;
}
+
+ if (wpa_s->p2p_in_invitation) {
+ if (wpa_s->current_ssid) {
+ wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation");
+ params.ssids[0].ssid = wpa_s->current_ssid->ssid;
+ params.ssids[0].ssid_len =
+ wpa_s->current_ssid->ssid_len;
+ params.num_ssids = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation");
+ }
+ goto ssid_list_set;
+ }
#endif /* CONFIG_P2P */
/* Find the starting point from which to continue scanning */
@@ -675,19 +731,48 @@
}
}
- if (scan_req != MANUAL_SCAN_REQ && wpa_s->conf->ap_scan == 2) {
+ if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+ wpa_s->conf->ap_scan == 2) {
wpa_s->connect_without_scan = NULL;
wpa_s->prev_scan_wildcard = 0;
wpa_supplicant_assoc_try(wpa_s, ssid);
return;
-#ifndef ANDROID
} else if (wpa_s->conf->ap_scan == 2) {
/*
* User-initiated scan request in ap_scan == 2; scan with
* wildcard SSID.
*/
ssid = NULL;
-#endif
+ } else if (wpa_s->reattach && wpa_s->current_ssid != NULL) {
+ /*
+ * Perform single-channel single-SSID scan for
+ * reassociate-to-same-BSS operation.
+ */
+ /* Setup SSID */
+ ssid = wpa_s->current_ssid;
+ wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
+ ssid->ssid, ssid->ssid_len);
+ params.ssids[0].ssid = ssid->ssid;
+ params.ssids[0].ssid_len = ssid->ssid_len;
+ params.num_ssids = 1;
+
+ /*
+ * Allocate memory for frequency array, allocate one extra
+ * slot for the zero-terminator.
+ */
+ params.freqs = os_malloc(sizeof(int) * 2);
+ if (params.freqs == NULL) {
+ wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed");
+ return;
+ }
+ params.freqs[0] = wpa_s->assoc_freq;
+ params.freqs[1] = 0;
+
+ /*
+ * Reset the reattach flag so that we fall back to full scan if
+ * this scan fails.
+ */
+ wpa_s->reattach = 0;
} else {
struct wpa_ssid *start = ssid, *tssid;
int freqs_set = 0;
@@ -714,6 +799,10 @@
ssid = wpa_s->conf->ssid;
}
+ if (wpa_s->scan_id_count &&
+ wpa_s->last_scan_req == MANUAL_SCAN_REQ)
+ wpa_set_scan_ssids(wpa_s, ¶ms, max_ssids);
+
for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) {
if (wpas_network_disabled(wpa_s, tssid))
continue;
@@ -755,6 +844,9 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
"the scan request");
params.num_ssids++;
+ } else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_passive && params.num_ssids == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
} else {
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
params.num_ssids++;
@@ -768,6 +860,17 @@
wpa_supplicant_optimize_freqs(wpa_s, ¶ms);
extra_ie = wpa_supplicant_extra_ies(wpa_s);
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_only_new)
+ params.only_new_results = 1;
+
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
+ wpa_s->manual_scan_freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels");
+ params.freqs = wpa_s->manual_scan_freqs;
+ wpa_s->manual_scan_freqs = NULL;
+ }
+
if (params.freqs == NULL && wpa_s->next_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
"generated frequency list");
@@ -810,7 +913,7 @@
}
#ifdef CONFIG_P2P
- if (wpa_s->p2p_in_provisioning ||
+ if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation ||
(wpa_s->show_group_started && wpa_s->go_params)) {
/*
* The interface may not yet be in P2P mode, so we have to
@@ -833,7 +936,8 @@
* station interface when we are not configured to prefer station
* connection and a concurrent operation is already in process.
*/
- if (wpa_s->scan_for_connection && scan_req == NORMAL_SCAN_REQ &&
+ if (wpa_s->scan_for_connection &&
+ wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
!scan_params->freqs && !params.freqs &&
wpas_is_p2p_prioritized(wpa_s) &&
wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
@@ -855,6 +959,13 @@
ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
+ if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
+ !wpa_s->manual_scan_freqs) {
+ /* Restore manual_scan_freqs for the next attempt */
+ wpa_s->manual_scan_freqs = params.freqs;
+ params.freqs = NULL;
+ }
+
wpabuf_free(extra_ie);
os_free(params.freqs);
os_free(params.filter_ssids);
@@ -864,7 +975,7 @@
if (prev_state != wpa_s->wpa_state)
wpa_supplicant_set_state(wpa_s, prev_state);
/* Restore scan_req since we will try to scan again */
- wpa_s->scan_req = scan_req;
+ wpa_s->scan_req = wpa_s->last_scan_req;
wpa_supplicant_req_scan(wpa_s, 1, 0);
} else {
wpa_s->scan_for_connection = 0;
@@ -874,7 +985,7 @@
void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
{
- struct os_time remaining, new_int;
+ struct os_reltime remaining, new_int;
int cancelled;
cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
@@ -882,13 +993,15 @@
new_int.sec = sec;
new_int.usec = 0;
- if (cancelled && os_time_before(&remaining, &new_int)) {
+ if (cancelled && os_reltime_before(&remaining, &new_int)) {
new_int.sec = remaining.sec;
new_int.usec = remaining.usec;
}
- eloop_register_timeout(new_int.sec, new_int.usec, wpa_supplicant_scan,
- wpa_s, NULL);
+ if (cancelled) {
+ eloop_register_timeout(new_int.sec, new_int.usec,
+ wpa_supplicant_scan, wpa_s, NULL);
+ }
wpa_s->scan_interval = sec;
}
@@ -904,34 +1017,19 @@
*/
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
{
-#ifndef ANDROID
- /* If there's at least one network that should be specifically scanned
- * then don't cancel the scan and reschedule. Some drivers do
- * background scanning which generates frequent scan results, and that
- * causes the specific SSID scan to get continually pushed back and
- * never happen, which causes hidden APs to never get probe-scanned.
- */
- if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) &&
- wpa_s->conf->ap_scan == 1) {
- struct wpa_ssid *ssid = wpa_s->conf->ssid;
-
- while (ssid) {
- if (!wpas_network_disabled(wpa_s, ssid) &&
- ssid->scan_ssid)
- break;
- ssid = ssid->next;
- }
- if (ssid) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
- "ensure that specific SSID scans occur");
- return;
- }
+ int res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
+ NULL);
+ if (res == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
+ sec, usec);
+ } else if (res == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner",
+ sec, usec);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",
+ sec, usec);
+ eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
}
-#endif
- wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
- sec, usec);
- eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
- eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
}
@@ -1143,6 +1241,9 @@
params.extra_ies_len = wpabuf_len(extra_ie);
}
+ if (wpa_s->conf->filter_rssi)
+ params.filter_rssi = wpa_s->conf->filter_rssi;
+
scan_params = ¶ms;
scan:
@@ -1203,10 +1304,6 @@
{
wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
- wpas_p2p_continue_after_scan(wpa_s);
-#ifdef ANDROID
- wpa_supplicant_notify_scanning(wpa_s, 0);
-#endif
}
@@ -1345,6 +1442,43 @@
/**
+ * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ *
+ * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
+ u32 vendor_type)
+{
+ const u8 *end, *pos;
+
+ if (res->beacon_ie_len == 0)
+ return NULL;
+
+ pos = (const u8 *) (res + 1);
+ pos += res->ie_len;
+ end = pos + res->beacon_ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+/**
* wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
* @res: Scan result entry
* @vendor_type: Vendor type (four octets starting the IE payload)
@@ -1632,12 +1766,12 @@
* Make sure we have a valid timestamp if the driver wrapper
* does not set this.
*/
- os_get_time(&scan_res->fetch_time);
+ os_get_reltime(&scan_res->fetch_time);
}
filter_scan_res(wpa_s, scan_res);
#ifdef CONFIG_WPS
- if (wpas_wps_in_progress(wpa_s)) {
+ if (wpas_wps_searching(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
"provisioning rules");
compar = wpa_scan_result_wps_compar;
@@ -1689,9 +1823,21 @@
struct wpa_scan_results *scan_res)
{
wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received");
- wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
+ wpa_s->manual_scan_id);
+ wpa_s->manual_scan_use_id = 0;
+ } else {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+ }
wpas_notify_scan_results(wpa_s);
wpas_notify_scan_done(wpa_s, 1);
+ if (wpa_s->scan_work) {
+ struct wpa_radio_work *work = wpa_s->scan_work;
+ wpa_s->scan_work = NULL;
+ radio_work_done(work);
+ }
}
@@ -1699,3 +1845,213 @@
{
return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL);
}
+
+
+struct wpa_driver_scan_params *
+wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
+{
+ struct wpa_driver_scan_params *params;
+ size_t i;
+ u8 *n;
+
+ params = os_zalloc(sizeof(*params));
+ if (params == NULL)
+ return NULL;
+
+ for (i = 0; i < src->num_ssids; i++) {
+ if (src->ssids[i].ssid) {
+ n = os_malloc(src->ssids[i].ssid_len);
+ if (n == NULL)
+ goto failed;
+ os_memcpy(n, src->ssids[i].ssid,
+ src->ssids[i].ssid_len);
+ params->ssids[i].ssid = n;
+ params->ssids[i].ssid_len = src->ssids[i].ssid_len;
+ }
+ }
+ params->num_ssids = src->num_ssids;
+
+ if (src->extra_ies) {
+ n = os_malloc(src->extra_ies_len);
+ if (n == NULL)
+ goto failed;
+ os_memcpy(n, src->extra_ies, src->extra_ies_len);
+ params->extra_ies = n;
+ params->extra_ies_len = src->extra_ies_len;
+ }
+
+ if (src->freqs) {
+ int len = int_array_len(src->freqs);
+ params->freqs = os_malloc((len + 1) * sizeof(int));
+ if (params->freqs == NULL)
+ goto failed;
+ os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int));
+ }
+
+ if (src->filter_ssids) {
+ params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) *
+ src->num_filter_ssids);
+ if (params->filter_ssids == NULL)
+ goto failed;
+ os_memcpy(params->filter_ssids, src->filter_ssids,
+ sizeof(*params->filter_ssids) *
+ src->num_filter_ssids);
+ params->num_filter_ssids = src->num_filter_ssids;
+ }
+
+ params->filter_rssi = src->filter_rssi;
+ params->p2p_probe = src->p2p_probe;
+ params->only_new_results = src->only_new_results;
+ params->low_priority = src->low_priority;
+
+ return params;
+
+failed:
+ wpa_scan_free_params(params);
+ return NULL;
+}
+
+
+void wpa_scan_free_params(struct wpa_driver_scan_params *params)
+{
+ size_t i;
+
+ if (params == NULL)
+ return;
+
+ for (i = 0; i < params->num_ssids; i++)
+ os_free((u8 *) params->ssids[i].ssid);
+ os_free((u8 *) params->extra_ies);
+ os_free(params->freqs);
+ os_free(params->filter_ssids);
+ os_free(params);
+}
+
+
+int wpas_start_pno(struct wpa_supplicant *wpa_s)
+{
+ int ret, interval;
+ size_t i, num_ssid, num_match_ssid;
+ struct wpa_ssid *ssid;
+ struct wpa_driver_scan_params params;
+
+ if (!wpa_s->sched_scan_supported)
+ return -1;
+
+ if (wpa_s->pno || wpa_s->pno_sched_pending)
+ return 0;
+
+ if ((wpa_s->wpa_state > WPA_SCANNING) &&
+ (wpa_s->wpa_state <= WPA_COMPLETED)) {
+ wpa_printf(MSG_ERROR, "PNO: In assoc process");
+ return -EAGAIN;
+ }
+
+ if (wpa_s->wpa_state == WPA_SCANNING) {
+ wpa_supplicant_cancel_scan(wpa_s);
+ if (wpa_s->sched_scanning) {
+ wpa_printf(MSG_DEBUG, "Schedule PNO on completion of "
+ "ongoing sched scan");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_s->pno_sched_pending = 1;
+ return 0;
+ }
+ }
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ num_ssid = num_match_ssid = 0;
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid)) {
+ num_match_ssid++;
+ if (ssid->scan_ssid)
+ num_ssid++;
+ }
+ ssid = ssid->next;
+ }
+
+ if (num_match_ssid == 0) {
+ wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
+ return -1;
+ }
+
+ if (num_match_ssid > num_ssid) {
+ params.num_ssids++; /* wildcard */
+ num_ssid++;
+ }
+
+ if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
+ wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
+ "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
+ num_ssid = WPAS_MAX_SCAN_SSIDS;
+ }
+
+ if (num_match_ssid > wpa_s->max_match_sets) {
+ num_match_ssid = wpa_s->max_match_sets;
+ wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match");
+ }
+ params.filter_ssids = os_calloc(num_match_ssid,
+ sizeof(struct wpa_driver_scan_filter));
+ if (params.filter_ssids == NULL)
+ return -1;
+ i = 0;
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid)) {
+ if (ssid->scan_ssid && params.num_ssids < num_ssid) {
+ params.ssids[params.num_ssids].ssid =
+ ssid->ssid;
+ params.ssids[params.num_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_ssids++;
+ }
+ os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
+ ssid->ssid_len);
+ params.filter_ssids[i].ssid_len = ssid->ssid_len;
+ params.num_filter_ssids++;
+ i++;
+ if (i == num_match_ssid)
+ break;
+ }
+ ssid = ssid->next;
+ }
+
+ if (wpa_s->conf->filter_rssi)
+ params.filter_rssi = wpa_s->conf->filter_rssi;
+
+ interval = wpa_s->conf->sched_scan_interval ?
+ wpa_s->conf->sched_scan_interval : 10;
+
+ if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
+ params.freqs = wpa_s->manual_sched_scan_freqs;
+ }
+
+ ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, interval);
+ os_free(params.filter_ssids);
+ if (ret == 0)
+ wpa_s->pno = 1;
+ else
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO");
+ return ret;
+}
+
+
+int wpas_stop_pno(struct wpa_supplicant *wpa_s)
+{
+ int ret = 0;
+
+ if (!wpa_s->pno)
+ return 0;
+
+ ret = wpa_supplicant_stop_sched_scan(wpa_s);
+
+ wpa_s->pno = 0;
+ wpa_s->pno_sched_pending = 0;
+
+ if (wpa_s->wpa_state == WPA_SCANNING)
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return ret;
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 2144787..946d2b3 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - Scanning
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -29,6 +29,8 @@
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
u32 vendor_type);
+const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
+ u32 vendor_type);
struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
u32 vendor_type);
int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
@@ -37,5 +39,14 @@
void scan_only_handler(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ int interval);
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
+struct wpa_driver_scan_params *
+wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
+void wpa_scan_free_params(struct wpa_driver_scan_params *params);
+int wpas_start_pno(struct wpa_supplicant *wpa_s);
+int wpas_stop_pno(struct wpa_supplicant *wpa_s);
#endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 925d132..5188b9f 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1,6 +1,6 @@
/*
* wpa_supplicant - SME
- * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -46,7 +46,7 @@
{
int i;
for (i = 0; i < idx; i++) {
- if (array[i] == -1)
+ if (array[i] <= 0)
return 0;
}
return 1;
@@ -56,9 +56,9 @@
static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
{
int *groups = wpa_s->conf->sae_groups;
- int default_groups[] = { 19, 20, 21, 25, 26 };
+ int default_groups[] = { 19, 20, 21, 25, 26, 0 };
- if (!groups)
+ if (!groups || groups[0] <= 0)
groups = default_groups;
/* Configuration may have changed, so validate current index */
@@ -151,12 +151,13 @@
#endif /* CONFIG_IEEE80211R */
int i, bssid_changed;
struct wpabuf *resp = NULL;
- u8 ext_capab[10];
+ u8 ext_capab[18];
int ext_capab_len;
if (bss == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
"the network");
+ wpas_connect_work_done(wpa_s);
return;
}
@@ -244,6 +245,7 @@
&wpa_s->sme.assoc_req_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites");
+ wpas_connect_work_done(wpa_s);
return;
}
} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
@@ -263,6 +265,7 @@
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites (no "
"scan results)");
+ wpas_connect_work_done(wpa_s);
return;
}
#ifdef CONFIG_WPS
@@ -357,17 +360,25 @@
struct wpabuf *hs20;
hs20 = wpabuf_alloc(20);
if (hs20) {
- wpas_hs20_add_indication(hs20);
- os_memcpy(wpa_s->sme.assoc_req_ie +
- wpa_s->sme.assoc_req_ie_len,
- wpabuf_head(hs20), wpabuf_len(hs20));
- wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+ int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+ size_t len;
+
+ wpas_hs20_add_indication(hs20, pps_mo_id);
+ len = sizeof(wpa_s->sme.assoc_req_ie) -
+ wpa_s->sme.assoc_req_ie_len;
+ if (wpabuf_len(hs20) <= len) {
+ os_memcpy(wpa_s->sme.assoc_req_ie +
+ wpa_s->sme.assoc_req_ie_len,
+ wpabuf_head(hs20), wpabuf_len(hs20));
+ wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+ }
wpabuf_free(hs20);
}
}
#endif /* CONFIG_HS20 */
- ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+ ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+ sizeof(ext_capab));
if (ext_capab_len > 0) {
u8 *pos = wpa_s->sme.assoc_req_ie;
if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
@@ -386,8 +397,10 @@
bss->bssid);
else
resp = sme_auth_build_sae_confirm(wpa_s);
- if (resp == NULL)
+ if (resp == NULL) {
+ wpas_connect_work_done(wpa_s);
return;
+ }
params.sae_data = wpabuf_head(resp);
params.sae_data_len = wpabuf_len(resp);
wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
@@ -410,6 +423,32 @@
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
+#ifdef CONFIG_P2P
+ /*
+ * If multi-channel concurrency is not supported, check for any
+ * frequency conflict. In case of any frequency conflict, remove the
+ * least prioritized connection.
+ */
+ if (wpa_s->num_multichan_concurrent < 2) {
+ int freq, num;
+ num = get_shared_radio_freqs(wpa_s, &freq, 1);
+ if (num > 0 && freq > 0 && freq != params.freq) {
+ wpa_printf(MSG_DEBUG,
+ "Conflicting frequency found (%d != %d)",
+ freq, params.freq);
+ if (wpas_p2p_handle_frequency_conflicts(wpa_s,
+ params.freq,
+ ssid) < 0) {
+ wpas_connection_failed(wpa_s, bss->bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpabuf_free(resp);
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+ }
+ }
+#endif /* CONFIG_P2P */
+
wpa_s->sme.auth_alg = params.auth_alg;
if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) {
wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
@@ -417,6 +456,7 @@
wpas_connection_failed(wpa_s, bss->bssid);
wpa_supplicant_mark_disassoc(wpa_s);
wpabuf_free(resp);
+ wpas_connect_work_done(wpa_s);
return;
}
@@ -432,14 +472,70 @@
}
+static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_connect_work *cwork = work->ctx;
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+
+ if (deinit) {
+ if (work->started)
+ wpa_s->connect_work = NULL;
+
+ wpas_connect_work_free(cwork);
+ return;
+ }
+
+ wpa_s->connect_work = work;
+
+ if (!wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+
+ sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
+}
+
+
void sme_authenticate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
+ struct wpa_connect_work *cwork;
+
+ if (bss == NULL || ssid == NULL)
+ return;
+ if (wpa_s->connect_work) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist");
+ return;
+ }
+
+ if (radio_work_pending(wpa_s, "sme-connect")) {
+ /*
+ * The previous sme-connect work might no longer be valid due to
+ * the fact that the BSS list was updated. In addition, it makes
+ * sense to adhere to the 'newer' decision.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SME: Remove previous pending sme-connect");
+ radio_remove_works(wpa_s, "sme-connect", 0);
+ }
+
+ cwork = os_zalloc(sizeof(*cwork));
+ if (cwork == NULL)
+ return;
+ cwork->bss = bss;
+ cwork->ssid = ssid;
+ cwork->sme = 1;
+
#ifdef CONFIG_SAE
wpa_s->sme.sae.state = SAE_NOTHING;
wpa_s->sme.sae.send_confirm = 0;
+ wpa_s->sme.sae_group_index = 0;
#endif /* CONFIG_SAE */
- sme_send_authentication(wpa_s, bss, ssid, 1);
+
+ if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1,
+ sme_auth_start_cb, cwork) < 0)
+ wpas_connect_work_free(cwork);
}
@@ -482,15 +578,18 @@
return -1;
if (auth_transaction == 1) {
+ int *groups = wpa_s->conf->sae_groups;
+
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
if (wpa_s->current_bss == NULL ||
wpa_s->current_ssid == NULL)
return -1;
if (wpa_s->sme.sae.state != SAE_COMMITTED)
return -1;
+ if (groups && groups[0] <= 0)
+ groups = NULL;
if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
- wpa_s->conf->sae_groups) !=
- WLAN_STATUS_SUCCESS)
+ groups) != WLAN_STATUS_SUCCESS)
return -1;
if (sae_process_commit(&wpa_s->sme.sae) < 0) {
@@ -585,6 +684,8 @@
return;
}
+ wpas_connect_work_done(wpa_s);
+
switch (data->auth.auth_type) {
case WLAN_AUTH_OPEN:
wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED;
@@ -647,9 +748,10 @@
params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
wpa_s->sme.assoc_req_ie : NULL;
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
- params.pairwise_suite =
- wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
- params.group_suite = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
+ params.pairwise_suite = wpa_s->pairwise_cipher;
+ params.group_suite = wpa_s->group_cipher;
+ params.key_mgmt_suite = wpa_s->key_mgmt;
+ params.wpa_proto = wpa_s->wpa_proto;
#ifdef CONFIG_HT_OVERRIDES
os_memset(&htcaps, 0, sizeof(htcaps));
os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
@@ -696,6 +798,10 @@
params.wpa_proto = WPA_PROTO_WPA;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
elems.wpa_ie_len + 2);
+ } else if (elems.osen) {
+ params.wpa_proto = WPA_PROTO_OSEN;
+ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2,
+ elems.osen_len + 2);
} else
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
@@ -903,8 +1009,11 @@
struct ieee80211_2040_intol_chan_report *ic_report;
struct wpabuf *buf;
- wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR,
- MAC2STR(wpa_s->bssid));
+ wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR
+ " (num_channels=%u num_intol=%u)",
+ MAC2STR(wpa_s->bssid), num_channels, num_intol);
+ wpa_hexdump(MSG_DEBUG, "SME: 20/40 BSS Intolerant Channels",
+ chan_list, num_channels);
buf = wpabuf_alloc(2 + /* action.category + action_code */
sizeof(struct ieee80211_2040_bss_coex_ie) +
@@ -986,8 +1095,14 @@
ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
+ wpa_printf(MSG_DEBUG, "SME OBSS scan BSS " MACSTR
+ " freq=%u chan=%u ht_cap=0x%x",
+ MAC2STR(bss->bssid), bss->freq, channel, ht_cap);
if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
+ if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+ num_intol++;
+
/* Check whether the channel is already considered */
for (i = 0; i < num_channels; i++) {
if (channel == chan_list[i])
@@ -996,9 +1111,6 @@
if (i != num_channels)
continue;
- if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
- num_intol++;
-
chan_list[num_channels++] = channel;
}
}
@@ -1061,6 +1173,7 @@
os_memset(¶ms, 0, sizeof(params));
wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms);
+ params.low_priority = 1;
wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
if (wpa_supplicant_trigger_scan(wpa_s, ¶ms))
@@ -1147,9 +1260,9 @@
static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
{
u32 tu;
- struct os_time now, passed;
- os_get_time(&now);
- os_time_sub(&now, &wpa_s->sme.sa_query_start, &passed);
+ struct os_reltime now, passed;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->sme.sa_query_start, &passed);
tu = (passed.sec * 1000000 + passed.usec) / 1024;
if (sa_query_max_timeout < tu) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out");
@@ -1199,7 +1312,7 @@
return;
if (wpa_s->sme.sa_query_count == 0) {
/* Starting a new SA Query procedure */
- os_get_time(&wpa_s->sme.sa_query_start);
+ os_get_reltime(&wpa_s->sme.sa_query_start);
}
trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
wpa_s->sme.sa_query_trans_id = nbuf;
@@ -1238,6 +1351,7 @@
const u8 *da, u16 reason_code)
{
struct wpa_ssid *ssid;
+ struct os_reltime now;
if (wpa_s->wpa_state != WPA_COMPLETED)
return;
@@ -1254,6 +1368,12 @@
if (wpa_s->sme.sa_query_count > 0)
return;
+ os_get_reltime(&now);
+ if (wpa_s->sme.last_unprot_disconnect.sec &&
+ !os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10))
+ return; /* limit SA Query procedure frequency */
+ wpa_s->sme.last_unprot_disconnect = now;
+
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
"possible AP/STA state mismatch - trigger SA Query");
sme_start_sa_query(wpa_s);
diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c
index 484a406..39971f2 100644
--- a/wpa_supplicant/tests/test_wpa.c
+++ b/wpa_supplicant/tests/test_wpa.c
@@ -17,10 +17,6 @@
#include "ap/wpa_auth.h"
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
-
struct wpa {
u8 auth_addr[ETH_ALEN];
u8 supp_addr[ETH_ALEN];
diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c
index 92ca536..b6f9236 100644
--- a/wpa_supplicant/wifi_display.c
+++ b/wpa_supplicant/wifi_display.c
@@ -16,6 +16,9 @@
#include "wifi_display.h"
+#define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
+
+
int wifi_display_init(struct wpa_global *global)
{
global->wifi_display = 1;
@@ -38,6 +41,9 @@
struct wpabuf *ie, *buf;
size_t len, plen;
+ if (global->p2p == NULL)
+ return 0;
+
wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
if (!global->wifi_display) {
@@ -249,3 +255,53 @@
1,
wpabuf_len(global->wfd_subelem[subelem]) - 1);
}
+
+
+char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
+{
+ char *subelem = NULL;
+ const u8 *buf;
+ size_t buflen;
+ size_t i = 0;
+ u16 elen;
+
+ if (!wfd_subelems)
+ return NULL;
+
+ buf = wpabuf_head_u8(wfd_subelems);
+ if (!buf)
+ return NULL;
+
+ buflen = wpabuf_len(wfd_subelems);
+
+ while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
+ elen = WPA_GET_BE16(buf + i + 1);
+ if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
+ break; /* truncated subelement */
+
+ if (buf[i] == id) {
+ /*
+ * Limit explicitly to an arbitrary length to avoid
+ * unnecessarily large allocations. In practice, this
+ * is limited to maximum frame length anyway, so the
+ * maximum memory allocation here is not really that
+ * large. Anyway, the Wi-Fi Display subelements that
+ * are fetched with this function are even shorter.
+ */
+ if (elen > 1000)
+ break;
+ subelem = os_zalloc(2 * elen + 1);
+ if (!subelem)
+ return NULL;
+ wpa_snprintf_hex(subelem, 2 * elen + 1,
+ buf + i +
+ WIFI_DISPLAY_SUBELEM_HEADER_LEN,
+ elen);
+ break;
+ }
+
+ i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
+ }
+
+ return subelem;
+}
diff --git a/wpa_supplicant/wifi_display.h b/wpa_supplicant/wifi_display.h
index b75d4f2..7554817 100644
--- a/wpa_supplicant/wifi_display.h
+++ b/wpa_supplicant/wifi_display.h
@@ -16,5 +16,6 @@
int wifi_display_subelem_set(struct wpa_global *global, char *cmd);
int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
char *buf, size_t buflen);
+char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id);
#endif /* WIFI_DISPLAY_H */
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 4f8d895..4a792c4 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -18,6 +18,7 @@
#include "ctrl_iface.h"
#include "bss.h"
#include "wnm_sta.h"
+#include "hs20_supplicant.h"
#define MAX_TFS_IE_LEN 1024
#define WNM_MAX_NEIGHBOR_REPORT 10
@@ -181,7 +182,7 @@
/* Install GTK/IGTK */
/* point to key data field */
- ptr = (u8 *) frm + 1 + 1 + 2;
+ ptr = (u8 *) frm + 1 + 2;
end = ptr + key_len_total;
wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
@@ -234,19 +235,23 @@
const u8 *frm, int len)
{
/*
- * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data |
+ * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
* WNM-Sleep Mode IE | TFS Response IE
*/
- u8 *pos = (u8 *) frm; /* point to action field */
- u16 key_len_total = le_to_host16(*((u16 *)(frm+2)));
+ u8 *pos = (u8 *) frm; /* point to payload after the action field */
+ u16 key_len_total;
struct wnm_sleep_element *wnmsleep_ie = NULL;
/* multiple TFS Resp IE (assuming consecutive) */
u8 *tfsresp_ie_start = NULL;
u8 *tfsresp_ie_end = NULL;
- wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d",
- frm[0], frm[1], key_len_total);
- pos += 4 + key_len_total;
+ if (len < 3)
+ return;
+ key_len_total = WPA_GET_LE16(frm + 1);
+
+ wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
+ frm[0], key_len_total);
+ pos += 3 + key_len_total;
if (pos > frm + len) {
wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
return;
@@ -314,6 +319,7 @@
os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
}
+ wpa_s->wnm_num_neighbor_report = 0;
os_free(wpa_s->wnm_neighbor_report_elements);
wpa_s->wnm_neighbor_report_elements = NULL;
}
@@ -328,10 +334,10 @@
wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
break;
}
+ os_free(rep->tsf_info);
rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
if (rep->tsf_info == NULL)
break;
- rep->tsf_info->present = 1;
os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
break;
@@ -341,11 +347,11 @@
"country string");
break;
}
+ os_free(rep->con_coun_str);
rep->con_coun_str =
os_zalloc(sizeof(struct condensed_country_string));
if (rep->con_coun_str == NULL)
break;
- rep->con_coun_str->present = 1;
os_memcpy(rep->con_coun_str->country_string, pos, 2);
break;
case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
@@ -354,25 +360,25 @@
"candidate");
break;
}
+ os_free(rep->bss_tran_can);
rep->bss_tran_can =
os_zalloc(sizeof(struct bss_transition_candidate));
if (rep->bss_tran_can == NULL)
break;
- rep->bss_tran_can->present = 1;
rep->bss_tran_can->preference = pos[0];
break;
case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
- if (elen < 12) {
+ if (elen < 10) {
wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
"duration");
break;
}
+ os_free(rep->bss_term_dur);
rep->bss_term_dur =
os_zalloc(sizeof(struct bss_termination_duration));
if (rep->bss_term_dur == NULL)
break;
- rep->bss_term_dur->present = 1;
- os_memcpy(rep->bss_term_dur->duration, pos, 12);
+ os_memcpy(rep->bss_term_dur->duration, pos, 10);
break;
case WNM_NEIGHBOR_BEARING:
if (elen < 8) {
@@ -380,51 +386,51 @@
"bearing");
break;
}
+ os_free(rep->bearing);
rep->bearing = os_zalloc(sizeof(struct bearing));
if (rep->bearing == NULL)
break;
- rep->bearing->present = 1;
os_memcpy(rep->bearing->bearing, pos, 8);
break;
case WNM_NEIGHBOR_MEASUREMENT_PILOT:
- if (elen < 2) {
+ if (elen < 1) {
wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
"pilot");
break;
}
+ os_free(rep->meas_pilot);
rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
if (rep->meas_pilot == NULL)
break;
- rep->meas_pilot->present = 1;
rep->meas_pilot->measurement_pilot = pos[0];
- rep->meas_pilot->num_vendor_specific = pos[1];
- os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2);
+ rep->meas_pilot->subelem_len = elen - 1;
+ os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
break;
case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
- if (elen < 4) {
+ if (elen < 5) {
wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
"capabilities");
break;
}
+ os_free(rep->rrm_cap);
rep->rrm_cap =
os_zalloc(sizeof(struct rrm_enabled_capabilities));
if (rep->rrm_cap == NULL)
break;
- rep->rrm_cap->present = 1;
- os_memcpy(rep->rrm_cap->capabilities, pos, 4);
+ os_memcpy(rep->rrm_cap->capabilities, pos, 5);
break;
case WNM_NEIGHBOR_MULTIPLE_BSSID:
- if (elen < 2) {
+ if (elen < 1) {
wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
break;
}
+ os_free(rep->mul_bssid);
rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
if (rep->mul_bssid == NULL)
break;
- rep->mul_bssid->present = 1;
rep->mul_bssid->max_bssid_indicator = pos[0];
- rep->mul_bssid->num_vendor_specific = pos[1];
- os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2);
+ rep->mul_bssid->subelem_len = elen - 1;
+ os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
break;
}
}
@@ -455,8 +461,15 @@
id = *pos++;
elen = *pos++;
+ wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
+ left -= 2;
+ if (elen > left) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Truncated neighbor report subelement");
+ break;
+ }
wnm_parse_neighbor_report_elem(rep, id, elen, pos);
- left -= 2 + elen;
+ left -= elen;
pos += elen;
}
}
@@ -470,9 +483,12 @@
u8 i, j;
- if (scan_res == NULL || num_neigh_rep == 0)
+ if (scan_res == NULL || num_neigh_rep == 0 || !wpa_s->current_bss)
return 0;
+ wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
+ MAC2STR(wpa_s->bssid), wpa_s->current_bss->level);
+
for (i = 0; i < num_neigh_rep; i++) {
for (j = 0; j < scan_res->num; j++) {
/* Check for a better RSSI AP */
@@ -483,8 +499,16 @@
/* Got a BSSID with better RSSI value */
os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "Found a BSS " MACSTR
+ " with better scan RSSI %d",
+ MAC2STR(scan_res->res[j]->bssid),
+ scan_res->res[j]->level);
return 1;
}
+ wpa_printf(MSG_DEBUG, "scan_res[%d] " MACSTR
+ " RSSI %d", j,
+ MAC2STR(scan_res->res[j]->bssid),
+ scan_res->res[j]->level);
}
}
@@ -521,6 +545,14 @@
if (target_bssid) {
os_memcpy(pos, target_bssid, ETH_ALEN);
pos += ETH_ALEN;
+ } else if (status == WNM_BSS_TM_ACCEPT) {
+ /*
+ * P802.11-REVmc clarifies that the Target BSSID field is always
+ * present when status code is zero, so use a fake value here if
+ * no BSSID is yet known.
+ */
+ os_memset(pos, 0, ETH_ALEN);
+ pos += ETH_ALEN;
}
len = pos - (u8 *) &mgmt->u.action.category;
@@ -562,7 +594,7 @@
wnm_send_bss_transition_mgmt_resp(wpa_s,
wpa_s->wnm_dialog_token,
WNM_BSS_TM_ACCEPT,
- 0, NULL);
+ 0, bssid);
}
wpa_s->reassociate = 1;
@@ -670,10 +702,12 @@
wpa_printf(MSG_DEBUG, "WNM: Truncated request");
return;
}
- wnm_parse_neighbor_report(
- wpa_s, pos, len,
- &wpa_s->wnm_neighbor_report_elements[
- wpa_s->wnm_num_neighbor_report]);
+ if (tag == WLAN_EID_NEIGHBOR_REPORT) {
+ struct neighbor_report *rep;
+ rep = &wpa_s->wnm_neighbor_report_elements[
+ wpa_s->wnm_num_neighbor_report];
+ wnm_parse_neighbor_report(wpa_s, pos, len, rep);
+ }
pos += len;
wpa_s->wnm_num_neighbor_report++;
@@ -717,7 +751,7 @@
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
- mgmt->u.action.u.bss_tm_query.dialog_token = 0;
+ mgmt->u.action.u.bss_tm_query.dialog_token = 1;
mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
pos = mgmt->u.action.u.bss_tm_query.variable;
@@ -731,23 +765,170 @@
}
+static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *data,
+ int len)
+{
+ const u8 *pos, *end, *next;
+ u8 ie, ie_len;
+
+ pos = data;
+ end = data + len;
+
+ while (pos + 1 < end) {
+ ie = *pos++;
+ ie_len = *pos++;
+ wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
+ ie, ie_len);
+ if (ie_len > end - pos) {
+ wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
+ "subelement");
+ break;
+ }
+ next = pos + ie_len;
+ if (ie_len < 4) {
+ pos = next;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
+ WPA_GET_BE24(pos), pos[3]);
+
+#ifdef CONFIG_HS20
+ if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
+ WPA_GET_BE24(pos) == OUI_WFA &&
+ pos[3] == HS20_WNM_SUB_REM_NEEDED) {
+ /* Subscription Remediation subelement */
+ const u8 *ie_end;
+ u8 url_len;
+ char *url;
+ u8 osu_method;
+
+ wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
+ "subelement");
+ ie_end = pos + ie_len;
+ pos += 4;
+ url_len = *pos++;
+ if (url_len == 0) {
+ wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
+ url = NULL;
+ osu_method = 1;
+ } else {
+ if (pos + url_len + 1 > ie_end) {
+ wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
+ url_len,
+ (int) (ie_end - pos));
+ break;
+ }
+ url = os_malloc(url_len + 1);
+ if (url == NULL)
+ break;
+ os_memcpy(url, pos, url_len);
+ url[url_len] = '\0';
+ osu_method = pos[url_len];
+ }
+ hs20_rx_subscription_remediation(wpa_s, url,
+ osu_method);
+ os_free(url);
+ pos = next;
+ continue;
+ }
+
+ if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
+ WPA_GET_BE24(pos) == OUI_WFA &&
+ pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
+ const u8 *ie_end;
+ u8 url_len;
+ char *url;
+ u8 code;
+ u16 reauth_delay;
+
+ ie_end = pos + ie_len;
+ pos += 4;
+ code = *pos++;
+ reauth_delay = WPA_GET_LE16(pos);
+ pos += 2;
+ url_len = *pos++;
+ wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
+ "Imminent - Reason Code %u "
+ "Re-Auth Delay %u URL Length %u",
+ code, reauth_delay, url_len);
+ if (pos + url_len > ie_end)
+ break;
+ url = os_malloc(url_len + 1);
+ if (url == NULL)
+ break;
+ os_memcpy(url, pos, url_len);
+ url[url_len] = '\0';
+ hs20_rx_deauth_imminent_notice(wpa_s, code,
+ reauth_delay, url);
+ os_free(url);
+ pos = next;
+ continue;
+ }
+#endif /* CONFIG_HS20 */
+
+ pos = next;
+ }
+}
+
+
+static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *frm, int len)
+{
+ const u8 *pos, *end;
+ u8 dialog_token, type;
+
+ /* Dialog Token [1] | Type [1] | Subelements */
+
+ if (len < 2 || sa == NULL)
+ return;
+ end = frm + len;
+ pos = frm;
+ dialog_token = *pos++;
+ type = *pos++;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
+ "(dialog_token %u type %u sa " MACSTR ")",
+ dialog_token, type, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
+ pos, end - pos);
+
+ if (wpa_s->wpa_state != WPA_COMPLETED ||
+ os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
+ "from our AP - ignore it");
+ return;
+ }
+
+ switch (type) {
+ case 1:
+ ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
+ break;
+ default:
+ wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
+ "WNM-Notification type %u", type);
+ break;
+ }
+}
+
+
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
- struct rx_action *action)
+ const struct ieee80211_mgmt *mgmt, size_t len)
{
const u8 *pos, *end;
u8 act;
- if (action->data == NULL || action->len == 0)
+ if (len < IEEE80211_HDRLEN + 2)
return;
- pos = action->data;
- end = pos + action->len;
+ pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
act = *pos++;
+ end = ((const u8 *) mgmt) + len;
wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
- act, MAC2STR(action->sa));
+ act, MAC2STR(mgmt->sa));
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
- os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
"frame");
return;
@@ -756,10 +937,13 @@
switch (act) {
case WNM_BSS_TRANS_MGMT_REQ:
ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
- !(action->da[0] & 0x01));
+ !(mgmt->da[0] & 0x01));
break;
case WNM_SLEEP_MODE_RESP:
- ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
+ ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
+ break;
+ case WNM_NOTIFICATION_REQ:
+ ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
break;
default:
wpa_printf(MSG_ERROR, "WNM: Unknown request");
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 2933926..d2eb96d 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -9,52 +9,41 @@
#ifndef WNM_STA_H
#define WNM_STA_H
-struct rx_action;
-struct wpa_supplicant;
-
struct tsf_info {
- u8 present;
u8 tsf_offset[2];
u8 beacon_interval[2];
};
struct condensed_country_string {
- u8 present;
u8 country_string[2];
};
struct bss_transition_candidate {
- u8 present;
u8 preference;
};
struct bss_termination_duration {
- u8 present;
- u8 duration[12];
+ u8 duration[10];
};
struct bearing {
- u8 present;
u8 bearing[8];
};
struct measurement_pilot {
- u8 present;
u8 measurement_pilot;
- u8 num_vendor_specific;
- u8 vendor_specific[255];
+ u8 subelem_len;
+ u8 subelems[255];
};
struct rrm_enabled_capabilities {
- u8 present;
- u8 capabilities[4];
+ u8 capabilities[5];
};
struct multiple_bssid {
- u8 present;
u8 max_bssid_indicator;
- u8 num_vendor_specific;
- u8 vendor_specific[255];
+ u8 subelem_len;
+ u8 subelems[255];
};
struct neighbor_report {
@@ -78,7 +67,7 @@
u8 action, u16 intval, struct wpabuf *tfs_req);
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
- struct rx_action *action);
+ const struct ieee80211_mgmt *mgmt, size_t len);
void wnm_scan_response(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index a379d65..0ad6187 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -28,7 +28,7 @@
static const char *wpa_cli_version =
"wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> and contributors";
static const char *wpa_cli_license =
@@ -496,6 +496,8 @@
return wpa_ctrl_command(ctrl, "STATUS-VERBOSE");
if (argc > 0 && os_strcmp(argv[0], "wps") == 0)
return wpa_ctrl_command(ctrl, "STATUS-WPS");
+ if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
+ return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
return wpa_ctrl_command(ctrl, "STATUS");
}
@@ -613,7 +615,9 @@
"p2p_oper_reg_class", "p2p_oper_channel",
"p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect",
"p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan",
+ "p2p_no_go_freq",
"p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface",
+ "p2p_go_vht",
"p2p_ignore_shared_freq", "country", "bss_max_count",
"bss_expiration_age", "bss_expiration_scan_count",
"filter_ssids", "filter_rssi", "max_num_sta",
@@ -623,9 +627,10 @@
"wps_nfc_dev_pw", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
"sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements",
- "ignore_old_scan_res", "freq_list"
+ "ignore_old_scan_res", "freq_list", "external_sim",
+ "tdls_external_control", "p2p_search_delay"
};
- int i, num_fields = sizeof(fields) / sizeof(fields[0]);
+ int i, num_fields = ARRAY_SIZE(fields);
if (arg == 1) {
char **res = os_calloc(num_fields + 1, sizeof(char *));
@@ -671,6 +676,12 @@
}
+static int wpa_cli_cmd_reattach(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "REATTACH");
+}
+
+
static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -831,58 +842,6 @@
}
-static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
-{
- int ret;
- char *buf;
- size_t buflen;
-
- if (argc != 1) {
- printf("Invalid 'nfc_rx_handover_req' command - one argument "
- "is required.\n");
- return -1;
- }
-
- buflen = 21 + os_strlen(argv[0]);
- buf = os_malloc(buflen);
- if (buf == NULL)
- return -1;
- os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]);
-
- ret = wpa_ctrl_command(ctrl, buf);
- os_free(buf);
-
- return ret;
-}
-
-
-static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
-{
- int ret;
- char *buf;
- size_t buflen;
-
- if (argc != 1) {
- printf("Invalid 'nfc_rx_handover_sel' command - one argument "
- "is required.\n");
- return -1;
- }
-
- buflen = 21 + os_strlen(argv[0]);
- buf = os_malloc(buflen);
- if (buf == NULL)
- return -1;
- os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]);
-
- ret = wpa_ctrl_command(ctrl, buf);
- os_free(buf);
-
- return ret;
-}
-
-
static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1268,6 +1227,38 @@
}
+static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256], *pos, *end;
+ int i, ret;
+
+ if (argc < 2) {
+ printf("Invalid SIM command: needs two arguments "
+ "(network id and SIM operation response)\n");
+ return -1;
+ }
+
+ end = cmd + sizeof(cmd);
+ pos = cmd;
+ ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s",
+ argv[0], argv[1]);
+ if (ret < 0 || ret >= end - pos) {
+ printf("Too long SIM command.\n");
+ return -1;
+ }
+ pos += ret;
+ for (i = 2; i < argc; i++) {
+ ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+ if (ret < 0 || ret >= end - pos) {
+ printf("Too long SIM command.\n");
+ return -1;
+ }
+ pos += ret;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1425,6 +1416,24 @@
}
+static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ if (argc == 0) {
+ wpa_cli_show_network_variables();
+ return 0;
+ }
+
+ if (argc < 3) {
+ printf("Invalid DUP_NETWORK command: needs three arguments\n"
+ "(src netid, dest netid, and variable name)\n");
+ return -1;
+ }
+
+ return wpa_cli_cmd(ctrl, "DUP_NETWORK", 3, argc, argv);
+}
+
+
static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1457,6 +1466,18 @@
}
+static int wpa_cli_cmd_get_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ if (argc != 2) {
+ printf("Invalid GET_CRED command: needs two arguments\n"
+ "(cred id, variable name)\n");
+ return -1;
+ }
+
+ return wpa_cli_cmd(ctrl, "GET_CRED", 2, argc, argv);
+}
+
+
static int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1549,7 +1570,7 @@
os_free(ctrl_ifname);
ctrl_ifname = os_strdup(argv[0]);
- if (wpa_cli_open_connection(ctrl_ifname, 1)) {
+ if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
printf("Connected to interface '%s.\n", ctrl_ifname);
} else {
printf("Could not connect to interface '%s' - re-trying\n",
@@ -1687,6 +1708,13 @@
{
return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
}
+
+static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "CHAN_SWITCH", 2, argc, argv);
+}
+
#endif /* CONFIG_AP */
@@ -1702,10 +1730,12 @@
}
+#ifdef CONFIG_TESTING_OPTIONS
static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "DROP_SA");
}
+#endif /* CONFIG_TESTING_OPTIONS */
static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -2078,7 +2108,7 @@
"disc_int",
"per_sta_psk",
};
- int i, num_fields = sizeof(fields) / sizeof(fields[0]);
+ int i, num_fields = ARRAY_SIZE(fields);
if (arg == 1) {
char **res = os_calloc(num_fields + 1, sizeof(char *));
@@ -2280,6 +2310,37 @@
return wpa_ctrl_command(ctrl, cmd);
}
+
+static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[512];
+
+ if (argc < 2) {
+ printf("Command needs two arguments (dst mac addr and "
+ "icon name)\n");
+ return -1;
+ }
+
+ if (write_cmd(cmd, sizeof(cmd), "HS20_ICON_REQUEST", argc, argv) < 0)
+ return -1;
+
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "FETCH_OSU");
+}
+
+
+static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU");
+}
+
#endif /* CONFIG_HS20 */
@@ -2372,23 +2433,15 @@
#ifdef ANDROID
static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- char cmd[256];
- int i;
- int len;
-
- if (argc < 1) {
- printf("Invalid DRIVER command: needs one argument (cmd)\n");
- return -1;
- }
-
- len = os_snprintf(cmd, sizeof(cmd), "DRIVER %s", argv[0]);
- for (i=1; i < argc; i++)
- len += os_snprintf(cmd + len, sizeof(cmd) - len, " %s", argv[i]);
- cmd[sizeof(cmd) - 1] = '\0';
- printf("%s: %s\n", __func__, cmd);
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "DRIVER", 1, argc, argv);
}
-#endif
+#endif /* ANDROID */
+
+
+static int wpa_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "VENDOR", 1, argc, argv);
+}
static int wpa_cli_cmd_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -2397,6 +2450,12 @@
}
+static int wpa_cli_cmd_radio_work(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "RADIO_WORK", 1, argc, argv);
+}
+
+
enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01
@@ -2463,6 +2522,9 @@
{ "reassociate", wpa_cli_cmd_reassociate, NULL,
cli_cmd_flag_none,
"= force reassociation" },
+ { "reattach", wpa_cli_cmd_reattach, NULL,
+ cli_cmd_flag_none,
+ "= force reassociation back to the same BSS" },
{ "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<BSSID> = force preauthentication" },
@@ -2486,6 +2548,9 @@
cli_cmd_flag_sensitive,
"<network id> <passphrase> = configure private key passphrase\n"
" for an SSID" },
+ { "sim", wpa_cli_cmd_sim, NULL,
+ cli_cmd_flag_sensitive,
+ "<network id> <pin> = report SIM operation result" },
{ "bssid", wpa_cli_cmd_bssid, NULL,
cli_cmd_flag_none,
"<network id> <BSSID> = set preferred BSSID for an SSID" },
@@ -2523,6 +2588,10 @@
{ "get_network", wpa_cli_cmd_get_network, NULL,
cli_cmd_flag_none,
"<network id> <variable> = get network variables" },
+ { "dup_network", wpa_cli_cmd_dup_network, NULL,
+ cli_cmd_flag_none,
+ "<src network id> <dst network id> <variable> = duplicate network variables"
+ },
{ "list_creds", wpa_cli_cmd_list_creds, NULL,
cli_cmd_flag_none,
"= list configured credentials" },
@@ -2535,6 +2604,9 @@
{ "set_cred", wpa_cli_cmd_set_cred, NULL,
cli_cmd_flag_sensitive,
"<cred id> <variable> <value> = set credential variables" },
+ { "get_cred", wpa_cli_cmd_get_cred, NULL,
+ cli_cmd_flag_none,
+ "<cred id> <variable> = get credential variables" },
{ "save_config", wpa_cli_cmd_save_config, NULL,
cli_cmd_flag_none,
"= save the current configuration" },
@@ -2628,12 +2700,6 @@
{ "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
cli_cmd_flag_none,
"<NDEF> <WPS> = create NFC handover select" },
- { "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL,
- cli_cmd_flag_none,
- "<hexdump of payload> = report received NFC handover request" },
- { "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL,
- cli_cmd_flag_none,
- "<hexdump of payload> = report received NFC handover select" },
{ "nfc_report_handover", wpa_cli_cmd_nfc_report_handover, NULL,
cli_cmd_flag_none,
"<role> <type> <hexdump of req> <hexdump of sel> = report completed "
@@ -2687,13 +2753,20 @@
{ "disassociate", wpa_cli_cmd_disassociate, NULL,
cli_cmd_flag_none,
"<addr> = disassociate a station" },
+ { "chan_switch", wpa_cli_cmd_chanswitch, NULL,
+ cli_cmd_flag_none,
+ "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]"
+ " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]"
+ " = CSA parameters" },
#endif /* CONFIG_AP */
{ "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none,
"= notification of suspend/hibernate" },
{ "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none,
"= notification of resume/thaw" },
+#ifdef CONFIG_TESTING_OPTIONS
{ "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none,
"= drop SA without deauth/disassoc (test command)" },
+#endif /* CONFIG_TESTING_OPTIONS */
{ "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> = roam to the specified BSS" },
@@ -2816,6 +2889,14 @@
{ "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list,
wpa_cli_complete_bss, cli_cmd_flag_none,
"<addr> <home realm> = get HS20 nai home realm list" },
+ { "hs20_icon_request", wpa_cli_cmd_hs20_icon_request,
+ wpa_cli_complete_bss, cli_cmd_flag_none,
+ "<addr> <icon name> = get Hotspot 2.0 OSU icon" },
+ { "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none,
+ "= fetch OSU provider information from all APs" },
+ { "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL,
+ cli_cmd_flag_none,
+ "= cancel fetch_osu command" },
#endif /* CONFIG_HS20 */
{ "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
cli_cmd_flag_none,
@@ -2853,10 +2934,14 @@
{ "flush", wpa_cli_cmd_flush, NULL, cli_cmd_flag_none,
"= flush wpa_supplicant state" },
#ifdef ANDROID
- { "driver", wpa_cli_cmd_driver, NULL,
- cli_cmd_flag_none,
+ { "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none,
"<command> = driver private commands" },
-#endif
+#endif /* ANDROID */
+ { "radio_work", wpa_cli_cmd_radio_work, NULL, cli_cmd_flag_none,
+ "= radio_work <show/add/done>" },
+ { "vendor", wpa_cli_cmd_vendor, NULL, cli_cmd_flag_none,
+ "<vendor id> <command id> [<hex formatted command argument>] = Send vendor command"
+ },
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
};
@@ -2917,7 +3002,7 @@
int i, count;
struct cli_txt_entry *e;
- count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]);
+ count = ARRAY_SIZE(wpa_cli_commands);
count += dl_list_len(&p2p_groups);
count += dl_list_len(&ifnames);
res = os_calloc(count + 1, sizeof(char *));
@@ -3064,28 +3149,19 @@
static int wpa_cli_exec(const char *program, const char *arg1,
const char *arg2)
{
- char *cmd;
+ char *arg;
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)
+ len = os_strlen(arg1) + os_strlen(arg2) + 2;
+ arg = os_malloc(len);
+ if (arg == 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);
+ os_snprintf(arg, len, "%s %s", arg1, arg2);
+ res = os_exec(program, arg, 1);
+ os_free(arg);
- return ret;
+ return res;
}
@@ -3165,6 +3241,10 @@
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_match(pos, ESS_DISASSOC_IMMINENT)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_match(pos, WPA_EVENT_TERMINATING)) {
printf("wpa_supplicant is terminating - stop monitoring\n");
wpa_cli_quit = 1;
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 4afaae9..5426177 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -552,8 +552,6 @@
}
-extern struct wpa_driver_ops *wpa_drivers[];
-
static struct wpa_priv_interface *
wpa_priv_interface_init(const char *dir, const char *params)
{
@@ -946,8 +944,6 @@
}
-extern int wpa_debug_level;
-
int main(int argc, char *argv[])
{
int c, i;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index b431662..9d21fe0 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -51,10 +51,11 @@
#include "offchannel.h"
#include "hs20_supplicant.h"
#include "wnm_sta.h"
+#include "wpas_kay.h"
const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> and contributors";
const char *wpa_supplicant_license =
"This software may be distributed under the terms of the BSD license.\n"
@@ -104,10 +105,8 @@
"\n";
#endif /* CONFIG_NO_STDOUT_DEBUG */
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-extern struct wpa_driver_ops *wpa_drivers[];
+struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
+ struct wpa_driver_capa *capa);
/* Configure default/group WEP keys for static WEP */
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
@@ -200,8 +199,6 @@
* So, wait a second until scanning again.
*/
wpa_supplicant_req_scan(wpa_s, 1, 0);
-
- wpas_p2p_continue_after_scan(wpa_s);
}
@@ -217,7 +214,7 @@
void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
int sec, int usec)
{
- if (wpa_s->conf && wpa_s->conf->ap_scan == 0 &&
+ if (wpa_s->conf->ap_scan == 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
return;
@@ -293,18 +290,20 @@
EAPOL_REQUIRE_KEY_BROADCAST;
}
- if (wpa_s->conf && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)
eapol_conf.required_keys = 0;
}
- if (wpa_s->conf)
- eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
eapol_conf.eap_disabled =
!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
+ eapol_conf.external_sim = wpa_s->conf->external_sim;
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
#endif /* IEEE8021X_EAPOL */
+
+ ieee802_1x_alloc_kay_sm(wpa_s, ssid);
}
@@ -379,6 +378,8 @@
static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{
+ int i;
+
bgscan_deinit(wpa_s);
autoscan_deinit(wpa_s);
scard_deinit(wpa_s->scard);
@@ -404,6 +405,11 @@
os_free(wpa_s->confanother);
wpa_s->confanother = NULL;
+#ifdef CONFIG_P2P
+ os_free(wpa_s->conf_p2p_dev);
+ wpa_s->conf_p2p_dev = NULL;
+#endif /* CONFIG_P2P */
+
wpa_sm_set_eapol(wpa_s->wpa, NULL);
eapol_sm_deinit(wpa_s->eapol);
wpa_s->eapol = NULL;
@@ -446,9 +452,7 @@
wpa_supplicant_ap_deinit(wpa_s);
#endif /* CONFIG_AP */
-#ifdef CONFIG_P2P
wpas_p2p_deinit(wpa_s);
-#endif /* CONFIG_P2P */
#ifdef CONFIG_OFFCHANNEL
offchannel_deinit(wpa_s);
@@ -459,11 +463,19 @@
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = NULL;
+
+ os_free(wpa_s->manual_sched_scan_freqs);
+ wpa_s->manual_sched_scan_freqs = NULL;
+
gas_query_deinit(wpa_s->gas);
wpa_s->gas = NULL;
free_hw_features(wpa_s);
+ ieee802_1x_dealloc_kay_sm(wpa_s);
+
os_free(wpa_s->bssid_filter);
wpa_s->bssid_filter = NULL;
@@ -481,9 +493,21 @@
wpa_s->ext_pw = NULL;
wpabuf_free(wpa_s->last_gas_resp);
+ wpa_s->last_gas_resp = NULL;
+ wpabuf_free(wpa_s->prev_gas_resp);
+ wpa_s->prev_gas_resp = NULL;
os_free(wpa_s->last_scan_res);
wpa_s->last_scan_res = NULL;
+
+#ifdef CONFIG_HS20
+ hs20_deinit(wpa_s);
+#endif /* CONFIG_HS20 */
+
+ for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+ wpabuf_free(wpa_s->vendor_elem[i]);
+ wpa_s->vendor_elem[i] = NULL;
+ }
}
@@ -497,29 +521,23 @@
*/
void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
{
- if (wpa_s->keys_cleared) {
- /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
- * timing issues with keys being cleared just before new keys
- * are set or just after association or something similar. This
- * shows up in group key handshake failing often because of the
- * client not receiving the first encrypted packets correctly.
- * Skipping some of the extra key clearing steps seems to help
- * in completing group key handshake more reliably. */
- wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - "
- "skip key clearing");
- return;
- }
+ int i, max;
+
+#ifdef CONFIG_IEEE80211W
+ max = 6;
+#else /* CONFIG_IEEE80211W */
+ max = 4;
+#endif /* CONFIG_IEEE80211W */
/* MLME-DELETEKEYS.request */
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
-#ifdef CONFIG_IEEE80211W
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
-#endif /* CONFIG_IEEE80211W */
- if (addr) {
+ for (i = 0; i < max; i++) {
+ if (wpa_s->keys_cleared & BIT(i))
+ continue;
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+ NULL, 0);
+ }
+ if (!(wpa_s->keys_cleared & BIT(0)) && addr &&
+ !is_zero_ether_addr(addr)) {
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
0);
/* MLME-SETPROTECTION.request(None) */
@@ -528,7 +546,7 @@
MLME_SETPROTECTION_PROTECT_TYPE_NONE,
MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
}
- wpa_s->keys_cleared = 1;
+ wpa_s->keys_cleared = (u32) -1;
}
@@ -570,14 +588,26 @@
static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
{
+ const char *name;
+
+ if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan)
+ name = wpa_s->current_ssid->bgscan;
+ else
+ name = wpa_s->conf->bgscan;
+ if (name == NULL || name[0] == '\0')
+ return;
if (wpas_driver_bss_selection(wpa_s))
return;
if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
return;
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+ return;
+#endif /* CONFIG_P2P */
bgscan_deinit(wpa_s);
- if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
- if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
+ if (wpa_s->current_ssid) {
+ if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
"bgscan");
/*
@@ -651,11 +681,16 @@
wpa_supplicant_state_txt(wpa_s->wpa_state),
wpa_supplicant_state_txt(state));
-#ifdef ANDROID_P2P
- if(state == WPA_ASSOCIATED && wpa_s->current_ssid) {
- wpa_s->current_ssid->assoc_retry = 0;
+ if (state == WPA_INTERFACE_DISABLED) {
+ /* Assure normal scan when interface is restored */
+ wpa_s->normal_scans = 0;
}
-#endif /* ANDROID_P2P */
+
+ if (state == WPA_COMPLETED) {
+ wpas_connect_work_done(wpa_s);
+ /* Reinitialize normal_scan counter */
+ wpa_s->normal_scans = 0;
+ }
if (state != WPA_SCANNING)
wpa_supplicant_notify_scanning(wpa_s, 0);
@@ -664,7 +699,7 @@
struct wpa_ssid *ssid = wpa_s->current_ssid;
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
- MACSTR " completed (auth) [id=%d id_str=%s]",
+ MACSTR " completed [id=%d id_str=%s]",
MAC2STR(wpa_s->bssid),
ssid ? ssid->id : -1,
ssid && ssid->id_str ? ssid->id_str : "");
@@ -677,9 +712,8 @@
wpa_drv_set_supp_port(wpa_s, 1);
#endif /* IEEE8021X_EAPOL */
wpa_s->after_wps = 0;
-#ifdef CONFIG_P2P
+ wpa_s->known_wps_freq = 0;
wpas_p2p_completed(wpa_s);
-#endif /* CONFIG_P2P */
sme_sched_obss_scan(wpa_s, 1);
} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
@@ -696,7 +730,7 @@
#ifdef CONFIG_BGSCAN
if (state == WPA_COMPLETED)
wpa_supplicant_start_bgscan(wpa_s);
- else
+ else if (state < WPA_ASSOCIATED)
wpa_supplicant_stop_bgscan(wpa_s);
#endif /* CONFIG_BGSCAN */
@@ -709,6 +743,12 @@
if (wpa_s->wpa_state != old_state) {
wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+ /*
+ * Notify the P2P Device interface about a state change in one
+ * of the interfaces.
+ */
+ wpas_p2p_indicate_state_change(wpa_s);
+
if (wpa_s->wpa_state == WPA_COMPLETED ||
old_state == WPA_COMPLETED)
wpas_notify_auth_changed(wpa_s);
@@ -722,9 +762,15 @@
#ifdef CONFIG_WPS
struct wpa_supplicant *wpa_s = global->ifaces;
while (wpa_s) {
+ struct wpa_supplicant *next = wpa_s->next;
if (wpas_wps_terminate_pending(wpa_s) == 1)
pending = 1;
- wpa_s = wpa_s->next;
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE ||
+ (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group))
+ wpas_p2p_disconnect(wpa_s);
+#endif /* CONFIG_P2P */
+ wpa_s = next;
}
#endif /* CONFIG_WPS */
if (pending)
@@ -853,34 +899,6 @@
}
-enum wpa_key_mgmt key_mgmt2driver(int key_mgmt)
-{
- switch (key_mgmt) {
- case WPA_KEY_MGMT_NONE:
- return KEY_MGMT_NONE;
- case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
- return KEY_MGMT_802_1X_NO_WPA;
- case WPA_KEY_MGMT_IEEE8021X:
- return KEY_MGMT_802_1X;
- case WPA_KEY_MGMT_WPA_NONE:
- return KEY_MGMT_WPA_NONE;
- case WPA_KEY_MGMT_FT_IEEE8021X:
- return KEY_MGMT_FT_802_1X;
- case WPA_KEY_MGMT_FT_PSK:
- return KEY_MGMT_FT_PSK;
- case WPA_KEY_MGMT_IEEE8021X_SHA256:
- return KEY_MGMT_802_1X_SHA256;
- case WPA_KEY_MGMT_PSK_SHA256:
- return KEY_MGMT_PSK_SHA256;
- case WPA_KEY_MGMT_WPS:
- return KEY_MGMT_WPS;
- case WPA_KEY_MGMT_PSK:
- default:
- return KEY_MGMT_PSK;
- }
-}
-
-
static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct wpa_ie_data *ie)
@@ -951,13 +969,14 @@
{
struct wpa_ie_data ie;
int sel, proto;
- const u8 *bss_wpa, *bss_rsn;
+ const u8 *bss_wpa, *bss_rsn, *bss_osen;
if (bss) {
bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
} else
- bss_wpa = bss_rsn = NULL;
+ bss_wpa = bss_rsn = bss_osen = NULL;
if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
@@ -973,11 +992,23 @@
(ie.key_mgmt & ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
proto = WPA_PROTO_WPA;
+#ifdef CONFIG_HS20
+ } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
+ /* TODO: parse OSEN element */
+ os_memset(&ie, 0, sizeof(ie));
+ ie.group_cipher = WPA_CIPHER_CCMP;
+ ie.pairwise_cipher = WPA_CIPHER_CCMP;
+ ie.key_mgmt = WPA_KEY_MGMT_OSEN;
+ proto = WPA_PROTO_OSEN;
+#endif /* CONFIG_HS20 */
} else if (bss) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
return -1;
} else {
- if (ssid->proto & WPA_PROTO_RSN)
+ if (ssid->proto & WPA_PROTO_OSEN)
+ proto = WPA_PROTO_OSEN;
+ else if (ssid->proto & WPA_PROTO_RSN)
proto = WPA_PROTO_RSN;
else
proto = WPA_PROTO_WPA;
@@ -1010,7 +1041,7 @@
wpa_s->wpa_proto = proto;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
- !!(ssid->proto & WPA_PROTO_RSN));
+ !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
if (bss || !wpa_s->ap_ies_from_associnfo) {
if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
@@ -1081,6 +1112,11 @@
} else if (sel & WPA_KEY_MGMT_WPA_NONE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+#ifdef CONFIG_HS20
+ } else if (sel & WPA_KEY_MGMT_OSEN) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
+#endif /* CONFIG_HS20 */
} else {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
"authenticated key management type");
@@ -1102,6 +1138,18 @@
wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
"AES-128-CMAC");
+ } else if (sel & WPA_CIPHER_BIP_GMAC_128) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ "BIP-GMAC-128");
+ } else if (sel & WPA_CIPHER_BIP_GMAC_256) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ "BIP-GMAC-256");
+ } else if (sel & WPA_CIPHER_BIP_CMAC_256) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ "BIP-CMAC-256");
} else {
wpa_s->mgmt_group_cipher = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
@@ -1220,8 +1268,16 @@
#endif /* CONFIG_INTERWORKING */
break;
case 4: /* Bits 32-39 */
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING)
+ *pos |= 0x01; /* Bit 32 - QoS Map */
+#endif /* CONFIG_INTERWORKING */
break;
case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+ if (wpa_s->conf->hs20)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
break;
case 6: /* Bits 48-55 */
break;
@@ -1229,13 +1285,18 @@
}
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
{
u8 *pos = buf;
- u8 len = 4, i;
+ u8 len = 6, i;
if (len < wpa_s->extended_capa_len)
len = wpa_s->extended_capa_len;
+ if (buflen < (size_t) len + 2) {
+ wpa_printf(MSG_INFO,
+ "Not enough room for building extended capabilities element");
+ return -1;
+ }
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = len;
@@ -1259,6 +1320,70 @@
}
+static int wpas_valid_bss(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *test_bss)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss == test_bss)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpas_valid_ssid(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *test_ssid)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == test_ssid)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
+ struct wpa_ssid *test_ssid)
+{
+ if (test_bss && !wpas_valid_bss(wpa_s, test_bss))
+ return 0;
+
+ return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid);
+}
+
+
+void wpas_connect_work_free(struct wpa_connect_work *cwork)
+{
+ if (cwork == NULL)
+ return;
+ os_free(cwork);
+}
+
+
+void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_connect_work *cwork;
+ struct wpa_radio_work *work = wpa_s->connect_work;
+
+ if (!work)
+ return;
+
+ wpa_s->connect_work = NULL;
+ cwork = work->ctx;
+ work->ctx = NULL;
+ wpas_connect_work_free(cwork);
+ radio_work_done(work);
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit);
+
/**
* wpa_supplicant_associate - Request association
* @wpa_s: Pointer to wpa_supplicant data
@@ -1270,29 +1395,12 @@
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
- u8 wpa_ie[200];
- size_t wpa_ie_len;
- int use_crypt, ret, i, bssid_changed;
- int algs = WPA_AUTH_ALG_OPEN;
- enum wpa_cipher cipher_pairwise, cipher_group;
- struct wpa_driver_associate_params params;
- int wep_keys_set = 0;
- int assoc_failed = 0;
- struct wpa_ssid *old_ssid;
- u8 ext_capab[10];
- int ext_capab_len;
-#ifdef CONFIG_HT_OVERRIDES
- struct ieee80211_ht_capabilities htcaps;
- struct ieee80211_ht_capabilities htcaps_mask;
-#endif /* CONFIG_HT_OVERRIDES */
+ struct wpa_connect_work *cwork;
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
wpa_s->ibss_rsn = NULL;
#endif /* CONFIG_IBSS_RSN */
-#ifdef ANDROID_P2P
- int freq = 0;
-#endif
if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
@@ -1328,8 +1436,77 @@
return;
}
+ if (wpa_s->connect_work) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");
+ return;
+ }
+
+ if (radio_work_pending(wpa_s, "connect")) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
+ return;
+ }
+
+ cwork = os_zalloc(sizeof(*cwork));
+ if (cwork == NULL)
+ return;
+
+ cwork->bss = bss;
+ cwork->ssid = ssid;
+
+ if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
+ wpas_start_assoc_cb, cwork) < 0) {
+ os_free(cwork);
+ }
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_connect_work *cwork = work->ctx;
+ struct wpa_bss *bss = cwork->bss;
+ struct wpa_ssid *ssid = cwork->ssid;
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ u8 wpa_ie[200];
+ size_t wpa_ie_len;
+ int use_crypt, ret, i, bssid_changed;
+ int algs = WPA_AUTH_ALG_OPEN;
+ unsigned int cipher_pairwise, cipher_group;
+ struct wpa_driver_associate_params params;
+ int wep_keys_set = 0;
+ int assoc_failed = 0;
+ struct wpa_ssid *old_ssid;
+#ifdef CONFIG_HT_OVERRIDES
+ struct ieee80211_ht_capabilities htcaps;
+ struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ struct ieee80211_vht_capabilities vhtcaps;
+ struct ieee80211_vht_capabilities vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+ if (deinit) {
+ if (work->started) {
+ wpa_s->connect_work = NULL;
+
+ /* cancel possible auth. timeout */
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
+ NULL);
+ }
+ wpas_connect_work_free(cwork);
+ return;
+ }
+
+ wpa_s->connect_work = work;
+
+ if (!wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+
os_memset(¶ms, 0, sizeof(params));
wpa_s->reassociate = 0;
+ wpa_s->eap_expected_failure = 0;
if (bss && !wpas_driver_bss_selection(wpa_s)) {
#ifdef CONFIG_IEEE80211R
const u8 *ie, *md = NULL;
@@ -1479,6 +1656,8 @@
"disallows" : "allows");
}
}
+
+ os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
#endif /* CONFIG_P2P */
#ifdef CONFIG_HS20
@@ -1486,30 +1665,49 @@
struct wpabuf *hs20;
hs20 = wpabuf_alloc(20);
if (hs20) {
- wpas_hs20_add_indication(hs20);
- os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20),
- wpabuf_len(hs20));
- wpa_ie_len += wpabuf_len(hs20);
+ int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+ size_t len;
+
+ wpas_hs20_add_indication(hs20, pps_mo_id);
+ len = sizeof(wpa_ie) - wpa_ie_len;
+ if (wpabuf_len(hs20) <= len) {
+ os_memcpy(wpa_ie + wpa_ie_len,
+ wpabuf_head(hs20), wpabuf_len(hs20));
+ wpa_ie_len += wpabuf_len(hs20);
+ }
wpabuf_free(hs20);
}
}
#endif /* CONFIG_HS20 */
- ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
- if (ext_capab_len > 0) {
- u8 *pos = wpa_ie;
- if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
- pos += 2 + pos[1];
- os_memmove(pos + ext_capab_len, pos,
- wpa_ie_len - (pos - wpa_ie));
- wpa_ie_len += ext_capab_len;
- os_memcpy(pos, ext_capab, ext_capab_len);
+ /*
+ * Workaround: Add Extended Capabilities element only if the AP
+ * included this element in Beacon/Probe Response frames. Some older
+ * APs seem to have interoperability issues if this element is
+ * included, so while the standard may require us to include the
+ * element in all cases, it is justifiable to skip it to avoid
+ * interoperability issues.
+ */
+ if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
+ u8 ext_capab[18];
+ int ext_capab_len;
+ ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+ sizeof(ext_capab));
+ if (ext_capab_len > 0) {
+ u8 *pos = wpa_ie;
+ if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+ pos += 2 + pos[1];
+ os_memmove(pos + ext_capab_len, pos,
+ wpa_ie_len - (pos - wpa_ie));
+ wpa_ie_len += ext_capab_len;
+ os_memcpy(pos, ext_capab, ext_capab_len);
+ }
}
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
- cipher_pairwise = wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
- cipher_group = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
+ cipher_pairwise = wpa_s->pairwise_cipher;
+ cipher_group = wpa_s->group_cipher;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
@@ -1533,7 +1731,7 @@
/* Assume that dynamic WEP-104 keys will be used and
* set cipher suites in order for drivers to expect
* encryption. */
- cipher_pairwise = cipher_group = CIPHER_WEP104;
+ cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
}
}
#endif /* IEEE8021X_EAPOL */
@@ -1556,6 +1754,8 @@
params.bssid = bss->bssid;
params.freq = bss->freq;
}
+ params.bssid_hint = bss->bssid;
+ params.freq_hint = bss->freq;
} else {
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
@@ -1570,11 +1770,19 @@
if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 &&
params.freq == 0)
params.freq = ssid->frequency; /* Initial channel for IBSS */
+
+ if (ssid->mode == WPAS_MODE_IBSS) {
+ if (ssid->beacon_int)
+ params.beacon_int = ssid->beacon_int;
+ else
+ params.beacon_int = wpa_s->conf->beacon_int;
+ }
+
params.wpa_ie = wpa_ie;
params.wpa_ie_len = wpa_ie_len;
params.pairwise_suite = cipher_pairwise;
params.group_suite = cipher_group;
- params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+ params.key_mgmt_suite = wpa_s->key_mgmt;
params.wpa_proto = wpa_s->wpa_proto;
params.auth_alg = algs;
params.mode = ssid->mode;
@@ -1587,8 +1795,8 @@
params.wep_tx_keyidx = ssid->wep_tx_keyidx;
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
- (params.key_mgmt_suite == KEY_MGMT_PSK ||
- params.key_mgmt_suite == KEY_MGMT_FT_PSK)) {
+ (params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
params.passphrase = ssid->passphrase;
if (ssid->psk_set)
params.psk = ssid->psk;
@@ -1628,19 +1836,35 @@
params.htcaps_mask = (u8 *) &htcaps_mask;
wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ os_memset(&vhtcaps, 0, sizeof(vhtcaps));
+ os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
+ params.vhtcaps = &vhtcaps;
+ params.vhtcaps_mask = &vhtcaps_mask;
+ wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, ¶ms);
+#endif /* CONFIG_VHT_OVERRIDES */
-#ifdef ANDROID_P2P
- /* If multichannel concurrency is not supported, check for any frequency
- * conflict and take appropriate action.
+#ifdef CONFIG_P2P
+ /*
+ * If multi-channel concurrency is not supported, check for any
+ * frequency conflict. In case of any frequency conflict, remove the
+ * least prioritized connection.
*/
- if ((wpa_s->num_multichan_concurrent < 2) &&
- ((freq = wpa_drv_shared_freq(wpa_s)) > 0) && (freq != params.freq)) {
- wpa_printf(MSG_DEBUG, "Shared interface with conflicting frequency found (%d != %d)"
- , freq, params.freq);
- if (wpas_p2p_handle_frequency_conflicts(wpa_s, params.freq, ssid) < 0)
- return;
+ if (wpa_s->num_multichan_concurrent < 2) {
+ int freq, num;
+ num = get_shared_radio_freqs(wpa_s, &freq, 1);
+ if (num > 0 && freq > 0 && freq != params.freq) {
+ wpa_printf(MSG_DEBUG,
+ "Assoc conflicting freq found (%d != %d)",
+ freq, params.freq);
+ if (wpas_p2p_handle_frequency_conflicts(wpa_s,
+ params.freq,
+ ssid) < 0)
+ return;
+ }
}
-#endif
+#endif /* CONFIG_P2P */
+
ret = wpa_drv_associate(wpa_s, ¶ms);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -1952,6 +2176,59 @@
/**
+ * wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @pkcs11_engine_path: PKCS #11 engine path or NULL
+ * @pkcs11_module_path: PKCS #11 module path or NULL
+ * Returns: 0 on success; -1 on failure
+ *
+ * Sets the PKCS #11 engine and module path. Both have to be NULL or a valid
+ * path. If resetting the EAPOL state machine with the new PKCS #11 engine and
+ * module path fails the paths will be reset to the default value (NULL).
+ */
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+ const char *pkcs11_engine_path,
+ const char *pkcs11_module_path)
+{
+ char *pkcs11_engine_path_copy = NULL;
+ char *pkcs11_module_path_copy = NULL;
+
+ if (pkcs11_engine_path != NULL) {
+ pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path);
+ if (pkcs11_engine_path_copy == NULL)
+ return -1;
+ }
+ if (pkcs11_module_path != NULL) {
+ pkcs11_module_path_copy = os_strdup(pkcs11_module_path);
+ if (pkcs11_module_path_copy == NULL) {
+ os_free(pkcs11_engine_path_copy);
+ return -1;
+ }
+ }
+
+ os_free(wpa_s->conf->pkcs11_engine_path);
+ os_free(wpa_s->conf->pkcs11_module_path);
+ wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy;
+ wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy;
+
+ wpa_sm_set_eapol(wpa_s->wpa, NULL);
+ eapol_sm_deinit(wpa_s->eapol);
+ wpa_s->eapol = NULL;
+ if (wpa_supplicant_init_eapol(wpa_s)) {
+ /* Error -> Reset paths to the default value (NULL) once. */
+ if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL)
+ wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL,
+ NULL);
+
+ return -1;
+ }
+ wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+ return 0;
+}
+
+
+/**
* wpa_supplicant_set_ap_scan - Set AP scan mode for interface
* @wpa_s: wpa_supplicant structure for a network interface
* @ap_scan: AP scan mode
@@ -2245,6 +2522,16 @@
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
+#ifdef CONFIG_PEERKEY
+ if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
+ wpa_s->current_ssid->peerkey &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+ wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key");
+ return;
+ }
+#endif /* CONFIG_PEERKEY */
+
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
(wpa_s->last_eapol_matches_bssid &&
#ifdef CONFIG_AP
@@ -2270,7 +2557,7 @@
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
if (wpa_s->pending_eapol_rx) {
- os_get_time(&wpa_s->pending_eapol_rx_time);
+ os_get_reltime(&wpa_s->pending_eapol_rx_time);
os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
ETH_ALEN);
}
@@ -2455,9 +2742,15 @@
wpa_s->prev_scan_wildcard = 0;
if (wpa_supplicant_enabled_networks(wpa_s)) {
- if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count,
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ interface_count = 0;
+ }
+ if (!wpa_s->p2p_mgmt &&
+ wpa_supplicant_delayed_sched_scan(wpa_s,
+ interface_count % 3,
100000))
- wpa_supplicant_req_scan(wpa_s, interface_count,
+ wpa_supplicant_req_scan(wpa_s, interface_count % 3,
100000);
interface_count++;
} else
@@ -2552,7 +2845,7 @@
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
- u16 msk;
+ le16 msk;
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
@@ -2625,8 +2918,8 @@
int disabled)
{
/* Masking these out disables HT40 */
- u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
- HT_CAP_INFO_SHORT_GI40MHZ);
+ le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+ HT_CAP_INFO_SHORT_GI40MHZ);
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
@@ -2647,8 +2940,8 @@
int disabled)
{
/* Masking these out disables SGI */
- u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
- HT_CAP_INFO_SHORT_GI40MHZ);
+ le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
+ HT_CAP_INFO_SHORT_GI40MHZ);
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled);
@@ -2663,6 +2956,27 @@
}
+static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int disabled)
+{
+ /* Masking these out disables LDPC */
+ le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP);
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled);
+
+ if (disabled)
+ htcaps->ht_capabilities_info &= ~msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+
+ htcaps_mask->ht_capabilities_info |= msk;
+
+ return 0;
+}
+
+
void wpa_supplicant_apply_ht_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params)
@@ -2686,6 +3000,13 @@
wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
+ wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc);
+
+ if (ssid->ht40_intolerant) {
+ le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT);
+ htcaps->ht_capabilities_info |= bit;
+ htcaps_mask->ht_capabilities_info |= bit;
+ }
}
#endif /* CONFIG_HT_OVERRIDES */
@@ -2698,6 +3019,10 @@
{
struct ieee80211_vht_capabilities *vhtcaps;
struct ieee80211_vht_capabilities *vhtcaps_mask;
+#ifdef CONFIG_HT_OVERRIDES
+ int max_ampdu;
+ const u32 max_ampdu_mask = VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX;
+#endif /* CONFIG_HT_OVERRIDES */
if (!ssid)
return;
@@ -2713,6 +3038,20 @@
vhtcaps->vht_capabilities_info = ssid->vht_capa;
vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
+#ifdef CONFIG_HT_OVERRIDES
+ /* if max ampdu is <= 3, we have to make the HT cap the same */
+ if (ssid->vht_capa_mask & max_ampdu_mask) {
+ max_ampdu = (ssid->vht_capa & max_ampdu_mask) >>
+ find_first_bit(max_ampdu_mask);
+
+ max_ampdu = max_ampdu < 3 ? max_ampdu : 3;
+ wpa_set_ampdu_factor(wpa_s,
+ (void *) params->htcaps,
+ (void *) params->htcaps_mask,
+ max_ampdu);
+ }
+#endif /* CONFIG_HT_OVERRIDES */
+
#define OVERRIDE_MCS(i) \
if (ssid->vht_tx_mcs_nss_ ##i >= 0) { \
vhtcaps_mask->vht_supported_mcs_set.tx_map |= \
@@ -2747,7 +3086,7 @@
if (!wpa_s->conf->pcsc_reader)
return 0;
- wpa_s->scard = scard_init(SCARD_TRY_BOTH, wpa_s->conf->pcsc_reader);
+ wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
if (!wpa_s->scard)
return 1;
@@ -2813,10 +3152,329 @@
}
+static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_capa *capa)
+{
+ struct wowlan_triggers *triggers;
+ int ret = 0;
+
+ if (!wpa_s->conf->wowlan_triggers)
+ return 0;
+
+ triggers = wpa_get_wowlan_triggers(wpa_s->conf->wowlan_triggers, capa);
+ if (triggers) {
+ ret = wpa_drv_wowlan(wpa_s, triggers);
+ os_free(triggers);
+ }
+ return ret;
+}
+
+
+static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
+ const char *rn)
+{
+ struct wpa_supplicant *iface = wpa_s->global->ifaces;
+ struct wpa_radio *radio;
+
+ while (rn && iface) {
+ radio = iface->radio;
+ if (radio && os_strcmp(rn, radio->name) == 0) {
+ wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s",
+ wpa_s->ifname, rn);
+ dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+ return radio;
+ }
+
+ iface = iface->next;
+ }
+
+ wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s",
+ wpa_s->ifname, rn ? rn : "N/A");
+ radio = os_zalloc(sizeof(*radio));
+ if (radio == NULL)
+ return NULL;
+
+ if (rn)
+ os_strlcpy(radio->name, rn, sizeof(radio->name));
+ dl_list_init(&radio->ifaces);
+ dl_list_init(&radio->work);
+ dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+
+ return radio;
+}
+
+
+static void radio_work_free(struct wpa_radio_work *work)
+{
+ if (work->wpa_s->scan_work == work) {
+ /* This should not really happen. */
+ wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work",
+ work->type, work, work->started);
+ work->wpa_s->scan_work = NULL;
+ }
+
+#ifdef CONFIG_P2P
+ if (work->wpa_s->p2p_scan_work == work) {
+ /* This should not really happen. */
+ wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work",
+ work->type, work, work->started);
+ work->wpa_s->p2p_scan_work = NULL;
+ }
+#endif /* CONFIG_P2P */
+
+ dl_list_del(&work->list);
+ os_free(work);
+}
+
+
+static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_radio *radio = eloop_ctx;
+ struct wpa_radio_work *work;
+ struct os_reltime now, diff;
+ struct wpa_supplicant *wpa_s;
+
+ work = dl_list_first(&radio->work, struct wpa_radio_work, list);
+ if (work == NULL)
+ return;
+
+ if (work->started)
+ return; /* already started and still in progress */
+
+ wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
+ radio_list);
+ if (wpa_s && wpa_s->external_scan_running) {
+ wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+ return;
+ }
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &work->time, &diff);
+ wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+ work->type, work, diff.sec, diff.usec);
+ work->started = 1;
+ work->time = now;
+ work->cb(work, 0);
+}
+
+
+/*
+ * This function removes both started and pending radio works running on
+ * the provided interface's radio.
+ * Prior to the removal of the radio work, its callback (cb) is called with
+ * deinit set to be 1. Each work's callback is responsible for clearing its
+ * internal data and restoring to a correct state.
+ * @wpa_s: wpa_supplicant data
+ * @type: type of works to be removed
+ * @remove_all: 1 to remove all the works on this radio, 0 to remove only
+ * this interface's works.
+ */
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+ const char *type, int remove_all)
+{
+ struct wpa_radio_work *work, *tmp;
+ struct wpa_radio *radio = wpa_s->radio;
+
+ dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work,
+ list) {
+ if (type && os_strcmp(type, work->type) != 0)
+ continue;
+
+ /* skip other ifaces' works */
+ if (!remove_all && work->wpa_s != wpa_s)
+ continue;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s",
+ work->type, work, work->started ? " (started)" : "");
+ work->cb(work, 1);
+ radio_work_free(work);
+ }
+
+ /* in case we removed the started work */
+ radio_work_check_next(wpa_s);
+}
+
+
+static void radio_remove_interface(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_radio *radio = wpa_s->radio;
+
+ if (!radio)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s",
+ wpa_s->ifname, radio->name);
+ dl_list_del(&wpa_s->radio_list);
+ radio_remove_works(wpa_s, NULL, 0);
+ wpa_s->radio = NULL;
+ if (!dl_list_empty(&radio->ifaces))
+ return; /* Interfaces remain for this radio */
+
+ wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name);
+ eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+ os_free(radio);
+}
+
+
+void radio_work_check_next(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_radio *radio = wpa_s->radio;
+
+ if (dl_list_empty(&radio->work))
+ return;
+ eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+ eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL);
+}
+
+
+/**
+ * radio_add_work - Add a radio work item
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency of the offchannel operation in MHz or 0
+ * @type: Unique identifier for each type of work
+ * @next: Force as the next work to be executed
+ * @cb: Callback function for indicating when radio is available
+ * @ctx: Context pointer for the work (work->ctx in cb())
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to request time for an operation that requires
+ * exclusive radio control. Once the radio is available, the registered callback
+ * function will be called. radio_work_done() must be called once the exclusive
+ * radio operation has been completed, so that the radio is freed for other
+ * operations. The special case of deinit=1 is used to free the context data
+ * during interface removal. That does not allow the callback function to start
+ * the radio operation, i.e., it must free any resources allocated for the radio
+ * work and return.
+ *
+ * The @freq parameter can be used to indicate a single channel on which the
+ * offchannel operation will occur. This may allow multiple radio work
+ * operations to be performed in parallel if they apply for the same channel.
+ * Setting this to 0 indicates that the work item may use multiple channels or
+ * requires exclusive control of the radio.
+ */
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const char *type, int next,
+ void (*cb)(struct wpa_radio_work *work, int deinit),
+ void *ctx)
+{
+ struct wpa_radio_work *work;
+ int was_empty;
+
+ work = os_zalloc(sizeof(*work));
+ if (work == NULL)
+ return -1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work);
+ os_get_reltime(&work->time);
+ work->freq = freq;
+ work->type = type;
+ work->wpa_s = wpa_s;
+ work->cb = cb;
+ work->ctx = ctx;
+
+ was_empty = dl_list_empty(&wpa_s->radio->work);
+ if (next)
+ dl_list_add(&wpa_s->radio->work, &work->list);
+ else
+ dl_list_add_tail(&wpa_s->radio->work, &work->list);
+ if (was_empty) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
+ radio_work_check_next(wpa_s);
+ }
+
+ return 0;
+}
+
+
+/**
+ * radio_work_done - Indicate that a radio work item has been completed
+ * @work: Completed work
+ *
+ * This function is called once the callback function registered with
+ * radio_add_work() has completed its work.
+ */
+void radio_work_done(struct wpa_radio_work *work)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct os_reltime now, diff;
+ unsigned int started = work->started;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &work->time, &diff);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds",
+ work->type, work, started ? "done" : "canceled",
+ diff.sec, diff.usec);
+ radio_work_free(work);
+ if (started)
+ radio_work_check_next(wpa_s);
+}
+
+
+int radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
+{
+ struct wpa_radio_work *work;
+ struct wpa_radio *radio = wpa_s->radio;
+
+ dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+ if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpas_init_driver(struct wpa_supplicant *wpa_s,
+ struct wpa_interface *iface)
+{
+ const char *ifname, *driver, *rn;
+
+ driver = iface->driver;
+next_driver:
+ if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+ return -1;
+
+ wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
+ if (wpa_s->drv_priv == NULL) {
+ const char *pos;
+ pos = driver ? os_strchr(driver, ',') : NULL;
+ if (pos) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+ "driver interface - try next driver wrapper");
+ driver = pos + 1;
+ goto next_driver;
+ }
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
+ "interface");
+ return -1;
+ }
+ if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
+ "driver_param '%s'", wpa_s->conf->driver_param);
+ return -1;
+ }
+
+ ifname = wpa_drv_get_ifname(wpa_s);
+ if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
+ "interface name with '%s'", ifname);
+ os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+ }
+
+ rn = wpa_driver_get_radio_name(wpa_s);
+ if (rn && rn[0] == '\0')
+ rn = NULL;
+
+ wpa_s->radio = radio_add_interface(wpa_s, rn);
+ if (wpa_s->radio == NULL)
+ return -1;
+
+ return 0;
+}
+
+
static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
struct wpa_interface *iface)
{
- const char *ifname, *driver;
struct wpa_driver_capa capa;
wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
@@ -2849,6 +3507,13 @@
wpa_s->confanother = os_rel2abs_path(iface->confanother);
wpa_config_read(wpa_s->confanother, wpa_s->conf);
+#ifdef CONFIG_P2P
+ wpa_s->conf_p2p_dev = os_rel2abs_path(iface->conf_p2p_dev);
+#ifndef ANDROID_P2P
+ wpa_config_read(wpa_s->conf_p2p_dev, wpa_s->conf);
+#endif
+#endif /* CONFIG_P2P */
+
/*
* Override ctrl_interface and driver_param if set on command
* line.
@@ -2908,38 +3573,9 @@
* L2 receive handler so that association events are processed before
* EAPOL-Key packets if both become available for the same select()
* call. */
- driver = iface->driver;
-next_driver:
- if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+ if (wpas_init_driver(wpa_s, iface) < 0)
return -1;
- wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
- if (wpa_s->drv_priv == NULL) {
- const char *pos;
- pos = driver ? os_strchr(driver, ',') : NULL;
- if (pos) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
- "driver interface - try next driver wrapper");
- driver = pos + 1;
- goto next_driver;
- }
- wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
- "interface");
- return -1;
- }
- if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
- wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
- "driver_param '%s'", wpa_s->conf->driver_param);
- return -1;
- }
-
- ifname = wpa_drv_get_ifname(wpa_s);
- if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
- "interface name with '%s'", ifname);
- os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
- }
-
if (wpa_supplicant_init_wpa(wpa_s) < 0)
return -1;
@@ -3053,20 +3689,27 @@
return -1;
}
-#ifdef CONFIG_P2P
if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
return -1;
}
-#endif /* CONFIG_P2P */
if (wpa_bss_init(wpa_s) < 0)
return -1;
+ /*
+ * Set Wake-on-WLAN triggers, if configured.
+ * Note: We don't restore/remove the triggers on shutdown (it doesn't
+ * have effect anyway when the interface is down).
+ */
+ if (wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+ return -1;
+
#ifdef CONFIG_EAP_PROXY
{
size_t len;
- wpa_s->mnc_len = eap_proxy_get_imsi(wpa_s->imsi, &len);
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi,
+ &len);
if (wpa_s->mnc_len > 0) {
wpa_s->imsi[len] = '\0';
wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
@@ -3100,14 +3743,10 @@
}
wpa_supplicant_cleanup(wpa_s);
+ wpas_p2p_deinit_iface(wpa_s);
-#ifdef CONFIG_P2P
- if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
- "the management interface is being removed");
- wpas_p2p_deinit_global(wpa_s->global);
- }
-#endif /* CONFIG_P2P */
+ wpas_ctrl_radio_work_flush(wpa_s);
+ radio_remove_interface(wpa_s);
if (wpa_s->drv_priv)
wpa_drv_deinit(wpa_s);
@@ -3502,7 +4141,8 @@
os_free(global->params.override_driver);
os_free(global->params.override_ctrl_interface);
- os_free(global->p2p_disallow_freq);
+ os_free(global->p2p_disallow_freq.range);
+ os_free(global->p2p_go_avoid_freq.range);
os_free(global->add_psk);
os_free(global);
@@ -3532,11 +4172,7 @@
#ifdef CONFIG_WPS
wpas_wps_update_config(wpa_s);
#endif /* CONFIG_WPS */
-
-#ifdef CONFIG_P2P
wpas_p2p_update_config(wpa_s);
-#endif /* CONFIG_P2P */
-
wpa_s->conf->changed_parameters = 0;
}
@@ -3595,6 +4231,8 @@
int count;
int *freqs = NULL;
+ wpas_connect_work_done(wpa_s);
+
/*
* Remove possible authentication timeout since the connection failed.
*/
@@ -3651,7 +4289,7 @@
if (count > 3 && wpa_s->current_ssid) {
wpa_printf(MSG_DEBUG, "Continuous association failures - "
"consider temporary network disabling");
- wpas_auth_failed(wpa_s);
+ wpas_auth_failed(wpa_s, "CONN_FAILED");
}
switch (count) {
@@ -3681,8 +4319,6 @@
*/
wpa_supplicant_req_scan(wpa_s, timeout / 1000,
1000 * (timeout % 1000));
-
- wpas_p2p_continue_after_scan(wpa_s);
}
@@ -3716,7 +4352,7 @@
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_EAP_PASSWORD:
- os_free(eap->password);
+ bin_clear_free(eap->password, eap->password_len);
eap->password = (u8 *) os_strdup(value);
eap->password_len = os_strlen(value);
eap->pending_req_password = 0;
@@ -3724,7 +4360,7 @@
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
- os_free(eap->new_password);
+ bin_clear_free(eap->new_password, eap->new_password_len);
eap->new_password = (u8 *) os_strdup(value);
eap->new_password_len = os_strlen(value);
eap->pending_req_new_password = 0;
@@ -3732,14 +4368,14 @@
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_EAP_PIN:
- os_free(eap->pin);
+ str_clear_free(eap->pin);
eap->pin = os_strdup(value);
eap->pending_req_pin = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_EAP_OTP:
- os_free(eap->otp);
+ bin_clear_free(eap->otp, eap->otp_len);
eap->otp = (u8 *) os_strdup(value);
eap->otp_len = os_strlen(value);
os_free(eap->pending_req_otp);
@@ -3747,12 +4383,16 @@
eap->pending_req_otp_len = 0;
break;
case WPA_CTRL_REQ_EAP_PASSPHRASE:
- os_free(eap->private_key_passwd);
- eap->private_key_passwd = (u8 *) os_strdup(value);
+ str_clear_free(eap->private_key_passwd);
+ eap->private_key_passwd = os_strdup(value);
eap->pending_req_passphrase = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
break;
+ case WPA_CTRL_REQ_SIM:
+ str_clear_free(eap->external_sim_resp);
+ eap->external_sim_resp = os_strdup(value);
+ break;
default:
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
return -1;
@@ -3797,7 +4437,7 @@
}
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
- !ssid->ext_psk)
+ (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk)
return 1;
return 0;
@@ -3814,11 +4454,11 @@
}
-void wpas_auth_failed(struct wpa_supplicant *wpa_s)
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
int dur;
- struct os_time now;
+ struct os_reltime now;
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "Authentication failure but no known "
@@ -3844,27 +4484,33 @@
if (ssid->auth_failures > 50)
dur = 300;
- else if (ssid->auth_failures > 20)
- dur = 120;
else if (ssid->auth_failures > 10)
- dur = 60;
+ dur = 120;
else if (ssid->auth_failures > 5)
+ dur = 90;
+ else if (ssid->auth_failures > 3)
+ dur = 60;
+ else if (ssid->auth_failures > 2)
dur = 30;
else if (ssid->auth_failures > 1)
dur = 20;
else
dur = 10;
- os_get_time(&now);
+ if (ssid->auth_failures > 1 &&
+ wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt))
+ dur += os_random() % (ssid->auth_failures * 10);
+
+ os_get_reltime(&now);
if (now.sec + dur <= ssid->disabled_until.sec)
return;
ssid->disabled_until.sec = now.sec + dur;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
- "id=%d ssid=\"%s\" auth_failures=%u duration=%d",
+ "id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s",
ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
- ssid->auth_failures, dur);
+ ssid->auth_failures, dur, reason);
}
@@ -3943,90 +4589,44 @@
}
-/**
- * wpas_wpa_is_in_progress - Check whether a connection is in progress
- * @wpa_s: Pointer to wpa_supplicant data
- *
- * This function is to check if the wpa state is in beginning of the connection
- * during 4-way handshake or group key handshake with WPA on any shared
- * interface.
- */
-int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s)
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+ struct wpa_used_freq_data *freqs_data,
+ unsigned int len)
{
- const char *rn, *rn2;
- struct wpa_supplicant *ifs;
+ unsigned int i;
- if (!wpa_s->driver->get_radio_name)
- return 0;
-
- rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
- if (rn == NULL || rn[0] == '\0')
- return 0;
-
- for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
- if (ifs == wpa_s || !ifs->driver->get_radio_name)
- continue;
-
- rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
- if (!rn2 || os_strcmp(rn, rn2) != 0)
- continue;
- if (ifs->wpa_state >= WPA_AUTHENTICATING &&
- ifs->wpa_state != WPA_COMPLETED) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Connection is in progress "
- "on interface %s - defer scan", ifs->ifname);
- return 1;
- }
+ wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s",
+ len, title);
+ for (i = 0; i < len; i++) {
+ struct wpa_used_freq_data *cur = &freqs_data[i];
+ wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X",
+ i, cur->freq, cur->flags);
}
-
- return 0;
}
/*
* Find the operating frequencies of any of the virtual interfaces that
- * are using the same radio as the current interface.
+ * are using the same radio as the current interface, and in addition, get
+ * information about the interface types that are using the frequency.
*/
-int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
- int *freq_array, unsigned int len)
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs_data,
+ unsigned int len)
{
- const char *rn, *rn2;
struct wpa_supplicant *ifs;
u8 bssid[ETH_ALEN];
int freq;
unsigned int idx = 0, i;
- os_memset(freq_array, 0, sizeof(int) * len);
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Determining shared radio frequencies (max len %u)", len);
+ os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len);
- /* First add the frequency of the local interface */
- if (wpa_s->current_ssid != NULL && wpa_s->assoc_freq != 0) {
- if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
- wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)
- freq_array[idx++] = wpa_s->current_ssid->frequency;
- else if (wpa_drv_get_bssid(wpa_s, bssid) == 0)
- freq_array[idx++] = wpa_s->assoc_freq;
- }
-
- /* If get_radio_name is not supported, use only the local freq */
- if (!wpa_s->driver->get_radio_name) {
- freq = wpa_drv_shared_freq(wpa_s);
- if (freq > 0 && idx < len &&
- (idx == 0 || freq_array[0] != freq))
- freq_array[idx++] = freq;
- return idx;
- }
-
- rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
- if (rn == NULL || rn[0] == '\0')
- return idx;
-
- for (ifs = wpa_s->global->ifaces, idx = 0; ifs && idx < len;
- ifs = ifs->next) {
- if (wpa_s == ifs || !ifs->driver->get_radio_name)
- continue;
-
- rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
- if (!rn2 || os_strcmp(rn, rn2) != 0)
- continue;
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (idx == len)
+ break;
if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
continue;
@@ -4041,11 +4641,45 @@
/* Hold only distinct freqs */
for (i = 0; i < idx; i++)
- if (freq_array[i] == freq)
+ if (freqs_data[i].freq == freq)
break;
if (i == idx)
- freq_array[idx++] = freq;
+ freqs_data[idx++].freq = freq;
+
+ if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
+ freqs_data[i].flags = ifs->current_ssid->p2p_group ?
+ WPA_FREQ_USED_BY_P2P_CLIENT :
+ WPA_FREQ_USED_BY_INFRA_STATION;
+ }
}
+
+ dump_freq_data(wpa_s, "completed iteration", freqs_data, idx);
return idx;
}
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * are using the same radio as the current interface.
+ */
+int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
+ int *freq_array, unsigned int len)
+{
+ struct wpa_used_freq_data *freqs_data;
+ int num, i;
+
+ os_memset(freq_array, 0, sizeof(int) * len);
+
+ freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data));
+ if (!freqs_data)
+ return -1;
+
+ num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+ for (i = 0; i < num; i++)
+ freq_array[i] = freqs_data[i].freq;
+
+ os_free(freqs_data);
+
+ return num;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index d73d371..2a0dc20 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -81,6 +81,8 @@
# to make wpa_supplicant interoperate with these APs, the version number is set
# to 1 by default. This configuration value can be used to set it to the new
# version (2).
+# Note: When using MACsec, eapol_version shall be set to 3, which is
+# defined in IEEE Std 802.1X-2010.
eapol_version=1
# AP scanning/selection
@@ -97,6 +99,8 @@
# non-WPA drivers when using IEEE 802.1X mode; do not try to associate with
# APs (i.e., external program needs to control association). This mode must
# also be used when using wired Ethernet drivers.
+# Note: macsec_qca driver is one type of Ethernet driver which implements
+# macsec feature.
# 2: like 0, but associate with APs using security policy and SSID (but not
# BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to
# enable operation with hidden SSIDs and optimized roaming; in this mode,
@@ -241,14 +245,14 @@
# This is an optional set of parameters for automatic scanning
# within an interface in following format:
#autoscan=<autoscan module name>:<module parameters>
-# autoscan is like bgscan but on disconnected or inactive state.
-# For instance, on exponential module parameters would be <base>:<limit>
+# autoscan is like bgscan but on disconnected or inactive state.
+# For instance, on exponential module parameters would be <base>:<limit>
#autoscan=exponential:3:300
# Which means a delay between scans on a base exponential of 3,
-# up to the limit of 300 seconds (3, 9, 27 ... 300)
-# For periodic module, parameters would be <fixed interval>
+# up to the limit of 300 seconds (3, 9, 27 ... 300)
+# For periodic module, parameters would be <fixed interval>
#autoscan=periodic:30
-# So a delay of 30 seconds will be applied between each scan
+# So a delay of 30 seconds will be applied between each scan
# filter_ssids - SSID-based scan result filtering
# 0 = do not filter scan results (default)
@@ -265,6 +269,19 @@
# inactive stations.
#p2p_go_max_inactivity=300
+# Passphrase length (8..63) for P2P GO
+#
+# This parameter controls the length of the random passphrase that is
+# generated at the GO. Default: 8.
+#p2p_passphrase_len=8
+
+# Extra delay between concurrent P2P search iterations
+#
+# This value adds extra delay in milliseconds between concurrent search
+# iterations to make p2p_find friendlier to concurrent operations by avoiding
+# it from taking 100% of radio resources. The default value is 500 ms.
+#p2p_search_delay=500
+
# Opportunistic Key Caching (also known as Proactive Key Caching) default
# This parameter can be used to set the default behavior for the
# proactive_key_caching parameter. By default, OKC is disabled unless enabled
@@ -342,6 +359,8 @@
#
# credential fields:
#
+# temporary: Whether this credential is temporary and not to be saved
+#
# priority: Priority group
# By default, all networks and credentials get the same priority group
# (0). This field can be used to give higher priority for credentials
@@ -399,9 +418,11 @@
# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
# format
#
-# domain: Home service provider FQDN
+# domain: Home service provider FQDN(s)
# This is used to compare against the Domain Name List to figure out
-# whether the AP is operated by the Home SP.
+# whether the AP is operated by the Home SP. Multiple domain entries can
+# be used to configure alternative FQDNs that will be considered home
+# networks.
#
# roaming_consortium: Roaming Consortium OI
# If roaming_consortium_len is non-zero, this field contains the
@@ -428,6 +449,59 @@
# matching with the network. Multiple entries can be used to specify more
# than one SSID.
#
+# roaming_partner: Roaming partner information
+# This optional field can be used to configure preferences between roaming
+# partners. The field is a string in following format:
+# <FQDN>,<0/1 exact match>,<priority>,<* or country code>
+# (non-exact match means any subdomain matches the entry; priority is in
+# 0..255 range with 0 being the highest priority)
+#
+# update_identifier: PPS MO ID
+# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# provisioning_sp: FQDN of the SP that provisioned the credential
+# This optional field can be used to keep track of the SP that provisioned
+# the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
+#
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+# These fields can be used to specify minimum download/upload backhaul
+# bandwidth that is preferred for the credential. This constraint is
+# ignored if the AP does not advertise WAN Metrics information or if the
+# limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
+# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
+# (PPS/<X+>/Policy/MaximumBSSLoadValue)
+# This value is used as the maximum channel utilization for network
+# selection purposes for home networks. If the AP does not advertise
+# BSS Load or if the limit would prevent any connection, this constraint
+# will be ignored.
+#
+# req_conn_capab: Required connection capability
+# (PPS/<X+>/Policy/RequiredProtoPortTuple)
+# This value is used to configure set of required protocol/port pairs that
+# a roaming network shall support (include explicitly in Connection
+# Capability ANQP element). This constraint is ignored if the AP does not
+# advertise Connection Capability or if this constraint would prevent any
+# network connection. This policy is not used in home networks.
+# Format: <protocol>[:<comma-separated list of ports]
+# Multiple entries can be used to list multiple requirements.
+# For example, number of common TCP protocols:
+# req_conn_capab=6,22,80,443
+# For example, IPSec/IKE:
+# req_conn_capab=17:500
+# req_conn_capab=50
+#
+# 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
+#
+# sim_num: Identifier for which SIM to use in multi-SIM devices
+#
# for example:
#
#cred={
@@ -504,9 +578,10 @@
# 0 = infrastructure (Managed) mode, i.e., associate with an AP (default)
# 1 = IBSS (ad-hoc, peer-to-peer)
# 2 = AP (access point)
-# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP)
-# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). WPA-None requires
-# following network block options:
+# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) and
+# WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE (fixed group key
+# TKIP/CCMP) is available for backwards compatibility, but its use is
+# deprecated. WPA-None requires following network block options:
# proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not
# both), and psk must also be set.
#
@@ -547,6 +622,12 @@
# bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>:
# <long interval>[:<database file name>]"
# bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan"
+# Explicitly disable bgscan by setting
+# bgscan=""
+#
+# This option can also be set outside of all network blocks for the bgscan
+# parameter to apply for all the networks that have no specific bgscan
+# parameter.
#
# proto: list of accepted protocols
# WPA = WPA/IEEE 802.11i/D3.0
@@ -611,8 +692,16 @@
# bit0 (1): require dynamically generated unicast WEP key
# bit1 (2): require dynamically generated broadcast WEP key
# (3 = require both keys; default)
-# Note: When using wired authentication, eapol_flags must be set to 0 for the
-# authentication to be completed successfully.
+# Note: When using wired authentication (including macsec_qca driver),
+# eapol_flags must be set to 0 for the authentication to be completed
+# successfully.
+#
+# macsec_policy: IEEE 802.1X/MACsec options
+# This determines how sessions are secured with MACsec. It is currently
+# applicable only when using the macsec_qca driver interface.
+# 0: MACsec not in use (default)
+# 1: MACsec enabled - Should secure, accept key server's advice to
+# determine whether to use a secure session or not.
#
# mixed_cell: This option can be used to configure whether so called mixed
# cells, i.e., networks that use both plaintext and encryption in the same
@@ -791,6 +880,10 @@
# EAP workarounds are disabled with eap_workarounds=0.
# For EAP-FAST, this must be set to 0 (or left unconfigured for the
# default value to be used automatically).
+# tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers
+# that have issues interoperating with updated TLS version)
+# tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers
+# that have issues interoperating with updated TLS version)
#
# Following certificate/private key fields are used in inner Phase2
# authentication when using EAP-TTLS or EAP-PEAP.
@@ -881,6 +974,14 @@
# 0 = SGI enabled (if AP supports it)
# 1 = SGI disabled
#
+# disable_ldpc: Whether LDPC should be disabled.
+# 0 = LDPC enabled (if AP supports it)
+# 1 = LDPC disabled
+#
+# ht40_intolerant: Whether 40 MHz intolerant should be indicated.
+# 0 = 40 MHz tolerant (default)
+# 1 = 40 MHz intolerant
+#
# ht_mcs: Configure allowed MCS rates.
# Parsed as an array of bytes, in base-16 (ascii-hex)
# ht_mcs="" // Use all available (default)
@@ -892,6 +993,9 @@
# 0 = Enable MAX-AMSDU if hardware supports it.
# 1 = Disable AMSDU
#
+# ampdu_factor: Maximum A-MPDU Length Exponent
+# Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+#
# ampdu_density: Allow overriding AMPDU density configuration.
# Treated as hint by the kernel.
# -1 = Do not make any changes.
@@ -1157,7 +1261,19 @@
}
-# IBSS/ad-hoc network with WPA-None/TKIP.
+# IBSS/ad-hoc network with RSN
+network={
+ ssid="ibss-rsn"
+ key_mgmt=WPA-PSK
+ proto=RSN
+ psk="12345678"
+ mode=1
+ frequency=2412
+ pairwise=CCMP
+ group=CCMP
+}
+
+# IBSS/ad-hoc network with WPA-None/TKIP (deprecated)
network={
ssid="test adhoc"
mode=1
@@ -1250,3 +1366,17 @@
network={
key_mgmt=NONE
}
+
+
+# Example MACsec configuration
+#network={
+# key_mgmt=IEEE8021X
+# eap=TTLS
+# phase2="auth=PAP"
+# anonymous_identity="anonymous@example.com"
+# identity="user@example.com"
+# password="secretr"
+# ca_cert="/etc/cert/ca.pem"
+# eapol_flags=0
+# macsec_policy=1
+#}
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index d69cd61..bf3d19d 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1,6 +1,6 @@
/*
* wpa_supplicant - Internal definitions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -12,6 +12,8 @@
#include "utils/list.h"
#include "common/defs.h"
#include "common/sae.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_defs.h"
#include "config_ssid.h"
extern const char *wpa_supplicant_version;
@@ -63,6 +65,17 @@
*/
const char *confanother;
+#ifdef CONFIG_P2P
+ /**
+ * conf_p2p_dev - Additional configuration file used to hold the
+ * P2P Device configuration parameters.
+ *
+ * This can also be %NULL. In such a case, if a P2P Device dedicated
+ * interfaces is created, the main configuration file will be used.
+ */
+ const char *conf_p2p_dev;
+#endif /* CONFIG_P2P */
+
/**
* ctrl_interface - Control interface parameter
*
@@ -227,12 +240,6 @@
char *service;
};
-struct wpa_freq_range {
- unsigned int min;
- unsigned int max;
-};
-
-
/**
* struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
*
@@ -252,20 +259,20 @@
struct wpa_supplicant *p2p_group_formation;
struct wpa_supplicant *p2p_invite_group;
u8 p2p_dev_addr[ETH_ALEN];
- struct os_time p2p_go_wait_client;
+ struct os_reltime p2p_go_wait_client;
struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
int p2p_disabled;
int cross_connection;
- struct wpa_freq_range *p2p_disallow_freq;
- unsigned int num_p2p_disallow_freq;
+ struct wpa_freq_range_list p2p_disallow_freq;
+ struct wpa_freq_range_list p2p_go_avoid_freq;
enum wpa_conc_pref {
WPA_CONC_PREF_NOT_SET,
WPA_CONC_PREF_STA,
WPA_CONC_PREF_P2P
} conc_pref;
- unsigned int p2p_cb_on_scan_complete:1;
unsigned int p2p_per_sta_psk:1;
+ unsigned int p2p_fail_on_wps_complete:1;
#ifdef CONFIG_WIFI_DISPLAY
int wifi_display;
@@ -278,6 +285,61 @@
/**
+ * struct wpa_radio - Internal data for per-radio information
+ *
+ * This structure is used to share data about configured interfaces
+ * (struct wpa_supplicant) that share the same physical radio, e.g., to allow
+ * better coordination of offchannel operations.
+ */
+struct wpa_radio {
+ char name[16]; /* from driver_ops get_radio_name() or empty if not
+ * available */
+ struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
+ struct dl_list work; /* struct wpa_radio_work::list entries */
+};
+
+/**
+ * struct wpa_radio_work - Radio work item
+ */
+struct wpa_radio_work {
+ struct dl_list list;
+ unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */
+ const char *type;
+ struct wpa_supplicant *wpa_s;
+ void (*cb)(struct wpa_radio_work *work, int deinit);
+ void *ctx;
+ unsigned int started:1;
+ struct os_reltime time;
+};
+
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const char *type, int next,
+ void (*cb)(struct wpa_radio_work *work, int deinit),
+ void *ctx);
+void radio_work_done(struct wpa_radio_work *work);
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+ const char *type, int remove_all);
+void radio_work_check_next(struct wpa_supplicant *wpa_s);
+int radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
+
+struct wpa_connect_work {
+ unsigned int sme:1;
+ struct wpa_bss *bss;
+ struct wpa_ssid *ssid;
+};
+
+int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
+ struct wpa_ssid *test_ssid);
+void wpas_connect_work_free(struct wpa_connect_work *cwork);
+void wpas_connect_work_done(struct wpa_supplicant *wpa_s);
+
+struct wpa_external_work {
+ unsigned int id;
+ char type[100];
+ unsigned int timeout;
+};
+
+/**
* offchannel_send_action_result - Result of offchannel send Action frame
*/
enum offchannel_send_action_result {
@@ -296,7 +358,7 @@
WPS_AP_SEL_REG_OUR
} type;
unsigned int tries;
- struct os_time last_attempt;
+ struct os_reltime last_attempt;
};
struct wpa_ssid_value {
@@ -304,6 +366,14 @@
size_t ssid_len;
};
+#define WPA_FREQ_USED_BY_INFRA_STATION BIT(0)
+#define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1)
+
+struct wpa_used_freq_data {
+ int freq;
+ unsigned int flags;
+};
+
/**
* struct wpa_supplicant - Internal data for wpa_supplicant interface
*
@@ -314,6 +384,8 @@
*/
struct wpa_supplicant {
struct wpa_global *global;
+ struct wpa_radio *radio; /* shared radio context */
+ struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */
struct wpa_supplicant *parent;
struct wpa_supplicant *next;
struct l2_packet_data *l2;
@@ -334,9 +406,14 @@
char *confname;
char *confanother;
+
+#ifdef CONFIG_P2P
+ char *conf_p2p_dev;
+#endif /* CONFIG_P2P */
+
struct wpa_config *conf;
int countermeasures;
- os_time_t last_michael_mic_error;
+ struct os_reltime last_michael_mic_error;
u8 bssid[ETH_ALEN];
u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
* field contains the target BSSID. */
@@ -368,6 +445,9 @@
enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband;
+ /* Preferred network for the next connection attempt */
+ struct wpa_ssid *next_ssid;
+
/* previous scan was wildcard when interleaving between
* wildcard scans and specific SSID scan when max_ssids=1 */
int prev_scan_wildcard;
@@ -400,8 +480,7 @@
struct wpa_bss **last_scan_res;
unsigned int last_scan_res_used;
unsigned int last_scan_res_size;
- int last_scan_full;
- struct os_time last_scan;
+ struct os_reltime last_scan;
struct wpa_driver_ops *driver;
int interface_removed; /* whether the network interface has been
@@ -412,6 +491,7 @@
struct ctrl_iface_priv *ctrl_iface;
enum wpa_states wpa_state;
+ struct wpa_radio_work *scan_work;
int scanning;
int sched_scanning;
int new_connection;
@@ -425,7 +505,8 @@
unsigned char last_eapol_src[ETH_ALEN];
- int keys_cleared;
+ unsigned int keys_cleared; /* bitfield of key indexes that the driver is
+ * known not to be configured with a key */
struct wpa_blacklist *blacklist;
@@ -470,14 +551,27 @@
* to be run.
*/
MANUAL_SCAN_REQ
- } scan_req;
- struct os_time scan_trigger_time;
+ } scan_req, last_scan_req;
+ struct os_reltime scan_trigger_time, scan_start_time;
int scan_runs; /* number of scan runs since WPS was started */
int *next_scan_freqs;
+ int *manual_scan_freqs;
+ int *manual_sched_scan_freqs;
+ unsigned int manual_scan_passive:1;
+ unsigned int manual_scan_use_id:1;
+ unsigned int manual_scan_only_new:1;
+ unsigned int own_scan_requested:1;
+ unsigned int own_scan_running:1;
+ unsigned int external_scan_running:1;
+ unsigned int clear_driver_scan_cache:1;
+ unsigned int manual_scan_id;
int scan_interval; /* time in sec between scans to find suitable AP */
int normal_scans; /* normal scans run before sched_scan */
int scan_for_connection; /* whether the scan request was triggered for
* finding a connection */
+#define MAX_SCAN_ID 16
+ int scan_id[MAX_SCAN_ID];
+ unsigned int scan_id_count;
unsigned int drv_flags;
unsigned int drv_enc;
@@ -509,9 +603,11 @@
int blacklist_cleared;
struct wpabuf *pending_eapol_rx;
- struct os_time pending_eapol_rx_time;
+ struct os_reltime pending_eapol_rx_time;
u8 pending_eapol_rx_src[ETH_ALEN];
unsigned int last_eapol_matches_bssid:1;
+ unsigned int eap_expected_failure:1;
+ unsigned int reattach:1; /* reassociation to the same BSS requested */
struct ibss_rsn *ibss_rsn;
@@ -543,7 +639,8 @@
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_time sa_query_start;
+ struct os_reltime sa_query_start;
+ struct os_reltime last_unprot_disconnect;
u8 sched_obss_scan;
u16 obss_scan_int;
u16 bss_max_idle_period;
@@ -570,6 +667,7 @@
unsigned int pending_action_freq;
int pending_action_no_cck;
int pending_action_without_roc;
+ unsigned int pending_action_tx_done:1;
void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
@@ -603,6 +701,8 @@
u8 p2p_auth_invite[ETH_ALEN];
int p2p_sd_over_ctrl_iface;
int p2p_in_provisioning;
+ int p2p_in_invitation;
+ int p2p_invite_go_freq;
int pending_invite_ssid_id;
int show_group_started;
u8 go_dev_addr[ETH_ALEN];
@@ -610,6 +710,8 @@
u8 pending_join_iface_addr[ETH_ALEN];
u8 pending_join_dev_addr[ETH_ALEN];
int pending_join_wps_method;
+ u8 p2p_join_ssid[32];
+ size_t p2p_join_ssid_len;
int p2p_join_scan_count;
int auto_pd_scan_retry;
int force_long_sd;
@@ -639,20 +741,35 @@
*/
char cross_connect_uplink[100];
- unsigned int sta_scan_pending:1;
unsigned int p2p_auto_join:1;
unsigned int p2p_auto_pd:1;
unsigned int p2p_persistent_group:1;
unsigned int p2p_fallback_to_go_neg:1;
unsigned int p2p_pd_before_go_neg:1;
unsigned int p2p_go_ht40:1;
+ unsigned int p2p_go_vht:1;
unsigned int user_initiated_pd:1;
+ unsigned int p2p_go_group_formation_completed:1;
+ unsigned int waiting_presence_resp;
+ int p2p_first_connection_timeout;
+ unsigned int p2p_nfc_tag_enabled:1;
+ unsigned int p2p_peer_oob_pk_hash_known:1;
+ unsigned int p2p_disable_ip_addr_req:1;
int p2p_persistent_go_freq;
int p2p_persistent_id;
int p2p_go_intent;
int p2p_connect_freq;
- struct os_time p2p_auto_started;
+ struct os_reltime p2p_auto_started;
struct wpa_ssid *p2p_last_4way_hs_fail;
+ struct wpa_radio_work *p2p_scan_work;
+ struct wpa_radio_work *p2p_listen_work;
+ struct wpa_radio_work *p2p_send_action_work;
+
+ u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
+ struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
+ * formation */
+ u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ u8 p2p_ip_addr_info[3 * 4];
#endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid;
@@ -672,7 +789,6 @@
int after_wps;
int known_wps_freq;
unsigned int wps_freq;
- u16 wps_ap_channel;
int wps_fragment_size;
int auto_reconnect_disabled;
@@ -689,7 +805,15 @@
unsigned int auto_select:1;
unsigned int auto_network_select:1;
unsigned int fetch_all_anqp:1;
+ unsigned int fetch_osu_info:1;
+ unsigned int fetch_osu_icon_in_progress:1;
struct wpa_bss *interworking_gas_bss;
+ unsigned int osu_icon_id;
+ struct osu_provider *osu_prov;
+ size_t osu_prov_count;
+ struct os_reltime osu_icon_fetch_start;
+ unsigned int num_osu_scans;
+ unsigned int num_prov_found;
#endif /* CONFIG_INTERWORKING */
unsigned int drv_capa_known;
@@ -698,19 +822,24 @@
u16 num_modes;
u16 flags;
} hw;
+#ifdef CONFIG_MACSEC
+ struct ieee802_1x_kay *kay;
+#endif /* CONFIG_MACSEC */
int pno;
+ int pno_sched_pending;
/* WLAN_REASON_* reason codes. Negative if locally generated. */
int disconnect_reason;
struct ext_password_data *ext_pw;
- struct wpabuf *last_gas_resp;
- u8 last_gas_addr[ETH_ALEN];
- u8 last_gas_dialog_token;
+ struct wpabuf *last_gas_resp, *prev_gas_resp;
+ u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN];
+ u8 last_gas_dialog_token, prev_gas_dialog_token;
unsigned int no_keep_alive:1;
+ unsigned int ext_mgmt_frame_handling:1;
#ifdef CONFIG_WNM
u8 wnm_dialog_token;
@@ -729,6 +858,11 @@
#endif /* CONFIG_TESTING_GET_GTK */
unsigned int num_multichan_concurrent;
+ struct wpa_radio_work *connect_work;
+
+ unsigned int ext_work_id;
+
+ struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES];
};
@@ -776,6 +910,9 @@
struct wpa_ssid *ssid);
void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+ const char *pkcs11_engine_path,
+ const char *pkcs11_module_path);
int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s,
int ap_scan);
int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
@@ -807,21 +944,19 @@
void wpa_supplicant_terminate_proc(struct wpa_global *global);
void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len);
-enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
-void wpas_auth_failed(struct wpa_supplicant *wpa_s);
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason);
void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int clear_failures);
int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
size_t ssid_len);
void wpas_request_connection(struct wpa_supplicant *wpa_s);
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf);
-int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s);
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
/**
* wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@@ -867,6 +1002,13 @@
int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+ struct wpa_used_freq_data *freqs_data,
+ unsigned int len);
+
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs_data,
+ unsigned int len);
int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
int *freq_array, unsigned int len);
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index a08eb33..f3f2a64 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -1,6 +1,6 @@
##### wpa_supplicant configuration file template #####
update_config=1
-ctrl_interface=wlan0
eapol_version=1
ap_scan=1
fast_reauth=1
+pmf=1
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 61a42bd..350b122 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -26,6 +26,7 @@
#include "bss.h"
#include "scan.h"
#include "notify.h"
+#include "wpas_kay.h"
#ifndef CONFIG_NO_CONFIG_BLOBS
@@ -216,29 +217,50 @@
}
-static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
+static const char * result_str(enum eapol_supp_result result)
+{
+ switch (result) {
+ case EAPOL_SUPP_RESULT_FAILURE:
+ return "FAILURE";
+ case EAPOL_SUPP_RESULT_SUCCESS:
+ return "SUCCESS";
+ case EAPOL_SUPP_RESULT_EXPECTED_FAILURE:
+ return "EXPECTED_FAILURE";
+ }
+ return "?";
+}
+
+
+static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol,
+ enum eapol_supp_result result,
void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
int res, pmk_len;
u8 pmk[PMK_LEN];
- wpa_printf(MSG_DEBUG, "EAPOL authentication completed %ssuccessfully",
- success ? "" : "un");
+ wpa_printf(MSG_DEBUG, "EAPOL authentication completed - result=%s",
+ result_str(result));
if (wpas_wps_eapol_cb(wpa_s) > 0)
return;
- if (!success) {
+ wpa_s->eap_expected_failure = result ==
+ EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
+
+ if (result != EAPOL_SUPP_RESULT_SUCCESS) {
/*
* Make sure we do not get stuck here waiting for long EAPOL
* timeout if the AP does not disconnect in case of
* authentication failure.
*/
wpa_supplicant_req_auth_timeout(wpa_s, 2, 0);
+ } else {
+ ieee802_1x_notify_create_actor(wpa_s, wpa_s->last_eapol_src);
}
- if (!success || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+ if (result != EAPOL_SUPP_RESULT_SUCCESS ||
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
return;
if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt))
@@ -539,12 +561,12 @@
static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *buf,
- size_t len)
+ u16 status_code, u32 peer_capab,
+ const u8 *buf, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
- status_code, buf, len);
+ status_code, peer_capab, buf, len);
}
@@ -560,7 +582,9 @@
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)
+ 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 wpa_supplicant *wpa_s = ctx;
struct hostapd_sta_add_params params;
@@ -588,6 +612,10 @@
params.set = !add;
params.ext_capab = ext_capab;
params.ext_capab_len = ext_capab_len;
+ params.supp_channels = supp_channels;
+ params.supp_channels_len = supp_channels_len;
+ params.supp_oper_classes = supp_oper_classes;
+ params.supp_oper_classes_len = supp_oper_classes_len;
return wpa_drv_sta_add(wpa_s, ¶ms);
}
@@ -611,6 +639,8 @@
return WPA_CTRL_REQ_EAP_OTP;
else if (os_strcmp(field, "PASSPHRASE") == 0)
return WPA_CTRL_REQ_EAP_PASSPHRASE;
+ else if (os_strcmp(field, "SIM") == 0)
+ return WPA_CTRL_REQ_SIM;
return WPA_CTRL_REQ_UNKNOWN;
}
@@ -647,6 +677,9 @@
*txt = "Private key passphrase";
ret = "PASSPHRASE";
break;
+ case WPA_CTRL_REQ_SIM:
+ ret = "SIM";
+ break;
default:
break;
}
@@ -918,6 +951,22 @@
conf.ssid = ssid->ssid;
conf.ssid_len = ssid->ssid_len;
conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
+#ifdef CONFIG_P2P
+ if (ssid->p2p_group && wpa_s->current_bss &&
+ !wpa_s->p2p_disable_ip_addr_req) {
+ struct wpabuf *p2p;
+ p2p = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+ P2P_IE_VENDOR_TYPE);
+ if (p2p) {
+ u8 group_capab;
+ group_capab = p2p_get_group_capab(p2p);
+ if (group_capab &
+ P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION)
+ conf.p2p = 1;
+ wpabuf_free(p2p);
+ }
+ }
+#endif /* CONFIG_P2P */
}
wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
}
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
new file mode 100644
index 0000000..354decf
--- /dev/null
+++ b/wpa_supplicant/wpas_kay.c
@@ -0,0 +1,378 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <openssl/ssl.h>
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_i.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "pae/ieee802_1x_key.h"
+#include "pae/ieee802_1x_kay.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "config_ssid.h"
+#include "driver_i.h"
+#include "wpas_kay.h"
+
+
+#define DEFAULT_KEY_LEN 16
+/* secure Connectivity Association Key Name (CKN) */
+#define DEFAULT_CKN_LEN 16
+
+
+static int wpas_macsec_init(void *priv, struct macsec_init_params *params)
+{
+ return wpa_drv_macsec_init(priv, params);
+}
+
+
+static int wpas_macsec_deinit(void *priv)
+{
+ return wpa_drv_macsec_deinit(priv);
+}
+
+
+static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled)
+{
+ return wpa_drv_enable_protect_frames(wpa_s, enabled);
+}
+
+
+static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window)
+{
+ return wpa_drv_set_replay_protect(wpa_s, enabled, window);
+}
+
+
+static int wpas_set_current_cipher_suite(void *wpa_s, const u8 *cs,
+ size_t cs_len)
+{
+ return wpa_drv_set_current_cipher_suite(wpa_s, cs, cs_len);
+}
+
+
+static int wpas_enable_controlled_port(void *wpa_s, Boolean enabled)
+{
+ return wpa_drv_enable_controlled_port(wpa_s, enabled);
+}
+
+
+static int wpas_get_receive_lowest_pn(void *wpa_s, u32 channel,
+ u8 an, u32 *lowest_pn)
+{
+ return wpa_drv_get_receive_lowest_pn(wpa_s, channel, an, lowest_pn);
+}
+
+
+static int wpas_get_transmit_next_pn(void *wpa_s, u32 channel,
+ u8 an, u32 *next_pn)
+{
+ return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn);
+}
+
+
+static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel,
+ u8 an, u32 next_pn)
+{
+ return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn);
+}
+
+
+static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel)
+{
+ return wpa_drv_get_available_receive_sc(wpa_s, channel);
+}
+
+
+static unsigned int conf_offset_val(enum confidentiality_offset co)
+{
+ switch (co) {
+ case CONFIDENTIALITY_OFFSET_30:
+ return 30;
+ break;
+ case CONFIDENTIALITY_OFFSET_50:
+ return 50;
+ default:
+ return 0;
+ }
+}
+
+
+static int wpas_create_receive_sc(void *wpa_s, u32 channel,
+ struct ieee802_1x_mka_sci *sci,
+ enum validate_frames vf,
+ enum confidentiality_offset co)
+{
+ return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, sci->port,
+ conf_offset_val(co), vf);
+}
+
+
+static int wpas_delete_receive_sc(void *wpa_s, u32 channel)
+{
+ return wpa_drv_delete_receive_sc(wpa_s, channel);
+}
+
+
+static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an,
+ u32 lowest_pn, const u8 *sak)
+{
+ return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak);
+}
+
+
+static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an)
+{
+ return wpa_drv_enable_receive_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an)
+{
+ return wpa_drv_disable_receive_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel)
+{
+ return wpa_drv_get_available_transmit_sc(wpa_s, channel);
+}
+
+
+static int
+wpas_create_transmit_sc(void *wpa_s, u32 channel,
+ const struct ieee802_1x_mka_sci *sci,
+ enum confidentiality_offset co)
+{
+ return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, sci->port,
+ conf_offset_val(co));
+}
+
+
+static int wpas_delete_transmit_sc(void *wpa_s, u32 channel)
+{
+ return wpa_drv_delete_transmit_sc(wpa_s, channel);
+}
+
+
+static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an,
+ u32 next_pn, Boolean confidentiality,
+ const u8 *sak)
+{
+ return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn,
+ confidentiality, sak);
+}
+
+
+static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+{
+ return wpa_drv_enable_transmit_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+{
+ return wpa_drv_disable_transmit_sa(wpa_s, channel, an);
+}
+
+
+int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ struct ieee802_1x_kay_ctx *kay_ctx;
+ struct ieee802_1x_kay *res = NULL;
+ enum macsec_policy policy;
+
+ ieee802_1x_dealloc_kay_sm(wpa_s);
+
+ if (!ssid || ssid->macsec_policy == 0)
+ return 0;
+
+ policy = ssid->macsec_policy == 1 ? SHOULD_SECURE : DO_NOT_SECURE;
+
+ kay_ctx = os_zalloc(sizeof(*kay_ctx));
+ if (!kay_ctx)
+ return -1;
+
+ kay_ctx->ctx = wpa_s;
+
+ kay_ctx->macsec_init = wpas_macsec_init;
+ kay_ctx->macsec_deinit = wpas_macsec_deinit;
+ kay_ctx->enable_protect_frames = wpas_enable_protect_frames;
+ kay_ctx->set_replay_protect = wpas_set_replay_protect;
+ kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite;
+ kay_ctx->enable_controlled_port = wpas_enable_controlled_port;
+ kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
+ kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn;
+ kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn;
+ kay_ctx->get_available_receive_sc = wpas_get_available_receive_sc;
+ kay_ctx->create_receive_sc = wpas_create_receive_sc;
+ kay_ctx->delete_receive_sc = wpas_delete_receive_sc;
+ kay_ctx->create_receive_sa = wpas_create_receive_sa;
+ kay_ctx->enable_receive_sa = wpas_enable_receive_sa;
+ kay_ctx->disable_receive_sa = wpas_disable_receive_sa;
+ kay_ctx->get_available_transmit_sc = wpas_get_available_transmit_sc;
+ kay_ctx->create_transmit_sc = wpas_create_transmit_sc;
+ kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc;
+ kay_ctx->create_transmit_sa = wpas_create_transmit_sa;
+ kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa;
+ kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa;
+
+ res = ieee802_1x_kay_init(kay_ctx, policy, wpa_s->ifname,
+ wpa_s->own_addr);
+ if (res == NULL) {
+ os_free(kay_ctx);
+ return -1;
+ }
+
+ wpa_s->kay = res;
+
+ return 0;
+}
+
+
+void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->kay)
+ return;
+
+ ieee802_1x_kay_deinit(wpa_s->kay);
+ wpa_s->kay = NULL;
+}
+
+
+static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s,
+ const u8 *addr, u8 *sid, size_t *len)
+{
+ const u8 *session_id;
+ size_t id_len, need_len;
+
+ session_id = eapol_sm_get_session_id(wpa_s->eapol, &id_len);
+ if (session_id == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to get SessionID from EAPOL state machines");
+ return -1;
+ }
+
+ need_len = 1 + 2 * SSL3_RANDOM_SIZE;
+ if (need_len > id_len) {
+ wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
+ return -1;
+ }
+
+ os_memcpy(sid, session_id, need_len);
+ *len = need_len;
+
+ return 0;
+}
+
+
+static int ieee802_1x_auth_get_msk(struct wpa_supplicant *wpa_s, const u8 *addr,
+ u8 *msk, size_t *len)
+{
+ u8 key[EAP_MSK_LEN];
+ size_t keylen;
+ struct eapol_sm *sm;
+ int res;
+
+ sm = wpa_s->eapol;
+ if (sm == NULL)
+ return -1;
+
+ keylen = EAP_MSK_LEN;
+ res = eapol_sm_get_key(sm, key, keylen);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to get MSK from EAPOL state machines");
+ return -1;
+ }
+
+ if (keylen > *len)
+ keylen = *len;
+ os_memcpy(msk, key, keylen);
+ *len = keylen;
+
+ return 0;
+}
+
+
+void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr)
+{
+ u8 *sid;
+ size_t sid_len = 128;
+ struct mka_key_name *ckn;
+ struct mka_key *cak;
+ struct mka_key *msk;
+ void *res = NULL;
+
+ if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: External notification - Create MKA for "
+ MACSTR, MAC2STR(peer_addr));
+
+ msk = os_zalloc(sizeof(*msk));
+ sid = os_zalloc(sid_len);
+ ckn = os_zalloc(sizeof(*ckn));
+ cak = os_zalloc(sizeof(*cak));
+ if (!msk || !sid || !ckn || !cak)
+ goto fail;
+
+ msk->len = DEFAULT_KEY_LEN;
+ if (ieee802_1x_auth_get_msk(wpa_s, wpa_s->bssid, msk->key, &msk->len)) {
+ wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK");
+ goto fail;
+ }
+
+ if (ieee802_1x_auth_get_session_id(wpa_s, wpa_s->bssid, sid, &sid_len))
+ {
+ wpa_printf(MSG_ERROR,
+ "IEEE 802.1X: Could not get EAP Session Id");
+ goto fail;
+ }
+
+ /* Derive CAK from MSK */
+ cak->len = DEFAULT_KEY_LEN;
+ if (ieee802_1x_cak_128bits_aes_cmac(msk->key, wpa_s->own_addr,
+ peer_addr, cak->key)) {
+ wpa_printf(MSG_ERROR,
+ "IEEE 802.1X: Deriving CAK failed");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len);
+
+ /* Derive CKN from MSK */
+ ckn->len = DEFAULT_CKN_LEN;
+ if (ieee802_1x_ckn_128bits_aes_cmac(msk->key, wpa_s->own_addr,
+ peer_addr, sid, sid_len,
+ ckn->name)) {
+ wpa_printf(MSG_ERROR,
+ "IEEE 802.1X: Deriving CKN failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len);
+
+ res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0,
+ EAP_EXCHANGE, FALSE);
+
+fail:
+ if (msk) {
+ os_memset(msk, 0, sizeof(*msk));
+ os_free(msk);
+ }
+ os_free(sid);
+ os_free(ckn);
+ if (cak) {
+ os_memset(cak, 0, sizeof(*cak));
+ os_free(cak);
+ }
+
+ return res;
+}
diff --git a/wpa_supplicant/wpas_kay.h b/wpa_supplicant/wpas_kay.h
new file mode 100644
index 0000000..b7236d0
--- /dev/null
+++ b/wpa_supplicant/wpas_kay.h
@@ -0,0 +1,41 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPAS_KAY_H
+#define WPAS_KAY_H
+
+#ifdef CONFIG_MACSEC
+
+int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr);
+void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s);
+
+#else /* CONFIG_MACSEC */
+
+static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
+static inline void *
+ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr)
+{
+ return NULL;
+}
+
+static inline void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif /* CONFIG_MACSEC */
+
+#endif /* WPAS_KAY_H */
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
new file mode 100644
index 0000000..e4c83b5
--- /dev/null
+++ b/wpa_supplicant/wpas_module_tests.c
@@ -0,0 +1,102 @@
+/*
+ * wpa_supplicant module tests
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "wpa_supplicant_i.h"
+#include "blacklist.h"
+
+
+static int wpas_blacklist_module_tests(void)
+{
+ struct wpa_supplicant wpa_s;
+ int ret = -1;
+
+ os_memset(&wpa_s, 0, sizeof(wpa_s));
+
+ wpa_blacklist_clear(&wpa_s);
+
+ if (wpa_blacklist_get(NULL, NULL) != NULL ||
+ wpa_blacklist_get(NULL, (u8 *) "123456") != NULL ||
+ wpa_blacklist_get(&wpa_s, NULL) != NULL ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "123456") != NULL)
+ goto fail;
+
+ if (wpa_blacklist_add(NULL, NULL) == 0 ||
+ wpa_blacklist_add(NULL, (u8 *) "123456") == 0 ||
+ wpa_blacklist_add(&wpa_s, NULL) == 0)
+ goto fail;
+
+ if (wpa_blacklist_del(NULL, NULL) == 0 ||
+ wpa_blacklist_del(NULL, (u8 *) "123456") == 0 ||
+ wpa_blacklist_del(&wpa_s, NULL) == 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "123456") == 0)
+ goto fail;
+
+ if (wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "444444") < 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "333333") < 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "xxxxxx") == 0 ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "xxxxxx") != NULL ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "111111") == NULL ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "222222") == NULL ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "444444") == NULL ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "222222") < 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "444444") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ wpa_blacklist_clear(&wpa_s);
+
+ if (ret)
+ wpa_printf(MSG_ERROR, "blacklist module test failure");
+
+ return ret;
+}
+
+
+int wpas_module_tests(void)
+{
+ int ret = 0;
+
+ wpa_printf(MSG_INFO, "wpa_supplicant module tests");
+
+ if (wpas_blacklist_module_tests() < 0)
+ ret = -1;
+
+#ifdef CONFIG_WPS
+ {
+ int wps_module_tests(void);
+ if (wps_module_tests() < 0)
+ ret = -1;
+ }
+#endif /* CONFIG_WPS */
+
+ {
+ int utils_module_tests(void);
+ if (utils_module_tests() < 0)
+ ret = -1;
+ }
+
+ {
+ int common_module_tests(void);
+ if (common_module_tests() < 0)
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index f6c2fcb..fd0d14a 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1,6 +1,6 @@
/*
* wpa_supplicant / WPS integration
- * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -52,8 +52,30 @@
}
+static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ int use_fast_assoc = timeout_ctx != NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb");
+ if (!use_fast_assoc ||
+ wpa_supplicant_fast_associate(wpa_s) != 1)
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0);
+ eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1);
+}
+
+
int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
{
+ if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
+ return 1;
+
if (!wpa_s->wps_success &&
wpa_s->current_ssid &&
eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
@@ -119,9 +141,18 @@
wpabuf_free(wps);
}
- if (!use_fast_assoc ||
- wpa_supplicant_fast_associate(wpa_s) != 1)
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ /*
+ * Complete the next step from an eloop timeout to allow pending
+ * driver events related to the disconnection to be processed
+ * first. This makes it less likely for disconnection event to
+ * cause problems with the following connection.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout");
+ wpas_wps_assoc_with_cred_cancel(wpa_s);
+ eloop_register_timeout(0, 10000,
+ wpas_wps_assoc_with_cred, wpa_s,
+ use_fast_assoc ? (void *) 1 :
+ (void *) 0);
return 1;
}
@@ -260,31 +291,6 @@
ssid->group_cipher != new_ssid->group_cipher)
continue;
- if (ssid->passphrase && new_ssid->passphrase) {
- if (os_strlen(ssid->passphrase) !=
- os_strlen(new_ssid->passphrase))
- continue;
- if (os_strcmp(ssid->passphrase, new_ssid->passphrase) !=
- 0)
- continue;
- } else if (ssid->passphrase || new_ssid->passphrase)
- continue;
-
- if ((ssid->psk_set || new_ssid->psk_set) &&
- os_memcmp(ssid->psk, new_ssid->psk, sizeof(ssid->psk)) != 0)
- continue;
-
- if (ssid->auth_alg == WPA_ALG_WEP) {
- if (ssid->wep_tx_keyidx != new_ssid->wep_tx_keyidx)
- continue;
- if (os_memcmp(ssid->wep_key, new_ssid->wep_key,
- sizeof(ssid->wep_key)))
- continue;
- if (os_memcmp(ssid->wep_key_len, new_ssid->wep_key_len,
- sizeof(ssid->wep_key_len)))
- continue;
- }
-
/* Remove the duplicated older network entry. */
wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
wpas_notify_network_removed(wpa_s, ssid);
@@ -298,7 +304,6 @@
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *ssid = wpa_s->current_ssid;
- u8 key_idx = 0;
u16 auth_type;
#ifdef CONFIG_WPS_REG_DISABLE_OPEN
int registrar = 0;
@@ -344,7 +349,6 @@
}
if (auth_type != WPS_AUTH_OPEN &&
- auth_type != WPS_AUTH_SHARED &&
auth_type != WPS_AUTH_WPAPSK &&
auth_type != WPS_AUTH_WPA2PSK) {
wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
@@ -392,6 +396,17 @@
ssid = wpa_config_add_network(wpa_s->conf);
if (ssid == NULL)
return -1;
+ if (wpa_s->current_ssid) {
+ /*
+ * Should the GO issue multiple credentials for some
+ * reason, each credential should be marked as a
+ * temporary P2P group similarly to the one that gets
+ * marked as such based on the pre-configured values
+ * used for the WPS network block.
+ */
+ ssid->p2p_group = wpa_s->current_ssid->p2p_group;
+ ssid->temporary = wpa_s->current_ssid->temporary;
+ }
wpas_notify_network_added(wpa_s, ssid);
}
@@ -407,38 +422,6 @@
switch (cred->encr_type) {
case WPS_ENCR_NONE:
break;
- case WPS_ENCR_WEP:
- if (cred->key_len <= 0)
- break;
- if (cred->key_len != 5 && cred->key_len != 13 &&
- cred->key_len != 10 && cred->key_len != 26) {
- wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length "
- "%lu", (unsigned long) cred->key_len);
- return -1;
- }
- if (cred->key_idx > NUM_WEP_KEYS) {
- wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d",
- cred->key_idx);
- return -1;
- }
- if (cred->key_idx)
- key_idx = cred->key_idx - 1;
- if (cred->key_len == 10 || cred->key_len == 26) {
- if (hexstr2bin((char *) cred->key,
- ssid->wep_key[key_idx],
- cred->key_len / 2) < 0) {
- wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key "
- "%d", key_idx);
- return -1;
- }
- ssid->wep_key_len[key_idx] = cred->key_len / 2;
- } else {
- os_memcpy(ssid->wep_key[key_idx], cred->key,
- cred->key_len);
- ssid->wep_key_len[key_idx] = cred->key_len;
- }
- ssid->wep_tx_keyidx = key_idx;
- break;
case WPS_ENCR_TKIP:
ssid->pairwise_cipher = WPA_CIPHER_TKIP;
break;
@@ -463,11 +446,6 @@
}
#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
break;
- case WPS_AUTH_SHARED:
- ssid->auth_alg = WPA_AUTH_ALG_SHARED;
- ssid->key_mgmt = WPA_KEY_MGMT_NONE;
- ssid->proto = 0;
- break;
case WPS_AUTH_WPAPSK:
ssid->auth_alg = WPA_AUTH_ALG_OPEN;
ssid->key_mgmt = WPA_KEY_MGMT_PSK;
@@ -509,9 +487,6 @@
wpas_wps_security_workaround(wpa_s, ssid, cred);
- if (cred->ap_channel)
- wpa_s->wps_ap_channel = cred->ap_channel;
-
wpas_wps_remove_dup_network(wpa_s, ssid);
#ifndef CONFIG_NO_CONFIG_WRITE
@@ -533,15 +508,6 @@
}
-#ifdef CONFIG_P2P
-static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
-{
- struct wpa_supplicant *wpa_s = eloop_ctx;
- wpas_p2p_notif_pbc_overlap(wpa_s);
-}
-#endif /* CONFIG_P2P */
-
-
static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
struct wps_event_m2d *m2d)
{
@@ -560,7 +526,7 @@
* Notify P2P from eloop timeout to avoid issues with the
* interface getting removed while processing a message.
*/
- eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s,
+ eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s,
NULL);
}
#endif /* CONFIG_P2P */
@@ -608,9 +574,7 @@
eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL);
wpas_notify_wps_event_fail(wpa_s, fail);
-#ifdef CONFIG_P2P
wpas_p2p_wps_failed(wpa_s, fail);
-#endif /* CONFIG_P2P */
}
@@ -669,9 +633,7 @@
eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
NULL);
-#ifdef CONFIG_P2P
wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
-#endif /* CONFIG_P2P */
}
@@ -882,12 +844,16 @@
int id;
struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+
prev_current = wpa_s->current_ssid;
/* Enable the networks disabled during wpas_wps_reassoc */
wpas_wps_reenable_networks(wpa_s);
eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
/* Remove any existing WPS network from configuration */
ssid = wpa_s->conf->ssid;
@@ -927,7 +893,8 @@
static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
- int registrar, const u8 *bssid)
+ int registrar, const u8 *dev_addr,
+ const u8 *bssid)
{
struct wpa_ssid *ssid;
@@ -947,6 +914,11 @@
return NULL;
}
+#ifdef CONFIG_P2P
+ if (dev_addr)
+ os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN);
+#endif /* CONFIG_P2P */
+
if (bssid) {
#ifndef CONFIG_P2P
struct wpa_bss *bss;
@@ -1027,13 +999,17 @@
static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *selected, const u8 *bssid)
+ struct wpa_ssid *selected, const u8 *bssid,
+ int freq)
{
struct wpa_bss *bss;
wpa_s->after_wps = 0;
wpa_s->known_wps_freq = 0;
- if (bssid) {
+ if (freq) {
+ wpa_s->after_wps = 5;
+ wpa_s->wps_freq = freq;
+ } else if (bssid) {
bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
if (bss && bss->freq > 0) {
wpa_s->known_wps_freq = 1;
@@ -1049,6 +1025,8 @@
wpa_s->normal_scans = 0;
wpa_s->wps_success = 0;
wpa_s->blacklist_cleared = 0;
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
@@ -1058,7 +1036,7 @@
{
struct wpa_ssid *ssid;
wpas_clear_wps(wpa_s);
- ssid = wpas_wps_add_network(wpa_s, 0, bssid);
+ ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
if (ssid == NULL)
return -1;
ssid->temporary = 1;
@@ -1081,24 +1059,47 @@
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
- wpas_wps_reassoc(wpa_s, ssid, bssid);
+ wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
return 0;
}
-int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
- const char *pin, int p2p_group, u16 dev_pw_id)
+static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, const u8 *bssid,
+ const char *pin, int p2p_group, u16 dev_pw_id,
+ const u8 *peer_pubkey_hash,
+ const u8 *ssid_val, size_t ssid_len, int freq)
{
struct wpa_ssid *ssid;
- char val[128];
+ char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
unsigned int rpin = 0;
+ char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
wpas_clear_wps(wpa_s);
- ssid = wpas_wps_add_network(wpa_s, 0, bssid);
- if (ssid == NULL)
+ if (bssid && is_zero_ether_addr(bssid))
+ bssid = NULL;
+ ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not add network");
return -1;
+ }
ssid->temporary = 1;
ssid->p2p_group = p2p_group;
+ if (ssid_val) {
+ ssid->ssid = os_malloc(ssid_len);
+ if (ssid->ssid) {
+ os_memcpy(ssid->ssid, ssid_val, ssid_len);
+ ssid->ssid_len = ssid_len;
+ }
+ }
+ if (peer_pubkey_hash) {
+ os_memcpy(hash, " pkhash=", 8);
+ wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8,
+ peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ } else {
+ hash[0] = '\0';
+ }
#ifdef CONFIG_P2P
if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
@@ -1112,25 +1113,38 @@
}
#endif /* CONFIG_P2P */
if (pin)
- os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
- pin, dev_pw_id);
- else {
+ os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"",
+ pin, dev_pw_id, hash);
+ else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+ os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
+ dev_pw_id, hash);
+ } else {
rpin = wps_generate_pin();
- os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
- rpin, dev_pw_id);
+ os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
+ rpin, dev_pw_id, hash);
}
- if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+ if (wpa_config_set(ssid, "phase1", val, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val);
return -1;
+ }
if (wpa_s->wps_fragment_size)
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
wpa_s->wps_ap_iter = 1;
- wpas_wps_reassoc(wpa_s, ssid, bssid);
+ wpas_wps_reassoc(wpa_s, ssid, bssid, freq);
return rpin;
}
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const char *pin, int p2p_group, u16 dev_pw_id)
+{
+ return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
+ dev_pw_id, NULL, NULL, 0, 0);
+}
+
+
/* Cancel the wps pbc/pin requests */
int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
{
@@ -1155,8 +1169,13 @@
} else {
wpas_wps_reenable_networks(wpa_s);
wpas_wps_clear_ap_info(wpa_s);
+ if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) >
+ 0)
+ wpas_clear_wps(wpa_s);
}
+ wpa_s->after_wps = 0;
+
return 0;
}
@@ -1172,7 +1191,7 @@
if (!pin)
return -1;
wpas_clear_wps(wpa_s);
- ssid = wpas_wps_add_network(wpa_s, 1, bssid);
+ ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid);
if (ssid == NULL)
return -1;
ssid->temporary = 1;
@@ -1200,7 +1219,7 @@
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
- wpas_wps_reassoc(wpa_s, ssid, bssid);
+ wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
return 0;
}
@@ -1267,7 +1286,6 @@
static u16 wps_fix_config_methods(u16 config_methods)
{
-#ifdef CONFIG_WPS2
if ((config_methods &
(WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
@@ -1282,7 +1300,6 @@
"virtual_push_button for WPS 2.0 compliance");
config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
}
-#endif /* CONFIG_WPS2 */
return config_methods;
}
@@ -1291,7 +1308,9 @@
static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
struct wps_context *wps)
{
- wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname);
+ char buf[50];
+ const char *src;
+
if (is_nil_uuid(wpa_s->conf->uuid)) {
struct wpa_supplicant *first;
first = wpa_s->global->ifaces;
@@ -1302,18 +1321,18 @@
os_memcpy(wps->uuid,
wpa_s->global->ifaces->wps->uuid,
WPS_UUID_LEN);
- wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
- "interface", wps->uuid, WPS_UUID_LEN);
+ src = "from the first interface";
} else {
uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
- wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
- "address", wps->uuid, WPS_UUID_LEN);
+ src = "based on MAC address";
}
} else {
os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
- wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration",
- wps->uuid, WPS_UUID_LEN);
+ src = "based on configuration";
}
+
+ uuid_bin2str(wps->uuid, buf, sizeof(buf));
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf);
}
@@ -1418,19 +1437,39 @@
}
+#ifdef CONFIG_WPS_ER
+static void wpas_wps_nfc_clear(struct wps_context *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_ER */
+
+
void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
{
+ wpas_wps_assoc_with_cred_cancel(wpa_s);
eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
wpas_wps_clear_ap_info(wpa_s);
+#ifdef CONFIG_P2P
+ eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL);
+#endif /* CONFIG_P2P */
+
if (wpa_s->wps == NULL)
return;
#ifdef CONFIG_WPS_ER
wps_er_deinit(wpa_s->wps_er, NULL, NULL);
wpa_s->wps_er = NULL;
+ wpas_wps_nfc_clear(wpa_s->wps);
#endif /* CONFIG_WPS_ER */
wps_registrar_deinit(wpa_s->wps->registrar);
@@ -1880,8 +1919,10 @@
if (os_strcmp(settings->encr, "NONE") == 0)
cred.encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
else if (os_strcmp(settings->encr, "WEP") == 0)
cred.encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
else if (os_strcmp(settings->encr, "TKIP") == 0)
cred.encr_type = WPS_ENCR_TKIP;
else if (os_strcmp(settings->encr, "CCMP") == 0)
@@ -1952,19 +1993,6 @@
}
-int wpas_wps_in_progress(struct wpa_supplicant *wpa_s)
-{
- struct wpa_ssid *ssid;
-
- for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
- if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS)
- return 1;
- }
-
- return 0;
-}
-
-
void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
{
struct wps_context *wps = wpa_s->wps;
@@ -2090,15 +2118,32 @@
}
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr,
+ const u8 *bssid,
+ const struct wpabuf *dev_pw, u16 dev_pw_id,
+ int p2p_group, const u8 *peer_pubkey_hash,
+ const u8 *ssid, size_t ssid_len, int freq)
{
struct wps_context *wps = wpa_s->wps;
char pw[32 * 2 + 1];
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+ dev_pw = wpa_s->conf->wps_nfc_dev_pw;
+ dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+ }
+
if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
- wpa_s->conf->wps_nfc_dh_privkey == NULL ||
- wpa_s->conf->wps_nfc_dev_pw == NULL)
+ wpa_s->conf->wps_nfc_dh_privkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Missing DH params - "
+ "cannot start NFC-triggered connection");
return -1;
+ }
+
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - "
+ "cannot start NFC-triggered connection", dev_pw_id);
+ return -1;
+ }
dh5_free(wps->dh_ctx);
wpabuf_free(wps->dh_pubkey);
@@ -2111,6 +2156,7 @@
wps->dh_pubkey = NULL;
wpabuf_free(wps->dh_privkey);
wps->dh_privkey = NULL;
+ wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key");
return -1;
}
wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
@@ -2119,22 +2165,25 @@
wps->dh_pubkey = NULL;
wpabuf_free(wps->dh_privkey);
wps->dh_privkey = NULL;
+ wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context");
return -1;
}
- wpa_snprintf_hex_uppercase(pw, sizeof(pw),
- wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
- wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
- return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
- wpa_s->conf->wps_nfc_dev_pw_id);
+ if (dev_pw) {
+ wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+ wpabuf_head(dev_pw),
+ wpabuf_len(dev_pw));
+ }
+ return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid,
+ dev_pw ? pw : NULL,
+ p2p_group, dev_pw_id, peer_pubkey_hash,
+ ssid, ssid_len, freq);
}
static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
struct wps_parse_attr *attr)
{
- wpa_s->wps_ap_channel = 0;
-
/*
* Disable existing networks temporarily to allow the newly learned
* credential to be preferred. Enable the temporarily disabled networks
@@ -2150,12 +2199,8 @@
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
return 0;
- wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
- "based on the received credential added");
- wpa_s->normal_scans = 0;
- wpa_supplicant_reinit_autoscan(wpa_s);
- if (wpa_s->wps_ap_channel) {
- u16 chan = wpa_s->wps_ap_channel;
+ if (attr->ap_channel) {
+ u16 chan = WPA_GET_BE16(attr->ap_channel);
int freq = 0;
if (chan >= 1 && chan <= 13)
@@ -2166,14 +2211,21 @@
freq = 5000 + 5 * chan;
if (freq) {
- wpa_printf(MSG_DEBUG, "WPS: Credential indicated "
- "AP channel %u -> %u MHz", chan, freq);
+ wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz",
+ chan, freq);
wpa_s->after_wps = 5;
wpa_s->wps_freq = freq;
}
}
+
+ wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+ "based on the received credential added");
+ wpa_s->normal_scans = 0;
+ wpa_supplicant_reinit_autoscan(wpa_s);
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, 0);
return 0;
@@ -2217,7 +2269,7 @@
int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
- const struct wpabuf *data)
+ const struct wpabuf *data, int forced_freq)
{
const struct wpabuf *wps = data;
struct wpabuf *tmp = NULL;
@@ -2230,6 +2282,15 @@
/* Assume this contains full NDEF record */
tmp = ndef_parse_wifi(data);
if (tmp == NULL) {
+#ifdef CONFIG_P2P
+ tmp = ndef_parse_p2p(data);
+ if (tmp) {
+ ret = wpas_p2p_nfc_tag_process(wpa_s, tmp,
+ forced_freq);
+ wpabuf_free(tmp);
+ return ret;
+ }
+#endif /* CONFIG_P2P */
wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
return -1;
}
@@ -2242,24 +2303,45 @@
}
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr)
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+ int ndef)
{
- if (cr)
- return ndef_build_wifi_hc(1);
- return ndef_build_wifi_hr();
+ struct wpabuf *ret;
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+ wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+ return NULL;
+
+ ret = wps_build_nfc_handover_req(wpa_s->wps,
+ wpa_s->conf->wps_nfc_dh_pubkey);
+
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
}
#ifdef CONFIG_WPS_NFC
-struct wpabuf * wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s,
- int ndef, const char *uuid)
+
+static struct wpabuf *
+wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef,
+ const char *uuid)
{
#ifdef CONFIG_WPS_ER
struct wpabuf *ret;
u8 u[UUID_LEN], *use_uuid = NULL;
u8 addr[ETH_ALEN], *use_addr = NULL;
+ struct wps_context *wps = wpa_s->wps;
- if (!wpa_s->wps_er)
+ if (wps == NULL)
return NULL;
if (uuid == NULL)
@@ -2271,11 +2353,23 @@
else
return NULL;
- /*
- * Handover Select carrier record for WPS uses the same format as
- * configuration token.
- */
- ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) {
+ if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+ return NULL;
+ }
+
+ wpas_wps_nfc_clear(wps);
+ wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+ wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+ wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+ if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+ wpas_wps_nfc_clear(wps);
+ return NULL;
+ }
+
+ ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid,
+ use_addr, wpa_s->conf->wps_nfc_dh_pubkey);
if (ndef && ret) {
struct wpabuf *tmp;
tmp = ndef_build_wifi(ret);
@@ -2306,29 +2400,125 @@
}
-int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
- const struct wpabuf *data)
-{
- /* TODO */
- return -1;
-}
-
-
-int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
- const struct wpabuf *data)
+static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data)
{
struct wpabuf *wps;
- int ret;
+ int ret = -1;
+ u16 wsc_len;
+ const u8 *pos;
+ struct wpabuf msg;
+ struct wps_parse_attr attr;
+ u16 dev_pw_id;
+ const u8 *bssid = NULL;
+ int freq = 0;
wps = ndef_parse_wifi(data);
if (wps == NULL)
return -1;
wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
"payload from NFC connection handover");
- wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
- ret = wpas_wps_nfc_tag_process(wpa_s, wps);
- wpabuf_free(wps);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+ if (wpabuf_len(wps) < 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select "
+ "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 Wi-Fi Handover Select Message", wsc_len);
+ goto out;
+ }
+ pos += 2;
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: WSC attributes in Wi-Fi Handover Select 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 Select 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 Select Message");
+ ret = -1;
+ goto out;
+ }
+
+ if (attr.ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover "
+ "Select Message");
+ ret = -1;
+ goto out;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len);
+
+ if (attr.mac_addr) {
+ bssid = attr.mac_addr;
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR,
+ MAC2STR(bssid));
+ }
+
+ if (attr.rf_bands)
+ wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands);
+
+ if (attr.ap_channel) {
+ u16 chan = WPA_GET_BE16(attr.ap_channel);
+
+ wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan);
+
+ if (chan >= 1 && chan <= 13 &&
+ (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ))
+ freq = 2407 + 5 * chan;
+ else if (chan == 14 &&
+ (attr.rf_bands == NULL ||
+ *attr.rf_bands & WPS_RF_24GHZ))
+ freq = 2484;
+ else if (chan >= 30 &&
+ (attr.rf_bands == NULL ||
+ *attr.rf_bands & WPS_RF_50GHZ))
+ freq = 5000 + 5 * chan;
+
+ if (freq) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: AP indicated channel %u -> %u MHz",
+ chan, freq);
+ }
+ }
+
+ 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 Select Message", dev_pw_id);
+ ret = -1;
+ goto out;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash",
+ attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+ ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0,
+ attr.oob_dev_password,
+ attr.ssid, attr.ssid_len, freq);
+
+out:
+ wpabuf_free(wps);
return ret;
}
@@ -2343,15 +2533,107 @@
return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
}
+
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ 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(wpa_s->wps->registrar,
+ attr.oob_dev_password,
+ DEV_PW_NFC_CONNECTION_HANDOVER,
+ NULL, 0, 1);
+
+out:
+ wpabuf_free(wps);
+ return ret;
+}
+
#endif /* CONFIG_WPS_NFC */
-extern int wpa_debug_level;
-
static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
{
size_t i;
- struct os_time now;
+ struct os_reltime now;
if (wpa_debug_level > MSG_DEBUG)
return;
@@ -2359,7 +2641,7 @@
if (wpa_s->wps_ap == NULL)
return;
- os_get_time(&now);
+ os_get_reltime(&now);
for (i = 0; i < wpa_s->num_wps_ap; i++) {
struct wps_ap_info *ap = &wpa_s->wps_ap[i];
@@ -2463,11 +2745,14 @@
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wps_ap_info *ap;
+
+ wpa_s->after_wps = 0;
+
if (!wpa_s->wps_ap_iter)
return;
ap = wpas_wps_get_ap_info(wpa_s, bssid);
if (ap == NULL)
return;
ap->tries++;
- os_get_time(&ap->last_attempt);
+ os_get_reltime(&ap->last_attempt);
}
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 2a212ca..2263512 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -60,24 +60,27 @@
struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
int ndef, const char *uuid);
int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
-int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
int ndef, const char *id_str);
struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+ const u8 *bssid,
+ const struct wpabuf *dev_pw, u16 dev_pw_id,
+ int p2p_group, const u8 *peer_pubkey_hash,
+ const u8 *ssid, size_t ssid_len, int freq);
int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
- const struct wpabuf *data);
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr);
+ const struct wpabuf *data, int forced_freq);
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+ int ndef);
struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
int ndef, int cr, const char *uuid);
-int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
- const struct wpabuf *data);
-int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
- const struct wpabuf *data);
int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
const struct wpabuf *req,
const struct wpabuf *sel);
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);