diff options
Diffstat (limited to 'wpa_supplicant')
170 files changed, 79368 insertions, 0 deletions
diff --git a/wpa_supplicant/.config b/wpa_supplicant/.config new file mode 100644 index 0000000..1b0ee97 --- /dev/null +++ b/wpa_supplicant/.config @@ -0,0 +1,395 @@ +# Example wpa_supplicant build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cases, these lines should use += in order not +# to override previous values of the variables. + + +# Uncomment following two lines and fix the paths if you have installed OpenSSL +# or GnuTLS in non-default location +#CFLAGS += -I/usr/local/openssl/include +#LIBS += -L/usr/local/openssl/lib + +# Some Red Hat versions seem to include kerberos header files from OpenSSL, but +# the kerberos files are not in the default include path. Following line can be +# 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 Prism54 driver +# (Note: Prism54 is not yet supported, i.e., this will not work as-is and is +# for developers only) +#CONFIG_DRIVER_PRISM54=y + +# 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 +#CONFIG_DRIVER_WEXT=y + +# Driver interface for Linux drivers using the nl80211 kernel interface +#CONFIG_DRIVER_NL80211=y +CONFIG_LIBNL20=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib + +# Driver interface for Windows NDIS +#CONFIG_DRIVER_NDIS=y +#CFLAGS += -I/usr/include/w32api/ddk +#LIBS += -L/usr/local/lib +# For native build using mingw +#CONFIG_NATIVE_WINDOWS=y +# Additional directories for cross-compilation on Linux host for mingw target +#CFLAGS += -I/opt/mingw/mingw32/include/ddk +#LIBS += -L/opt/mingw/mingw32/lib +#CC=mingw32-gcc +# By default, driver_ndis uses WinPcap for low-level operations. This can be +# replaced with the following option which replaces WinPcap calls with NDISUIO. +# However, this requires that WZC is disabled (net stop wzcsvc) before starting +# wpa_supplicant. +# CONFIG_USE_NDISUIO=y + +# Driver interface for development testing +#CONFIG_DRIVER_TEST=y + +# Include client MLME (management frame processing) for test driver +# This can be used to test MLME operations in hostapd with the test interface. +# space. +#CONFIG_CLIENT_MLME=y + +# Driver interface for wired Ethernet drivers +#CONFIG_DRIVER_WIRED=y + +# Driver interface for the Broadcom RoboSwitch family +#CONFIG_DRIVER_ROBOSWITCH=y + +# Driver interface for no driver (e.g., WPS ER only) +#CONFIG_DRIVER_NONE=y + +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is +# included) +CONFIG_IEEE8021X_EAPOL=y + +# EAP-MD5 +CONFIG_EAP_MD5=y + +# EAP-MSCHAPv2 +CONFIG_EAP_MSCHAPV2=y + +# EAP-TLS +CONFIG_EAP_TLS=y + +# EAL-PEAP +CONFIG_EAP_PEAP=y + +# EAP-TTLS +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. +CONFIG_EAP_FAST=y + +# EAP-GTC +CONFIG_EAP_GTC=y + +# EAP-OTP +CONFIG_EAP_OTP=y + +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) +CONFIG_EAP_SIM=y + +# EAP-PSK (experimental; this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# EAP-PAX +#CONFIG_EAP_PAX=y + +# LEAP +CONFIG_EAP_LEAP=y + +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) +#CONFIG_EAP_AKA=y + +# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used). +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=y + +# Enable USIM simulator (Milenage) for EAP-AKA +#CONFIG_USIM_SIMULATOR=y + +# EAP-SAKE +#CONFIG_EAP_SAKE=y + +# EAP-GPSK +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + +# EAP-TNC and related Trusted Network Connect support (experimental) +#CONFIG_EAP_TNC=y + +# Wi-Fi Protected Setup (WPS) +CONFIG_WPS=y +# Enable WSC 2.0 support +CONFIG_WPS2=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# Smartcard support (i.e., private key on a smartcard), e.g., with openssl +# engine. +CONFIG_SMARTCARD=y + +# PC/SC interface for smartcards (USIM, GSM SIM) +# Enable this if EAP-SIM or EAP-AKA is included +#CONFIG_PCSC=y + +# Development testing +#CONFIG_EAPOL_TEST=y + +# Select control interface backend for external programs, e.g, wpa_cli: +# unix = UNIX domain sockets (default for Linux/*BSD) +# udp = UDP sockets using localhost (127.0.0.1) +# named_pipe = Windows Named Pipe (default for Windows) +# y = use default (backwards compatibility) +# If this option is commented out, control interface is not included in the +# build. +CONFIG_CTRL_IFACE=y + +# Include support for GNU Readline and History Libraries in wpa_cli. +# When building a wpa_cli binary for distribution, please note that these +# libraries are licensed under GPL and as such, BSD license may not apply for +# the resulting binary. +#CONFIG_READLINE=y + +# Remove debugging code that is printing out debug message to stdout. +# This can be used to reduce the size of the wpa_supplicant considerably +# if debugging code is not needed. The size reduction can be around 35% +# (e.g., 90 kB). +#CONFIG_NO_STDOUT_DEBUG=y + +# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save +# 35-50 kB in code size. +#CONFIG_NO_WPA=y + +# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to +# save about 1 kB in code size when building only WPA-Personal (no EAP support) +# or 6 kB if building for WPA-Enterprise. +#CONFIG_NO_WPA2=y + +# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support +# This option can be used to reduce code size by removing support for +# converting ASCII passphrases into PSK. If this functionality is removed, the +# PSK can only be configured as the 64-octet hexstring (e.g., from +# wpa_passphrase). This saves about 0.5 kB in code size. +#CONFIG_NO_WPA_PASSPHRASE=y + +# Disable scan result processing (ap_mode=1) to save code size by about 1 kB. +# This can be used if ap_scan=1 mode is never enabled. +#CONFIG_NO_SCAN_PROCESSING=y + +# Select configuration backend: +# file = text file (e.g., wpa_supplicant.conf; note: the configuration file +# path is given on command line, not here; this option is just used to +# select the backend that allows configuration files to be used) +# winreg = Windows registry (see win_example.reg for an example) +CONFIG_BACKEND=file + +# Remove configuration write functionality (i.e., to allow the configuration +# file to be updated based on runtime configuration changes). The runtime +# configuration can still be changed, the changes are just not going to be +# persistent over restarts. This option can be used to reduce code size by +# about 3.5 kB. +#CONFIG_NO_CONFIG_WRITE=y + +# Remove support for configuration blobs to reduce code size by about 1.5 kB. +#CONFIG_NO_CONFIG_BLOBS=y + +# Select program entry point implementation: +# main = UNIX/POSIX like main() function (default) +# main_winsvc = Windows service (read parameters from registry) +# main_none = Very basic example (development use only) +#CONFIG_MAIN=main + +# Select wrapper for operatins system and C library specific functions +# unix = UNIX/POSIX like systems (default) +# win32 = Windows systems +# none = Empty template +CONFIG_OS=unix + +# Select event loop implementation +# eloop = select() loop (default) +# eloop_win = Windows events and WaitForMultipleObject() loop +# eloop_none = Empty template +CONFIG_ELOOP=eloop + +# Select layer 2 packet implementation +# linux = Linux packet socket (default) +# pcap = libpcap/libdnet/WinPcap +# freebsd = FreeBSD libpcap +# winpcap = WinPcap with receive thread +# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y) +# none = Empty template +CONFIG_L2_PACKET=linux + +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +CONFIG_PEERKEY=y + +# IEEE 802.11w (management frame protection) +# This version is an experimental implementation based on IEEE 802.11w/D1.0 +# draft and is subject to change since the standard has not yet been finalized. +# Driver support is also needed for IEEE 802.11w. +#CONFIG_IEEE80211W=y + +# Select TLS implementation +# openssl = OpenSSL (default) +# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA) +# internal = Internal TLSv1 implementation (experimental) +# none = Empty template +#CONFIG_TLS=openssl + +# Whether to enable TLS/IA support, which is required for EAP-TTLSv1. +# You need CONFIG_TLS=gnutls for this to have any effect. Please note that +# even though the core GnuTLS library is released under LGPL, this extra +# library uses GPL and as such, the terms of GPL apply to the combination +# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not +# apply for distribution of the resulting binary. +#CONFIG_GNUTLS_EXTRA=y + +# If CONFIG_TLS=internal is used, additional library and include paths are +# needed for LibTomMath. Alternatively, an integrated, minimal version of +# LibTomMath can be used. See beginning of libtommath.c for details on benefits +# and drawbacks of this option. +#CONFIG_INTERNAL_LIBTOMMATH=y +#ifndef CONFIG_INTERNAL_LIBTOMMATH +#LTM_PATH=/usr/src/libtommath-0.39 +#CFLAGS += -I$(LTM_PATH) +#LIBS += -L$(LTM_PATH) +#LIBS_p += -L$(LTM_PATH) +#endif +# At the cost of about 4 kB of additional binary size, the internal LibTomMath +# can be configured to include faster routines for exptmod, sqr, and div to +# speed up DH and RSA calculation considerably +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y + +# Include NDIS event processing through WMI into wpa_supplicant/wpasvc. +# This is only for Windows builds and requires WMI-related header files and +# WbemUuid.Lib from Platform SDK even when building with MinGW. +#CONFIG_NDIS_EVENTS_INTEGRATED=y +#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" + +# Add support for old DBus control interface +# (fi.epitest.hostap.WPASupplicant) +#CONFIG_CTRL_IFACE_DBUS=y + +# Add support for new DBus control interface +# (fi.w1.hostap.wpa_supplicant1) +#CONFIG_CTRL_IFACE_DBUS_NEW=y + +# Add introspection support for new DBus control interface (requires libxml2) +#CONFIG_CTRL_IFACE_DBUS_INTRO=y + +# Add support for loading EAP methods dynamically as shared libraries. +# When this option is enabled, each EAP method can be either included +# statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn). +# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to +# be loaded in the beginning of the wpa_supplicant configuration file +# (see load_dynamic_eap parameter in the example file) before being used in +# the network blocks. +# +# Note that some shared parts of EAP methods are included in the main program +# and in order to be able to use dynamic EAP methods using these parts, the +# main program must have been build with the EAP method enabled (=y or =dyn). +# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries +# unless at least one of them was included in the main build to force inclusion +# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included +# in the main build to be able to load these methods dynamically. +# +# Please also note that using dynamic libraries will increase the total binary +# size. Thus, it may not be the best option for targets that have limited +# amount of memory/flash. +#CONFIG_DYNAMIC_EAP_METHODS=y + +# IEEE Std 802.11r-2008 (Fast BSS Transition) +#CONFIG_IEEE80211R=y + +# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) +#CONFIG_DEBUG_FILE=y + +# Add support for writing debug log to Android logcat instead of standard output +CONFIG_ANDROID_LOG=y + +# Enable privilege separation (see README 'Privilege separation' for details) +#CONFIG_PRIVSEP=y + +# Enable mitigation against certain attacks against TKIP by delaying Michael +# MIC error reports by a random amount of time between 0 and 60 seconds +#CONFIG_DELAYED_MIC_ERROR_REPORT=y diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk new file mode 100644 index 0000000..8a756d8 --- /dev/null +++ b/wpa_supplicant/Android.mk @@ -0,0 +1,1427 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +LOCAL_PATH := $(call my-dir) + +WPA_BUILD_SUPPLICANT := false +ifneq ($(TARGET_SIMULATOR),true) + ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) + WPA_BUILD_SUPPLICANT := true + CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y + endif +endif + +include $(LOCAL_PATH)/.config + +# To ignore possible wrong network configurations +L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS + +# 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\" + +# To force sizeof(enum) = 4 +ifeq ($(TARGET_ARCH),arm) +L_CFLAGS += -mabi=aapcs-linux +endif + +# To allow non-ASCII characters in SSID +L_CFLAGS += -DWPA_UNICODE_SSID + +# OpenSSL is configured without engines on Android +L_CFLAGS += -DOPENSSL_NO_ENGINE + +INCLUDES = $(LOCAL_PATH) +INCLUDES += $(LOCAL_PATH)/src +INCLUDES += $(LOCAL_PATH)/src/common +# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes +INCLUDES += $(LOCAL_PATH)/src/drivers +INCLUDES += $(LOCAL_PATH)/src/eap_common +INCLUDES += $(LOCAL_PATH)/src/eapol_supp +INCLUDES += $(LOCAL_PATH)/src/eap_peer +INCLUDES += $(LOCAL_PATH)/src/eap_server +INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw +INCLUDES += $(LOCAL_PATH)/src/l2_packet +INCLUDES += $(LOCAL_PATH)/src/radius +INCLUDES += $(LOCAL_PATH)/src/rsn_supp +INCLUDES += $(LOCAL_PATH)/src/tls +INCLUDES += $(LOCAL_PATH)/src/utils +INCLUDES += $(LOCAL_PATH)/src/wps +INCLUDES += external/openssl/include +INCLUDES += frameworks/base/cmds/keystore +ifdef CONFIG_DRIVER_NL80211 +INCLUDES += external/libnl_2/include +endif + +OBJS = config.c +OBJS += notify.c +OBJS += bss.c +OBJS += eap_register.c +OBJS += src/utils/common.c +OBJS += src/utils/wpa_debug.c +OBJS += src/utils/wpabuf.c +OBJS_p = wpa_passphrase.c +OBJS_p += src/utils/common.c +OBJS_p += src/utils/wpa_debug.c +OBJS_p += src/utils/wpabuf.c +OBJS_c = wpa_cli.c src/common/wpa_ctrl.c +OBJS_c += src/utils/wpa_debug.c +OBJS_d = +OBJS_priv = + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +L_CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += src/utils/os_$(CONFIG_OS).c +OBJS_p += src/utils/os_$(CONFIG_OS).c +OBJS_c += src/utils/os_$(CONFIG_OS).c + +ifdef CONFIG_WPA_TRACE +L_CFLAGS += -DWPA_TRACE +OBJS += src/utils/trace.c +OBJS_p += src/utils/trace.c +OBJS_c += src/utils/trace.c +LDFLAGS += -rdynamic +L_CFLAGS += -funwind-tables +ifdef CONFIG_WPA_TRACE_BFD +L_CFLAGS += -DWPA_TRACE_BFD +LIBS += -lbfd +LIBS_p += -lbfd +LIBS_c += -lbfd +endif +endif + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += src/utils/$(CONFIG_ELOOP).c +OBJS_c += src/utils/$(CONFIG_ELOOP).c + + +ifdef CONFIG_EAPOL_TEST +L_CFLAGS += -Werror -DEAPOL_TEST +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.c +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +L_CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.c +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.c +endif + +ifdef CONFIG_NO_CONFIG_WRITE +L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_IEEE80211W +L_CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_IEEE80211R +L_CFLAGS += -DCONFIG_IEEE80211R +OBJS += src/rsn_supp/wpa_ft.c +NEED_80211_COMMON=y +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_PEERKEY +L_CFLAGS += -DCONFIG_PEERKEY +endif + +ifndef CONFIG_NO_WPA +OBJS += src/rsn_supp/wpa.c +OBJS += src/rsn_supp/preauth.c +OBJS += src/rsn_supp/pmksa_cache.c +OBJS += src/rsn_supp/peerkey.c +OBJS += src/rsn_supp/wpa_ie.c +OBJS += src/common/wpa_common.c +NEED_AES=y +NEED_SHA1=y +NEED_MD5=y +NEED_RC4=y +else +L_CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +endif + +ifdef CONFIG_IBSS_RSN +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_IBSS_RSN +OBJS += ibss_rsn.c +endif + +ifdef CONFIG_P2P +OBJS += p2p_supplicant.c +OBJS += src/p2p/p2p.c +OBJS += src/p2p/p2p_utils.c +OBJS += src/p2p/p2p_parse.c +OBJS += src/p2p/p2p_build.c +OBJS += src/p2p/p2p_go_neg.c +OBJS += src/p2p/p2p_sd.c +OBJS += src/p2p/p2p_pd.c +OBJS += src/p2p/p2p_invitation.c +OBJS += src/p2p/p2p_dev_disc.c +OBJS += src/p2p/p2p_group.c +OBJS += src/ap/p2p_hostapd.c +L_CFLAGS += -DCONFIG_P2P +NEED_80211_COMMON=y +ifdef CONFIG_P2P_STRICT +L_CFLAGS += -DCONFIG_P2P_STRICT +endif +endif + +ifdef CONFIG_NO_WPA2 +L_CFLAGS += -DCONFIG_NO_WPA2 +endif + +include $(LOCAL_PATH)/src/drivers/drivers.mk + +ifdef CONFIG_AP +OBJS_d += $(DRV_BOTH_OBJS) +L_CFLAGS += $(DRV_BOTH_CFLAGS) +LDFLAGS += $(DRV_BOTH_LDFLAGS) +LIBS += $(DRV_BOTH_LIBS) +else +NEED_AP_MLME= +OBJS_d += $(DRV_WPA_OBJS) +L_CFLAGS += $(DRV_WPA_CFLAGS) +LDFLAGS += $(DRV_WPA_LDFLAGS) +LIBS += $(DRV_WPA_LIBS) +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +L_CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +L_CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += src/eap_peer/eap_tls.so +else +L_CFLAGS += -DEAP_TLS +OBJS += src/eap_peer/eap_tls.c +OBJS_h += src/eap_server/eap_server_tls.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +L_CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_peap.so +else +L_CFLAGS += -DEAP_PEAP +OBJS += src/eap_peer/eap_peap.c +OBJS += src/eap_common/eap_peap_common.c +OBJS_h += src/eap_server/eap_server_peap.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +L_CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += src/eap_peer/eap_ttls.so +else +L_CFLAGS += -DEAP_TTLS +OBJS += src/eap_peer/eap_ttls.c +OBJS_h += src/eap_server/eap_server_ttls.c +endif +MS_FUNCS=y +TLS_FUNCS=y +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +L_CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += src/eap_peer/eap_md5.so +else +L_CFLAGS += -DEAP_MD5 +OBJS += src/eap_peer/eap_md5.c +OBJS_h += src/eap_server/eap_server_md5.c +endif +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +ifndef CONFIG_EAP_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 +ifeq ($(CONFIG_EAP_MSCHAPV2), dyn) +L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += src/eap_peer/eap_mschapv2.so +EAPDYN += src/eap_peer/mschapv2.so +else +L_CFLAGS += -DEAP_MSCHAPv2 +OBJS += src/eap_peer/eap_mschapv2.c +OBJS += src/eap_peer/mschapv2.c +OBJS_h += src/eap_server/eap_server_mschapv2.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +L_CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += src/eap_peer/eap_gtc.so +else +L_CFLAGS += -DEAP_GTC +OBJS += src/eap_peer/eap_gtc.c +OBJS_h += src/eap_server/eap_server_gtc.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +L_CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += src/eap_peer/eap_otp.so +else +L_CFLAGS += -DEAP_OTP +OBJS += src/eap_peer/eap_otp.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +L_CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += src/eap_peer/eap_sim.so +else +L_CFLAGS += -DEAP_SIM +OBJS += src/eap_peer/eap_sim.c +OBJS_h += src/eap_server/eap_server_sim.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +L_CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_leap.so +else +L_CFLAGS += -DEAP_LEAP +OBJS += src/eap_peer/eap_leap.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +L_CFLAGS += -DEAP_PSK_DYNAMIC +EAPDYN += src/eap_peer/eap_psk.so +else +L_CFLAGS += -DEAP_PSK +OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c +OBJS_h += src/eap_server/eap_server_psk.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +NEED_AES_OMAC1=y +NEED_AES_ENCBLOCK=y +NEED_AES_EAX=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +L_CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += src/eap_peer/eap_aka.so +else +L_CFLAGS += -DEAP_AKA +OBJS += src/eap_peer/eap_aka.c +OBJS_h += src/eap_server/eap_server_aka.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +L_CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += src/eap_common/eap_sim_common.c +OBJS_h += src/eap_server/eap_sim_db.c +NEED_AES=y +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +L_CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += src/eap_peer/eap_fast.so +EAPDYN += src/eap_common/eap_fast_common.c +else +L_CFLAGS += -DEAP_FAST +OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c +OBJS += src/eap_common/eap_fast_common.c +OBJS_h += src/eap_server/eap_server_fast.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +L_CFLAGS += -DEAP_PAX_DYNAMIC +EAPDYN += src/eap_peer/eap_pax.so +else +L_CFLAGS += -DEAP_PAX +OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c +OBJS_h += src/eap_server/eap_server_pax.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +L_CFLAGS += -DEAP_SAKE_DYNAMIC +EAPDYN += src/eap_peer/eap_sake.so +else +L_CFLAGS += -DEAP_SAKE +OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c +OBJS_h += src/eap_server/eap_server_sake.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +L_CFLAGS += -DEAP_GPSK_DYNAMIC +EAPDYN += src/eap_peer/eap_gpsk.so +else +L_CFLAGS += -DEAP_GPSK +OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c +OBJS_h += src/eap_server/eap_server_gpsk.c +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +L_CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_EAP_PWD +L_CFLAGS += -DEAP_PWD +OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c +OBJS_h += src/eap_server/eap_pwd.c +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + +ifdef CONFIG_WPS +ifdef CONFIG_WPS2 +L_CFLAGS += -DCONFIG_WPS2 +endif + +# EAP-WSC +L_CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.c +OBJS += src/utils/uuid.c +OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c +OBJS += src/wps/wps.c +OBJS += src/wps/wps_common.c +OBJS += src/wps/wps_attr_parse.c +OBJS += src/wps/wps_attr_build.c +OBJS += src/wps/wps_attr_process.c +OBJS += src/wps/wps_dev_attr.c +OBJS += src/wps/wps_enrollee.c +OBJS += src/wps/wps_registrar.c +OBJS_h += src/eap_server/eap_server_wsc.c +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y +NEED_80211_COMMON=y +NEED_AES_CBC=y +NEED_MODEXP=y + +ifdef CONFIG_WPS_UFD +L_CFLAGS += -DCONFIG_WPS_UFD +OBJS += src/wps/wps_ufd.c +NEED_WPS_OOB=y +endif + +ifdef CONFIG_WPS_NFC +L_CFLAGS += -DCONFIG_WPS_NFC +OBJS += src/wps/ndef.c +OBJS += src/wps/wps_nfc.c +NEED_WPS_OOB=y +ifdef CONFIG_WPS_NFC_PN531 +PN531_PATH ?= /usr/local/src/nfc +L_CFLAGS += -DCONFIG_WPS_NFC_PN531 +L_CFLAGS += -I${PN531_PATH}/inc +OBJS += src/wps/wps_nfc_pn531.c +LIBS += ${PN531_PATH}/lib/wpsnfc.dll +LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll +endif +endif + +ifdef NEED_WPS_OOB +L_CFLAGS += -DCONFIG_WPS_OOB +endif + +ifdef CONFIG_WPS_ER +CONFIG_WPS_UPNP=y +L_CFLAGS += -DCONFIG_WPS_ER +OBJS += src/wps/wps_er.c +OBJS += src/wps/wps_er_ssdp.c +endif + +ifdef CONFIG_WPS_UPNP +L_CFLAGS += -DCONFIG_WPS_UPNP +OBJS += src/wps/wps_upnp.c +OBJS += src/wps/wps_upnp_ssdp.c +OBJS += src/wps/wps_upnp_web.c +OBJS += src/wps/wps_upnp_event.c +OBJS += src/wps/wps_upnp_ap.c +OBJS += src/wps/upnp_xml.c +OBJS += src/wps/httpread.c +OBJS += src/wps/http_client.c +OBJS += src/wps/http_server.c +endif + +ifdef CONFIG_WPS_STRICT +L_CFLAGS += -DCONFIG_WPS_STRICT +OBJS += src/wps/wps_validate.c +endif + +ifdef CONFIG_WPS_TESTING +L_CFLAGS += -DCONFIG_WPS_TESTING +endif + +ifdef CONFIG_WPS_REG_DISABLE_OPEN +L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +L_CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c +EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +else +L_CFLAGS += -DEAP_IKEV2 +OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c +OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +OBJS_h += src/eap_server/eap_server_ikev2.c +OBJS_h += src/eap_server/ikev2.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_MODEXP=y +NEED_CIPHER=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +EAPDYN += src/eap_peer/eap_vendor_test.so +else +L_CFLAGS += -DEAP_VENDOR_TEST +OBJS += src/eap_peer/eap_vendor_test.c +OBJS_h += src/eap_server/eap_server_vendor_test.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +L_CFLAGS += -DEAP_TNC +OBJS += src/eap_peer/eap_tnc.c +OBJS += src/eap_peer/tncc.c +OBJS_h += src/eap_server/eap_server_tnc.c +OBJS_h += src/eap_server/tncs.c +NEED_BASE64=y +ifndef CONFIG_NATIVE_WINDOWS +ifndef CONFIG_DRIVER_BSD +LIBS += -ldl +endif +endif +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +L_CFLAGS += -DIEEE8021X_EAPOL +OBJS += src/eapol_supp/eapol_supp_sm.c +OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c +NEED_EAP_COMMON=y +ifdef CONFIG_DYNAMIC_EAP_METHODS +L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_AP +NEED_80211_COMMON=y +NEED_EAP_COMMON=y +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_AP +OBJS += ap.c +L_CFLAGS += -DCONFIG_NO_RADIUS +L_CFLAGS += -DCONFIG_NO_ACCOUNTING +L_CFLAGS += -DCONFIG_NO_VLAN +OBJS += src/ap/hostapd.c +OBJS += src/ap/wpa_auth_glue.c +OBJS += src/ap/utils.c +OBJS += src/ap/authsrv.c +OBJS += src/ap/ap_config.c +OBJS += src/utils/ip_addr.c +OBJS += src/ap/sta_info.c +OBJS += src/ap/tkip_countermeasures.c +OBJS += src/ap/ap_mlme.c +OBJS += src/ap/ieee802_1x.c +OBJS += src/eapol_auth/eapol_auth_sm.c +OBJS += src/ap/ieee802_11_auth.c +OBJS += src/ap/drv_callbacks.c +OBJS += src/ap/ap_drv_ops.c +ifdef CONFIG_IEEE80211N +OBJS += src/ap/ieee802_11_ht.c +endif +ifdef CONFIG_CTRL_IFACE +OBJS += src/ap/ctrl_iface_ap.c +endif + +L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY +OBJS += src/eap_server/eap_server.c +OBJS += src/eap_server/eap_server_identity.c +OBJS += src/eap_server/eap_server_methods.c + +ifdef CONFIG_IEEE80211N +L_CFLAGS += -DCONFIG_IEEE80211N +endif + +ifdef NEED_AP_MLME +OBJS += src/ap/beacon.c +OBJS += src/ap/wmm.c +OBJS += src/ap/ap_list.c +OBJS += src/ap/ieee802_11.c +OBJS += src/ap/hw_features.c +L_CFLAGS += -DNEED_AP_MLME +endif +ifdef CONFIG_WPS +L_CFLAGS += -DEAP_SERVER_WSC +OBJS += src/ap/wps_hostapd.c +OBJS += src/eap_server/eap_server_wsc.c +endif +endif + +ifdef NEED_RSN_AUTHENTICATOR +L_CFLAGS += -DCONFIG_NO_RADIUS +NEED_AES_WRAP=y +OBJS += src/ap/wpa_auth.c +OBJS += src/ap/wpa_auth_ie.c +OBJS += src/ap/pmksa_cache_auth.c +ifdef CONFIG_IEEE80211R +OBJS += src/ap/wpa_auth_ft.c +endif +ifdef CONFIG_PEERKEY +OBJS += src/ap/peerkey_auth.c +endif +endif + +ifdef CONFIG_EAP_SERVER +L_CFLAGS += -DEAP_SERVER +OBJS_h += src/eap_server/eap_server.c +OBJS_h += src/eap_server/eap_server_identity.c +OBJS_h += src/eap_server/eap_server_methods.c +endif + +ifdef CONFIG_RADIUS_CLIENT +OBJS_h += src/utils/ip_addr.c +OBJS_h += src/radius/radius.c +OBJS_h += src/radius/radius_client.c +endif + +ifdef CONFIG_AUTHENTICATOR +OBJS_h += src/eapol_auth/eapol_auth_sm.c +OBJS_h += src/ap/ieee802_1x.c +endif + +ifdef CONFIG_WPA_AUTHENTICATOR +OBJS_h += src/ap/wpa_auth.c +OBJS_h += src/ap/wpa_auth_ie.c +OBJS_h += src/ap/pmksa_cache_auth.c +ifdef CONFIG_IEEE80211R +OBJS_h += src/ap/wpa_auth_ft.c +endif +ifdef CONFIG_PEERKEY +OBJS_h += src/ap/peerkey_auth.c +endif +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += src/utils/pcsc_funcs.c +# -lpthread may not be needed depending on how pcsc-lite was configured +ifdef CONFIG_NATIVE_WINDOWS +#Once MinGW gets support for WinScard, -lwinscard could be used instead of the +#dynamic symbol loading that is now used in pcsc_funcs.c +#LIBS += -lwinscard +else +LIBS += -lpcsclite -lpthread +endif +endif + +ifdef CONFIG_SIM_SIMULATOR +L_CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +L_CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += src/crypto/milenage.c +endif + +ifdef CONFIG_PKCS12 +L_CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +L_CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += src/crypto/ms_funcs.c +NEED_DES=y +NEED_MD4=y +endif + +ifdef CHAP +OBJS += src/eap_common/chap.c +endif + +ifdef TLS_FUNCS +NEED_DES=y +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +OBJS += src/eap_peer/eap_tls_common.c +OBJS_h += src/eap_server/eap_server_tls_common.c +NEED_TLS_PRF=y +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifeq ($(CONFIG_TLS), openssl) +ifdef TLS_FUNCS +L_CFLAGS += -DEAP_TLS_OPENSSL +OBJS += src/crypto/tls_openssl.c +LIBS += -lssl +endif +OBJS += src/crypto/crypto_openssl.c +OBJS_p += src/crypto/crypto_openssl.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_openssl.c +endif +LIBS += -lcrypto +LIBS_p += -lcrypto +endif + +ifeq ($(CONFIG_TLS), gnutls) +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 +OBJS_p += src/crypto/crypto_gnutls.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_gnutls.c +endif +LIBS += -lgcrypt +LIBS_p += -lgcrypt +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), schannel) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_schannel.c +endif +OBJS += src/crypto/crypto_cryptoapi.c +OBJS_p += src/crypto/crypto_cryptoapi.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_cryptoapi.c +endif +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), nss) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_nss.c +LIBS += -lssl3 +endif +OBJS += src/crypto/crypto_nss.c +OBJS_p += src/crypto/crypto_nss.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_nss.c +endif +LIBS += -lnss3 +LIBS_p += -lnss3 +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +ifdef TLS_FUNCS +OBJS += src/crypto/crypto_internal-rsa.c +OBJS += src/crypto/tls_internal.c +OBJS += src/tls/tlsv1_common.c +OBJS += src/tls/tlsv1_record.c +OBJS += src/tls/tlsv1_cred.c +OBJS += src/tls/tlsv1_client.c +OBJS += src/tls/tlsv1_client_write.c +OBJS += src/tls/tlsv1_client_read.c +OBJS += src/tls/asn1.c +OBJS += src/tls/rsa.c +OBJS += src/tls/x509v3.c +OBJS += src/tls/pkcs1.c +OBJS += src/tls/pkcs5.c +OBJS += src/tls/pkcs8.c +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +NEED_MODEXP=y +NEED_CIPHER=y +L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +endif +ifdef NEED_CIPHER +NEED_DES=y +OBJS += src/crypto/crypto_internal-cipher.c +endif +ifdef NEED_MODEXP +OBJS += src/crypto/crypto_internal-modexp.c +OBJS += src/tls/bignum.c +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += src/crypto/crypto_libtomcrypt.c +OBJS_p += src/crypto/crypto_libtomcrypt.c +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += src/crypto/crypto_internal.c +OBJS_p += src/crypto/crypto_internal.c +NEED_AES_ENC=y +L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +L_CFLAGS += -DLTM_FAST +endif +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += src/crypto/crypto_cryptoapi.c +OBJS_p += src/crypto/crypto_cryptoapi.c +L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif +endif + +ifeq ($(CONFIG_TLS), none) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_none.c +L_CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif +OBJS += src/crypto/crypto_none.c +OBJS_p += src/crypto/crypto_none.c +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif + +ifdef TLS_FUNCS +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +endif + +ifndef TLS_FUNCS +OBJS += src/crypto/tls_none.c +ifeq ($(CONFIG_TLS), internal) +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_RC4=y +endif +endif + +AESOBJS = # none so far (see below) +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c +endif + +AESOBJS += src/crypto/aes-unwrap.c +ifdef NEED_AES_EAX +AESOBJS += src/crypto/aes-eax.c +NEED_AES_CTR=y +endif +ifdef NEED_AES_CTR +AESOBJS += src/crypto/aes-ctr.c +endif +ifdef NEED_AES_ENCBLOCK +AESOBJS += src/crypto/aes-encblock.c +endif +ifdef NEED_AES_OMAC1 +NEED_AES_ENC=y +AESOBJS += src/crypto/aes-omac1.c +endif +ifdef NEED_AES_WRAP +NEED_AES_ENC=y +AESOBJS += src/crypto/aes-wrap.c +endif +ifdef NEED_AES_CBC +NEED_AES_ENC=y +AESOBJS += src/crypto/aes-cbc.c +endif +ifdef NEED_AES_ENC +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal-enc.c +endif +endif +ifdef NEED_AES +OBJS += $(AESOBJS) +endif + +SHA1OBJS = +ifdef NEED_SHA1 +SHA1OBJS += src/crypto/sha1.c +ifdef CONFIG_INTERNAL_SHA1 +SHA1OBJS += src/crypto/sha1-internal.c +ifdef NEED_FIPS186_2_PRF +SHA1OBJS += src/crypto/fips_prf_internal.c +endif +endif +ifndef CONFIG_NO_WPA_PASSPHRASE +SHA1OBJS += src/crypto/sha1-pbkdf2.c +endif +ifdef NEED_T_PRF +SHA1OBJS += src/crypto/sha1-tprf.c +endif +ifdef NEED_TLS_PRF +SHA1OBJS += src/crypto/sha1-tlsprf.c +endif +endif + +MD5OBJS = src/crypto/md5.c +ifdef NEED_MD5 +ifdef CONFIG_INTERNAL_MD5 +MD5OBJS += src/crypto/md5-internal.c +endif +ifdef CONFIG_FIPS +MD5OBJS += src/crypto/md5-non-fips.c +endif +OBJS += $(MD5OBJS) +OBJS_p += $(MD5OBJS) +endif + +ifdef NEED_MD4 +ifdef CONFIG_INTERNAL_MD4 +OBJS += src/crypto/md4-internal.c +endif +endif + +DESOBJS = # none needed when not internal +ifdef NEED_DES +ifdef CONFIG_INTERNAL_DES +DESOBJS += src/crypto/des-internal.c +endif +endif + +ifdef NEED_RC4 +ifdef CONFIG_INTERNAL_RC4 +OBJS += src/crypto/rc4.c +endif +endif + +SHA256OBJS = # none by default +ifdef NEED_SHA256 +L_CFLAGS += -DCONFIG_SHA256 +SHA256OBJS += src/crypto/sha256.c +ifdef CONFIG_INTERNAL_SHA256 +SHA256OBJS += src/crypto/sha256-internal.c +endif +OBJS += $(SHA256OBJS) +endif + +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_groups.c +endif +ifdef NEED_DH_GROUPS_ALL +L_CFLAGS += -DALL_DH_GROUPS +endif +ifdef CONFIG_INTERNAL_DH_GROUP5 +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_group5.c +endif +endif + +ifdef CONFIG_NO_RANDOM_POOL +L_CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +OBJS += src/crypto/random.c +endif + +ifdef CONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), y) +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_CTRL_IFACE=named_pipe +else +CONFIG_CTRL_IFACE=unix +endif +endif +L_CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c +endif + +ifdef CONFIG_CTRL_IFACE_DBUS +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE +DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_old_handlers_wps.c +endif +DBUS_OBJS += dbus/dbus_dict_helpers.c +ifndef DBUS_LIBS +DBUS_LIBS := $(shell pkg-config --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +endif +dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1)) +DBUS_VERSION_MAJOR=$(word 1,$(dbus_version)) +DBUS_VERSION_MINOR=$(word 2,$(dbus_version)) +ifeq ($(DBUS_VERSION_MAJOR),) +DBUS_VERSION_MAJOR=0 +endif +ifeq ($(DBUS_VERSION_MINOR),) +DBUS_VERSION_MINOR=0 +endif +DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR) +DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR) +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef CONFIG_CTRL_IFACE_DBUS_NEW +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +DBUS_OBJS ?= dbus/dbus_dict_helpers.c +DBUS_OBJS += dbus/dbus_new_helpers.c +DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_new_handlers_wps.c +endif +ifndef DBUS_LIBS +DBUS_LIBS := $(shell pkg-config --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +endif +ifdef CONFIG_CTRL_IFACE_DBUS_INTRO +DBUS_OBJS += dbus/dbus_new_introspect.c +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef DBUS +DBUS_CFLAGS += -DCONFIG_DBUS +DBUS_OBJS += dbus/dbus_common.c +endif + +OBJS += $(DBUS_OBJS) +L_CFLAGS += $(DBUS_CFLAGS) +LIBS += $(DBUS_LIBS) + +ifdef CONFIG_READLINE +OBJS_c += src/utils/edit_readline.c +LIBS_c += -lncurses -lreadline +else +ifdef CONFIG_WPA_CLI_EDIT +OBJS_c += src/utils/edit.c +else +OBJS_c += src/utils/edit_simple.c +endif +endif + +ifdef CONFIG_NATIVE_WINDOWS +L_CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 -lgdi32 -lcrypt32 +LIBS_c += -lws2_32 +LIBS_p += -lws2_32 -lgdi32 +ifeq ($(CONFIG_CRYPTO), cryptoapi) +LIBS_p += -lcrypt32 +endif +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +L_CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_ANDROID_LOG +L_CFLAGS += -DCONFIG_ANDROID_LOG +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +L_CFLAGS += -DCONFIG_IPV6 +endif + +ifdef NEED_BASE64 +OBJS += src/utils/base64.c +endif + +ifdef NEED_SME +NEED_80211_COMMON=y +OBJS += sme.c +L_CFLAGS += -DCONFIG_SME +endif + +ifdef CONFIG_CLIENT_MLME +OBJS += mlme.c +L_CFLAGS += -DCONFIG_CLIENT_MLME +NEED_80211_COMMON=y +endif + +ifdef NEED_80211_COMMON +OBJS += src/common/ieee802_11_common.c +endif + +ifdef NEED_EAP_COMMON +OBJS += src/eap_common/eap_common.c +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_SYSLOG +L_CFLAGS += -DCONFIG_DEBUG_SYSLOG +endif + +ifdef CONFIG_DEBUG_FILE +L_CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +ifdef CONFIG_FIPS +L_CFLAGS += -DCONFIG_FIPS +endif + +OBJS += $(SHA1OBJS) $(DESOBJS) + +OBJS_p += $(SHA1OBJS) + +ifdef CONFIG_BGSCAN_SIMPLE +L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE +OBJS += bgscan_simple.c +NEED_BGSCAN=y +endif + +ifdef CONFIG_BGSCAN_LEARN +L_CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.c +NEED_BGSCAN=y +endif + +ifdef NEED_BGSCAN +L_CFLAGS += -DCONFIG_BGSCAN +OBJS += bgscan.c +endif + +OBJS_wpa_rm := ctrl_iface.c mlme.c ctrl_iface_unix.c +OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.c +ifdef CONFIG_AUTHENTICATOR +OBJS_wpa += tests/link_test.c +endif +OBJS_wpa += $(OBJS_l2) +OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c +OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c +OBJS_t += src/radius/radius_client.c +OBJS_t += src/radius/radius.c +ifndef CONFIG_AP +OBJS_t += src/utils/ip_addr.c +endif +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c +OBJS += $(CONFIG_MAIN).c + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) src/drivers/drivers.c +OBJS_priv += $(OBJS_l2) +OBJS_priv += src/utils/os_$(CONFIG_OS).c +OBJS_priv += src/utils/$(CONFIG_ELOOP).c +OBJS_priv += src/utils/common.c +OBJS_priv += src/utils/wpa_debug.c +OBJS_priv += src/utils/wpabuf.c +OBJS_priv += wpa_priv.c +ifdef CONFIG_DRIVER_TEST +OBJS_priv += $(SHA1OBJS) +OBJS_priv += $(MD5OBJS) +ifeq ($(CONFIG_TLS), openssl) +OBJS_priv += src/crypto/crypto_openssl.c +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS_priv += src/crypto/crypto_gnutls.c +endif +ifeq ($(CONFIG_TLS), nss) +OBJS_priv += src/crypto/crypto_nss.c +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS_priv += src/crypto/crypto_libtomcrypt.c +else +OBJS_priv += src/crypto/crypto_internal.c +endif +endif +endif # CONFIG_DRIVER_TEST +OBJS += src/l2_packet/l2_packet_privsep.c +OBJS += src/drivers/driver_privsep.c +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) src/drivers/drivers.c +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += src/drivers/ndis_events.c +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +ifeq ($(WPA_BUILD_SUPPLICANT),true) + +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_cli +LOCAL_MODULE_TAGS := debug +LOCAL_SHARED_LIBRARIES := libc libcutils +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS_c) +LOCAL_C_INCLUDES := $(INCLUDES) +include $(BUILD_EXECUTABLE) + +######################## +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_supplicant +ifdef CONFIG_DRIVER_CUSTOM +LOCAL_STATIC_LIBRARIES := libCustomWifi +endif +ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) +LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB) +endif +LOCAL_SHARED_LIBRARIES := libc libcutils libcrypto libssl +ifdef CONFIG_DRIVER_NL80211 +LOCAL_STATIC_LIBRARIES += libnl_2 +endif +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS) +LOCAL_C_INCLUDES := $(INCLUDES) +include $(BUILD_EXECUTABLE) + +######################## +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := eapol_test +#ifdef CONFIG_DRIVER_CUSTOM +#LOCAL_STATIC_LIBRARIES := libCustomWifi +#endif +#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl +#LOCAL_CFLAGS := $(L_CFLAGS) +#LOCAL_SRC_FILES := $(OBJS_t) +#LOCAL_C_INCLUDES := $(INCLUDES) +#include $(BUILD_EXECUTABLE) +# +######################## +# +#local_target_dir := $(TARGET_OUT)/etc/wifi +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := wpa_supplicant.conf +#LOCAL_MODULE_TAGS := user +#LOCAL_MODULE_CLASS := ETC +#LOCAL_MODULE_PATH := $(local_target_dir) +#LOCAL_SRC_FILES := $(LOCAL_MODULE) +#include $(BUILD_PREBUILT) +# +######################## + +endif # ifeq ($(WPA_BUILD_SUPPLICANT),true) + +include $(CLEAR_VARS) +LOCAL_MODULE = libwpa_client +LOCAL_CFLAGS = $(L_CFLAGS) +LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c +LOCAL_C_INCLUDES = $(INCLUDES) +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_COPY_HEADERS_TO := libwpa_client +LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h +include $(BUILD_SHARED_LIBRARY) diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog new file mode 100644 index 0000000..06119c6 --- /dev/null +++ b/wpa_supplicant/ChangeLog @@ -0,0 +1,1295 @@ +ChangeLog for wpa_supplicant + +2010-04-18 - v0.7.2 + * nl80211: fixed number of issues with roaming + * avoid unnecessary roaming if multiple APs with similar signal + strength are present in scan results + * add TLS client events and server probing to ease design of + automatic detection of EAP parameters + * add option for server certificate matching (SHA256 hash of the + certificate) instead of trusted CA certificate configuration + * bsd: Cleaned up driver wrapper and added various low-level + configuration options + * wpa_gui-qt4: do not show too frequent WPS AP available events as + tray messages + * TNC: fixed issues with fragmentation + * EAP-TNC: add Flags field into fragment acknowledgement (needed to + interoperate with other implementations; may potentially breaks + compatibility with older wpa_supplicant/hostapd versions) + * wpa_cli: added option for using a separate process to receive event + messages to reduce latency in showing these + (CFLAGS += -DCONFIG_WPA_CLI_FORK=y in .config to enable this) + * maximum BSS table size can now be configured (bss_max_count) + * BSSes to be included in the BSS table can be filtered based on + configured SSIDs to save memory (filter_ssids) + * fix number of issues with IEEE 802.11r/FT; this version is not + backwards compatible with old versions + * nl80211: add support for IEEE 802.11r/FT protocol (both over-the-air + and over-the-DS) + * add freq_list network configuration parameter to allow the AP + selection to filter out entries based on the operating channel + * add signal strength change events for bgscan; this allows more + dynamic changes to background scanning interval based on changes in + the signal strength with the current AP; this improves roaming within + ESS quite a bit, e.g., with bgscan="simple:30:-45:300" in the network + configuration block to request background scans less frequently when + signal strength remains good and to automatically trigger background + scans whenever signal strength drops noticeably + (this is currently only available with nl80211) + * add BSSID and reason code (if available) to disconnect event messages + * wpa_gui-qt4: more complete support for translating the GUI with + linguist and add German translation + * fix DH padding with internal crypto code (mainly, for WPS) + * do not trigger initial scan automatically anymore if there are no + enabled networks + +2010-01-16 - v0.7.1 + * cleaned up driver wrapper API (struct wpa_driver_ops); the new API + is not fully backwards compatible, so out-of-tree driver wrappers + will need modifications + * cleaned up various module interfaces + * merge hostapd and wpa_supplicant developers' documentation into a + single document + * nl80211: use explicit deauthentication to clear cfg80211 state to + avoid issues when roaming between APs + * dbus: major design changes in the new D-Bus API + (fi.w1.wpa_supplicant1) + * nl80211: added support for IBSS networks + * added internal debugging mechanism with backtrace support and memory + allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y) + * added WPS ER unsubscription command to more cleanly unregister from + receiving UPnP events when ER is terminated + * cleaned up AP mode operations to avoid need for virtual driver_ops + wrapper + * added BSS table to maintain more complete scan result information + over multiple scans (that may include only partial results) + * wpa_gui-qt4: update Peers dialog information more dynamically while + the dialog is kept open + * fixed PKCS#12 use with OpenSSL 1.0.0 + * driver_wext: Added cfg80211-specific optimization to avoid some + unnecessary scans and to speed up association + +2009-11-21 - v0.7.0 + * increased wpa_cli ping interval to 5 seconds and made this + configurable with a new command line options (-G<seconds>) + * fixed scan buffer processing with WEXT to handle up to 65535 + byte result buffer (previously, limited to 32768 bytes) + * allow multiple driver wrappers to be specified on command line + (e.g., -Dnl80211,wext); the first one that is able to initialize the + interface will be used + * added support for multiple SSIDs per scan request to optimize + scan_ssid=1 operations in ap_scan=1 mode (i.e., search for hidden + SSIDs); this requires driver support and can currently be used only + with nl80211 + * added support for WPS USBA out-of-band mechanism with USB Flash + Drives (UFD) (CONFIG_WPS_UFD=y) + * driver_ndis: add PAE group address to the multicast address list to + fix wired IEEE 802.1X authentication + * fixed IEEE 802.11r key derivation function to match with the standard + (note: this breaks interoperability with previous version) [Bug 303] + * added better support for drivers that allow separate authentication + and association commands (e.g., mac80211-based Linux drivers with + nl80211; SME in wpa_supplicant); this allows over-the-air FT protocol + to be used (IEEE 802.11r) + * fixed SHA-256 based key derivation function to match with the + standard when using CCMP (for IEEE 802.11r and IEEE 802.11w) + (note: this breaks interoperability with previous version) [Bug 307] + * use shared driver wrapper files with hostapd + * added AP mode functionality (CONFIG_AP=y) with mode=2 in the network + block; this can be used for open and WPA2-Personal networks + (optionally, with WPS); this links in parts of hostapd functionality + into wpa_supplicant + * wpa_gui-qt4: added new Peers dialog to show information about peers + (other devices, including APs and stations, etc. in the neighborhood) + * added support for WPS External Registrar functionality (configure APs + and enroll new devices); can be used with wpa_gui-qt4 Peers dialog + and wpa_cli commands wps_er_start, wps_er_stop, wps_er_pin, + wps_er_pbc, wps_er_learn + (this can also be used with a new 'none' driver wrapper if no + wireless device or IEEE 802.1X on wired is needed) + * driver_nl80211: multiple updates to provide support for new Linux + nl80211/mac80211 functionality + * updated management frame protection to use IEEE Std 802.11w-2009 + * fixed number of small WPS issues and added workarounds to + interoperate with common deployed broken implementations + * added support for NFC out-of-band mechanism with WPS + * driver_ndis: fixed wired IEEE 802.1X authentication with PAE group + address frames + * added preliminary support for IEEE 802.11r RIC processing + * added support for specifying subset of enabled frequencies to scan + (scan_freq option in the network configuration block); this can speed + up scanning process considerably if it is known that only a small + subset of channels is actually used in the network (this is currently + supported only with -Dnl80211) + * added a workaround for race condition between receiving the + association event and the following EAPOL-Key + * added background scan and roaming infrastructure to allow + network-specific optimizations to be used to improve roaming within + an ESS (same SSID) + * added new DBus interface (fi.w1.wpa_supplicant1) + +2009-01-06 - v0.6.7 + * added support for Wi-Fi Protected Setup (WPS) + (wpa_supplicant can now be configured to act as a WPS Enrollee to + enroll credentials for a network using PIN and PBC methods; in + addition, wpa_supplicant can act as a wireless WPS Registrar to + configure an AP); WPS support can be enabled by adding CONFIG_WPS=y + into .config and setting the runtime configuration variables in + wpa_supplicant.conf (see WPS section in the example configuration + file); new wpa_cli commands wps_pin, wps_pbc, and wps_reg are used to + manage WPS negotiation; see README-WPS for more details + * added support for EAP-AKA' (draft-arkko-eap-aka-kdf) + * added support for using driver_test over UDP socket + * fixed PEAPv0 Cryptobinding interoperability issue with Windows Server + 2008 NPS; optional cryptobinding is now enabled (again) by default + * fixed PSK editing in wpa_gui + * changed EAP-GPSK to use the IANA assigned EAP method type 51 + * added a Windows installer that includes WinPcap and all the needed + DLLs; in addition, it set up the registry automatically so that user + will only need start wpa_gui to get prompted to start the wpasvc + servide and add a new interface if needed through wpa_gui dialog + * updated management frame protection to use IEEE 802.11w/D7.0 + +2008-11-23 - v0.6.6 + * added Milenage SIM/USIM emulator for EAP-SIM/EAP-AKA + (can be used to simulate test SIM/USIM card with a known private key; + enable with CONFIG_SIM_SIMULATOR=y/CONFIG_USIM_SIMULATOR=y in .config + and password="Ki:OPc"/password="Ki:OPc:SQN" in network configuration) + * added a new network configuration option, wpa_ptk_rekey, that can be + used to enforce frequent PTK rekeying, e.g., to mitigate some attacks + against TKIP deficiencies + * added an optional mitigation mechanism for certain attacks against + TKIP by delaying Michael MIC error reports by a random amount of time + between 0 and 60 seconds; this can be enabled with a build option + CONFIG_DELAYED_MIC_ERROR_REPORT=y in .config + * fixed EAP-AKA to use RES Length field in AT_RES as length in bits, + not bytes + * updated OpenSSL code for EAP-FAST to use an updated version of the + session ticket overriding API that was included into the upstream + OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is + needed with that version anymore) + * updated userspace MLME instructions to match with the current Linux + mac80211 implementation; please also note that this can only be used + with driver_nl80211.c (the old code from driver_wext.c was removed) + * added support (Linux only) for RoboSwitch chipsets (often found in + consumer grade routers); driver interface 'roboswitch' + * fixed canceling of PMKSA caching when using drivers that generate + RSN IE and refuse to drop PMKIDs that wpa_supplicant does not know + about + +2008-11-01 - v0.6.5 + * added support for SHA-256 as X.509 certificate digest when using the + internal X.509/TLSv1 implementation + * updated management frame protection to use IEEE 802.11w/D6.0 + * added support for using SHA256-based stronger key derivation for WPA2 + (IEEE 802.11w) + * fixed FT (IEEE 802.11r) authentication after a failed association to + use correct FTIE + * added support for configuring Phase 2 (inner/tunneled) authentication + method with wpa_gui-qt4 + +2008-08-10 - v0.6.4 + * added support for EAP Sequences in EAP-FAST Phase 2 + * added support for using TNC with EAP-FAST + * added driver_ps3 for the PS3 Linux wireless driver + * added support for optional cryptobinding with PEAPv0 + * fixed the OpenSSL patches (0.9.8g and 0.9.9) for EAP-FAST to + allow fallback to full handshake if server rejects PAC-Opaque + * added fragmentation support for EAP-TNC + * added support for parsing PKCS #8 formatted private keys into the + internal TLS implementation (both PKCS #1 RSA key and PKCS #8 + encapsulated RSA key can now be used) + * added option of using faster, but larger, routines in the internal + LibTomMath (for internal TLS implementation) to speed up DH and RSA + calculations (CONFIG_INTERNAL_LIBTOMMATH_FAST=y) + * fixed race condition between disassociation event and group key + handshake to avoid getting stuck in incorrect state [Bug 261] + * fixed opportunistic key caching (proactive_key_caching) + +2008-02-22 - v0.6.3 + * removed 'nai' and 'eappsk' network configuration variables that were + previously used for configuring user identity and key for EAP-PSK, + EAP-PAX, EAP-SAKE, and EAP-GPSK. 'identity' field is now used as the + replacement for 'nai' (if old configuration used a separate + 'identity' value, that would now be configured as + 'anonymous_identity'). 'password' field is now used as the + replacement for 'eappsk' (it can also be set using hexstring to + present random binary data) + * removed '-w' command line parameter (wait for interface to be added, + if needed); cleaner way of handling this functionality is to use an + external mechanism (e.g., hotplug scripts) that start wpa_supplicant + when an interface is added + * updated FT support to use the latest draft, IEEE 802.11r/D9.0 + * added ctrl_iface monitor event (CTRL-EVENT-SCAN-RESULTS) for + indicating when new scan results become available + * added new ctrl_iface command, BSS, to allow scan results to be + fetched without hitting the message size limits (this command + can be used to iterate through the scan results one BSS at the time) + * fixed EAP-SIM not to include AT_NONCE_MT and AT_SELECTED_VERSION + attributes in EAP-SIM Start/Response when using fast reauthentication + * fixed EAPOL not to end up in infinite loop when processing dynamic + WEP keys with IEEE 802.1X + * fixed problems in getting NDIS events from WMI on Windows 2000 + +2008-01-01 - v0.6.2 + * added support for Makefile builds to include debug-log-to-a-file + functionality (CONFIG_DEBUG_FILE=y and -f<path> on command line) + * fixed EAP-SIM and EAP-AKA message parser to validate attribute + lengths properly to avoid potential crash caused by invalid messages + * added data structure for storing allocated buffers (struct wpabuf); + this does not affect wpa_supplicant usage, but many of the APIs + changed and various interfaces (e.g., EAP) is not compatible with old + versions + * added support for protecting EAP-AKA/Identity messages with + AT_CHECKCODE (optional feature in RFC 4187) + * added support for protected result indication with AT_RESULT_IND for + EAP-SIM and EAP-AKA (phase1="result_ind=1") + * added driver_wext workaround for race condition between scanning and + association with drivers that take very long time to scan all + channels (e.g., madwifi with dual-band cards); wpa_supplicant is now + using a longer hardcoded timeout for the scan if the driver supports + notifications for scan completion (SIOCGIWSCAN event); this helps, + e.g., in cases where wpa_supplicant and madwifi driver ended up in + loop where the driver did not even try to associate + * stop EAPOL timer tick when no timers are in use in order to reduce + power consumption (no need to wake up the process once per second) + [Bug 237] + * added support for privilege separation (run only minimal part of + wpa_supplicant functionality as root and rest as unprivileged, + non-root process); see 'Privilege separation' in README for details; + this is disabled by default and can be enabled with CONFIG_PRIVSEP=y + in .config + * changed scan results data structure to include all information + elements to make it easier to support new IEs; old get_scan_result() + driver_ops is still supported for backwards compatibility (results + are converted internally to the new format), but all drivers should + start using the new get_scan_results2() to make them more likely to + work with new features + * Qt4 version of wpa_gui (wpa_gui-qt4 subdirectory) is now native Qt4 + application, i.e., it does not require Qt3Support anymore; Windows + binary of wpa_gui.exe is now from this directory and only requires + QtCore4.dll and QtGui4.dll libraries + * updated Windows binary build to use Qt 4.3.3 and made Qt DLLs + available as a separate package to make wpa_gui installation easier: + http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip + * added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt); + only shared key/password authentication is supported in this version + +2007-11-24 - v0.6.1 + * added support for configuring password as NtPasswordHash + (16-byte MD4 hash of password) in hash:<32 hex digits> format + * added support for fallback from abbreviated TLS handshake to + full handshake when using EAP-FAST (e.g., due to an expired + PAC-Opaque) + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-07.txt) + * added support for drivers that take care of RSN 4-way handshake + internally (WPA_DRIVER_FLAGS_4WAY_HANDSHAKE in get_capa flags and + WPA_ALG_PMK in set_key) + * added an experimental port for Mac OS X (CONFIG_DRIVER_OSX=y in + .config); this version supports only ap_scan=2 mode and allow the + driver to take care of the 4-way handshake + * fixed a buffer overflow in parsing TSF from scan results when using + driver_wext.c with a driver that includes the TSF (e.g., iwl4965) + [Bug 232] + * updated FT support to use the latest draft, IEEE 802.11r/D8.0 + * fixed an integer overflow issue in the ASN.1 parser used by the + (experimental) internal TLS implementation to avoid a potential + buffer read overflow + * fixed a race condition with -W option (wait for a control interface + monitor before starting) that could have caused the first messages to + be lost + * added support for processing TNCC-TNCS-Messages to report + recommendation (allow/none/isolate) when using TNC [Bug 243] + +2007-05-28 - v0.6.0 + * added network configuration parameter 'frequency' for setting + initial channel for IBSS (adhoc) networks + * added experimental IEEE 802.11r/D6.0 support + * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48 + * updated EAP-PSK to use the IANA-allocated EAP type 47 + * fixed EAP-PAX key derivation + * fixed EAP-PSK bit ordering of the Flags field + * fixed EAP-PEAP/TTLS/FAST to use the correct EAP identifier in + tunnelled identity request (previously, the identifier from the outer + method was used, not the tunnelled identifier which could be + different) + * added support for fragmentation of outer TLS packets during Phase 2 + of EAP-PEAP/TTLS/FAST + * fixed EAP-TTLS AVP parser processing for too short AVP lengths + * added support for EAP-FAST authentication with inner methods that + generate MSK (e.g., EAP-MSCHAPv2 that was previously only supported + for PAC provisioning) + * added support for authenticated EAP-FAST provisioning + * added support for configuring maximum number of EAP-FAST PACs to + store in a PAC list (fast_max_pac_list_len=<max> in phase1 string) + * added support for storing EAP-FAST PACs in binary format + (fast_pac_format=binary in phase1 string) + * fixed dbus ctrl_iface to validate message interface before + dispatching to avoid a possible segfault [Bug 190] + * fixed PeerKey key derivation to use the correct PRF label + * updated Windows binary build to link against OpenSSL 0.9.8d and + added support for EAP-FAST + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-04.txt) + * fixed EAP-AKA Notification processing to allow Notification to be + processed after AKA Challenge response has been sent + * updated to use IEEE 802.11w/D2.0 for management frame protection + (still experimental) + * fixed EAP-TTLS implementation not to crash on use of freed memory + if TLS library initialization fails + * added support for EAP-TNC (Trusted Network Connect) + (this version implements the EAP-TNC method and EAP-TTLS changes + needed to run two methods in sequence (IF-T) and the IF-IMC and + IF-TNCCS interfaces from TNCC) + +2006-11-24 - v0.5.6 + * added experimental, integrated TLSv1 client implementation with the + needed X.509/ASN.1/RSA/bignum processing (this can be enabled by + setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in + .config); this can be useful, e.g., if the target system does not + have a suitable TLS library and a minimal code size is required + (total size of this internal TLS/crypto code is bit under 50 kB on + x86 and the crypto code is shared by rest of the supplicant so some + of it was already required; TLSv1/X.509/ASN.1/RSA added about 25 kB) + * removed STAKey handshake since PeerKey handshake has replaced it in + IEEE 802.11ma and there are no known deployments of STAKey + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-01.txt) + * added preliminary implementation of IEEE 802.11w/D1.0 (management + frame protection) + (Note: this requires driver support to work properly.) + (Note2: IEEE 802.11w is an unapproved draft and subject to change.) + * fixed Windows named pipes ctrl_iface to not stop listening for + commands if client program opens a named pipe and closes it + immediately without sending a command + * fixed USIM PIN status determination for the case that PIN is not + needed (this allows EAP-AKA to be used with USIM cards that do not + use PIN) + * added support for reading 3G USIM AID from EF_DIR to allow EAP-AKA to + be used with cards that do not support file selection based on + partial AID + * added support for matching the subjectAltName of the authentication + server certificate against multiple name components (e.g., + altsubject_match="DNS:server.example.com;DNS:server2.example.com") + * fixed EAP-SIM/AKA key derivation for re-authentication case (only + affects IEEE 802.1X with dynamic WEP keys) + * changed ctrl_iface network configuration 'get' operations to not + return password/key material; if these fields are requested, "*" + will be returned if the password/key is set, but the value of the + parameter is not exposed + +2006-08-27 - v0.5.5 + * added support for building Windows version with UNICODE defined + (wide-char functions) + * driver_ndis: fixed static WEP configuration to avoid race condition + issues with some NDIS drivers between association and setting WEP + keys + * driver_ndis: added validation for IELength value in scan results to + avoid crashes when using buggy NDIS drivers [Bug 165] + * fixed Release|Win32 target in the Visual Studio project files + (previously, only Debug|Win32 target was set properly) + * changed control interface API call wpa_ctrl_pending() to allow it to + return -1 on error (e.g., connection lost); control interface clients + will need to make sure that they verify that the value is indeed >0 + when determining whether there are pending messages + * added an alternative control interface backend for Windows targets: + Named Pipe (CONFIG_CTRL_IFACE=named_pipe); this is now the default + control interface mechanism for Windows builds (previously, UDP to + localhost was used) + * changed ctrl_interface configuration for UNIX domain sockets: + - deprecated ctrl_interface_group variable (it may be removed in + future versions) + - allow both directory and group be configured with ctrl_interface + in following format: DIR=/var/run/wpa_supplicant GROUP=wheel + - ctrl_interface=/var/run/wpa_supplicant is still supported for the + case when group is not changed + * added support for controlling more than one interface per process in + Windows version + * added a workaround for a case where the AP is using unknown address + (e.g., MAC address of the wired interface) as the source address for + EAPOL-Key frames; previously, that source address was used as the + destination for EAPOL-Key frames and in key derivation; now, BSSID is + used even if the source address does not match with it + (this resolves an interoperability issue with Thomson SpeedTouch 580) + * added a workaround for UDP-based control interface (which was used in + Windows builds before this release) to prevent packets with forged + addresses from being accepted as local control requests + * removed ndis_events.cpp and possibility of using external + ndis_events.exe; C version (ndis_events.c) is fully functional and + there is no desire to maintain two separate versions of this + implementation + * ndis_events: Changed NDIS event notification design to use WMI to + learn the adapter description through Win32_PnPEntity class; this + should fix some cases where the adapter name was not recognized + correctly (e.g., with some USB WLAN adapters, e.g., Ralink RT2500 + USB) [Bug 113] + * fixed selection of the first network in ap_scan=2 mode; previously, + wpa_supplicant could get stuck in SCANNING state when only the first + network for enabled (e.g., after 'wpa_cli select_network 0') + * winsvc: added support for configuring ctrl_interface parameters in + registry (ctrl_interface string value in + HKLM\SOFTWARE\wpa_supplicant\interfaces\0000 key); this new value is + required to enable control interface (previously, this was hardcoded + to be enabled) + * allow wpa_gui subdirectory to be built with both Qt3 and Qt4 + * converted wpa_gui-qt4 subdirectory to use Qt4 specific project format + +2006-06-20 - v0.5.4 + * fixed build with CONFIG_STAKEY=y [Bug 143] + * added support for doing MLME (IEEE 802.11 management frame + processing) in wpa_supplicant when using Devicescape IEEE 802.11 + stack (wireless-dev.git tree) + * added a new network block configuration option, fragment_size, to + configure the maximum EAP fragment size + * driver_ndis: Disable WZC automatically for the selected interface to + avoid conflicts with two programs trying to control the radio; WZC + will be re-enabled (if it was enabled originally) when wpa_supplicant + is terminated + * added an experimental TLSv1 client implementation + (CONFIG_TLS=internal) that can be used instead of an external TLS + library, e.g., to reduce total size requirement on systems that do + not include any TLS library by default (this is not yet complete; + basic functionality is there, but certificate validation is not yet + included) + * added PeerKey handshake implementation for IEEE 802.11e + direct link setup (DLS) to replace STAKey handshake + * fixed WPA PSK update through ctrl_iface for the case where the old + PSK was derived from an ASCII passphrase and the new PSK is set as + a raw PSK (hex string) + * added new configuration option for identifying which network block + was used (id_str in wpa_supplicant.conf; included on + WPA_EVENT_CONNECT monitor event and as WPA_ID_STR environmental + variable in wpa_cli action scripts; in addition WPA_ID variable is + set to the current unique identifier that wpa_supplicant assigned + automatically for the network and that can be used with + GET_NETWORK/SET_NETWORK ctrl_iface commands) + * wpa_cli action script is now called only when the connect/disconnect + status changes or when associating with a different network + * fixed configuration parser not to remove CCMP from group cipher list + if WPA-None (adhoc) is used (pairwise=NONE in that case) + * fixed integrated NDIS events processing not to hang the process due + to a missed change in eloop_win.c API in v0.5.3 [Bug 155] + * added support for EAP Generalized Pre-Shared Key (EAP-GPSK, + draft-clancy-emu-eap-shared-secret-00.txt) + * added Microsoft Visual Studio 2005 solution and project files for + build wpa_supplicant for Windows (see vs2005 subdirectory) + * eloop_win: fixed unregistration of Windows events + * l2_packet_winpcap: fixed a deadlock in deinitializing l2_packet + at the end of RSN pre-authentication and added unregistration of + a Windows event to avoid getting eloop_win stuck with an invalid + handle + * driver_ndis: added support for selecting AP based on BSSID + * added new environmental variable for wpa_cli action scripts: + WPA_CTRL_DIR is the current control interface directory + * driver_ndis: added support for using NDISUIO instead of WinPcap for + OID set/query operations (CONFIG_USE_NDISUIO=y in .config); with new + l2_packet_ndis (CONFIG_L2_PACKET=ndis), this can be used to build + wpa_supplicant without requiring WinPcap; note that using NDISUIO + requires that WZC is disabled (net stop wzcsvc) since NDISUIO allows + only one application to open the device + * changed NDIS driver naming to only include device GUID, e.g., + {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D}, instead of including WinPcap + specific \Device\NPF_ prefix before the GUID; the prefix is still + allowed for backwards compatibility, but it is not required anymore + when specifying the interface + * driver_ndis: re-initialize driver interface is the adapter is removed + and re-inserted [Bug 159] + * driver_madwifi: fixed TKIP and CCMP sequence number configuration on + big endian hosts [Bug 146] + +2006-04-27 - v0.5.3 + * fixed EAP-GTC response to include correct user identity when run as + phase 2 method of EAP-FAST (i.e., EAP-FAST did not work in v0.5.2) + * driver_ndis: Fixed encryption mode configuration for unencrypted + networks (some NDIS drivers ignored this, but others, e.g., Broadcom, + refused to associate with open networks) [Bug 106] + * driver_ndis: use BSSID OID polling to detect when IBSS network is + formed even when ndis_events code is included since some NDIS drivers + do not generate media connect events in IBSS mode + * config_winreg: allow global ctrl_interface parameter to be configured + in Windows registry + * config_winreg: added support for saving configuration data into + Windows registry + * added support for controlling network device operational state + (dormant/up) for Linux 2.6.17 to improve DHCP processing (see + http://www.flamewarmaster.de/software/dhcpclient/ for a DHCP client + that can use this information) + * driver_wext: added support for WE-21 change to SSID configuration + * driver_wext: fixed privacy configuration for static WEP keys mode + [Bug 140] + * added an optional driver_ops callback for MLME-SETPROTECTION.request + primitive + * added support for EAP-SAKE (no EAP method number allocated yet, so + this is using the same experimental type 255 as EAP-PSK) + * added support for dynamically loading EAP methods (.so files) instead + of requiring them to be statically linked in; this is disabled by + default (see CONFIG_DYNAMIC_EAP_METHODS in defconfig for information + on how to use this) + +2006-03-19 - v0.5.2 + * do not try to use USIM APDUs when initializing PC/SC for SIM card + access for a network that has not enabled EAP-AKA + * fixed EAP phase 2 Nak for EAP-{PEAP,TTLS,FAST} (this was broken in + v0.5.1 due to the new support for expanded EAP types) + * added support for generating EAP Expanded Nak + * try to fetch scan results once before requesting new scan when + starting up in ap_scan=1 mode (this can speed up initial association + a lot with, e.g., madwifi-ng driver) + * added support for receiving EAPOL frames from a Linux bridge + interface (-bbr0 on command line) + * fixed EAPOL re-authentication for sessions that used PMKSA caching + * changed EAP method registration to use a dynamic list of methods + instead of a static list generated at build time + * fixed PMKSA cache deinitialization not to use freed memory when + removing PMKSA entries + * fixed a memory leak in EAP-TTLS re-authentication + * reject WPA/WPA2 message 3/4 if it does not include any valid + WPA/RSN IE + * driver_wext: added fallback to use SIOCSIWENCODE for setting auth_alg + if the driver does not support SIOCSIWAUTH + +2006-01-29 - v0.5.1 + * driver_test: added better support for multiple APs and STAs by using + a directory with sockets that include MAC address for each device in + the name (driver_param=test_dir=/tmp/test) + * added support for EAP expanded type (vendor specific EAP methods) + * added AP_SCAN command into ctrl_iface so that ap_scan configuration + option can be changed if needed + * wpa_cli/wpa_gui: skip non-socket files in control directory when + using UNIX domain sockets; this avoids selecting an incorrect + interface (e.g., a PID file could be in this directory, even though + use of this directory for something else than socket files is not + recommended) + * fixed TLS library deinitialization after RSN pre-authentication not + to disable TLS library for normal authentication + * driver_wext: Remove null-termination from SSID length if the driver + used it; some Linux drivers do this and they were causing problems in + wpa_supplicant not finding matching configuration block. This change + would break a case where the SSID actually ends in '\0', but that is + not likely to happen in real use. + * fixed PMKSA cache processing not to trigger deauthentication if the + current PMKSA cache entry is replaced with a valid new entry + * fixed PC/SC initialization for ap_scan != 1 modes (this fixes + EAP-SIM and EAP-AKA with real SIM/USIM card when using ap_scan=0 or + ap_scan=2) + +2005-12-18 - v0.5.0 (beginning of 0.5.x development releases) + * added experimental STAKey handshake implementation for IEEE 802.11e + direct link setup (DLS); note: this is disabled by default in both + build and runtime configuration (can be enabled with CONFIG_STAKEY=y + and stakey=1) + * fixed EAP-SIM and EAP-AKA pseudonym and fast re-authentication to + decrypt AT_ENCR_DATA attributes correctly + * fixed EAP-AKA to allow resynchronization within the same session + * made code closer to ANSI C89 standard to make it easier to port to + other C libraries and compilers + * started moving operating system or C library specific functions into + wrapper functions defined in os.h and implemented in os_*.c to make + code more portable + * wpa_supplicant can now be built with Microsoft Visual C++ + (e.g., with the freely available Toolkit 2003 version or Visual + C++ 2005 Express Edition and Platform SDK); see nmake.mak for an + example makefile for nmake + * added support for using Windows registry for command line parameters + (CONFIG_MAIN=main_winsvc) and configuration data + (CONFIG_BACKEND=winreg); see win_example.reg for an example registry + contents; this version can be run both as a Windows service and as a + normal application; 'wpasvc.exe app' to start as applicant, + 'wpasvc.exe reg <full path to wpasvc.exe>' to register a service, + 'net start wpasvc' to start the service, 'wpasvc.exe unreg' to + unregister a service + * made it possible to link ndis_events.exe functionality into + wpa_supplicant.exe by defining CONFIG_NDIS_EVENTS_INTEGRATED + * added better support for multiple control interface backends + (CONFIG_CTRL_IFACE option); currently, 'unix' and 'udp' are supported + * fixed PC/SC code to use correct length for GSM AUTH command buffer + and to not use pioRecvPci with SCardTransmit() calls; these were not + causing visible problems with pcsc-lite, but Windows Winscard.dll + refused the previously used parameters; this fixes EAP-SIM and + EAP-AKA authentication using SIM/USIM card under Windows + * added new event loop implementation for Windows using + WaitForMultipleObject() instead of select() in order to allow waiting + for non-socket objects; this can be selected with + CONFIG_ELOOP=eloop_win in .config + * added support for selecting l2_packet implementation in .config + (CONFIG_L2_PACKET; following options are available now: linux, pcap, + winpcap, freebsd, none) + * added new l2_packet implementation for WinPcap + (CONFIG_L2_PACKET=winpcap) that uses a separate receive thread to + reduce latency in EAPOL receive processing from about 100 ms to about + 3 ms + * added support for EAP-FAST key derivation using other ciphers than + RC4-128-SHA for authentication and AES128-SHA for provisioning + * added support for configuring CA certificate as DER file and as a + configuration blob + * fixed private key configuration as configuration blob and added + support for using PKCS#12 as a blob + * tls_gnutls: added support for using PKCS#12 files; added support for + session resumption + * added support for loading trusted CA certificates from Windows + certificate store: ca_cert="cert_store://<name>", where <name> is + likely CA (Intermediate CA certificates) or ROOT (root certificates) + * added C version of ndis_events.cpp and made it possible to build this + with MinGW so that CONFIG_NDIS_EVENTS_INTEGRATED can be used more + easily on cross-compilation builds + * added wpasvc.exe into Windows binary release; this is an alternative + version of wpa_supplicant.exe with configuration backend using + Windows registry and with the entry point designed to run as a + Windows service + * integrated ndis_events.exe functionality into wpa_supplicant.exe and + wpasvc.exe and removed this additional tool from the Windows binary + release since it is not needed anymore + * load winscard.dll functions dynamically when building with MinGW + since MinGW does not yet include winscard library + +2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases) + * l2_packet_pcap: fixed wired IEEE 802.1X authentication with libpcap + and WinPcap to receive frames sent to PAE group address + * disable EAP state machine when IEEE 802.1X authentication is not used + in order to get rid of bogus "EAP failed" messages + * fixed OpenSSL error reporting to go through all pending errors to + avoid confusing reports of old errors being reported at later point + during handshake + * fixed configuration file updating to not write empty variables + (e.g., proto or key_mgmt) that the file parser would not accept + * fixed ADD_NETWORK ctrl_iface command to use the same default values + for variables as empty network definitions read from config file + would get + * fixed EAP state machine to not discard EAP-Failure messages in many + cases (e.g., during TLS handshake) + * fixed a infinite loop in private key reading if the configured file + cannot be parsed successfully + * driver_madwifi: added support for madwifi-ng + * wpa_gui: do not display password/PSK field contents + * wpa_gui: added CA certificate configuration + * driver_ndis: fixed scan request in ap_scan=2 mode not to change SSID + * driver_ndis: include Beacon IEs in AssocInfo in order to notice if + the new AP is using different WPA/RSN IE + * use longer timeout for IEEE 802.11 association to avoid problems with + drivers that may take more than five second to associate + +2005-10-27 - v0.4.6 + * allow fallback to WPA, if mixed WPA+WPA2 networks have mismatch in + RSN IE, but WPA IE would match with wpa_supplicant configuration + * added support for named configuration blobs in order to avoid having + to use file system for external files (e.g., certificates); + variables can be set to "blob://<blob name>" instead of file path to + use a named blob; supported fields: pac_file, client_cert, + private_key + * fixed RSN pre-authentication (it was broken in the clean up of WPA + state machine interface in v0.4.5) + * driver_madwifi: set IEEE80211_KEY_GROUP flag for group keys to make + sure the driver configures broadcast decryption correctly + * added ca_path (and ca_path2) configuration variables that can be used + to configure OpenSSL CA path, e.g., /etc/ssl/certs, for using the + system-wide trusted CA list + * added support for starting wpa_supplicant without a configuration + file (-C argument must be used to set ctrl_interface parameter for + this case; in addition, -p argument can be used to provide + driver_param; these new arguments can also be used with a + configuration to override the values from the configuration) + * added global control interface that can be optionally used for adding + and removing network interfaces dynamically (-g command line argument + for both wpa_supplicant and wpa_cli) without having to restart + wpa_supplicant process + * wpa_gui: + - try to save configuration whenever something is modified + - added WEP key configuration + - added possibility to edit the current network configuration + * driver_ndis: fixed driver polling not to increase frequency on each + received EAPOL frame due to incorrectly cancelled timeout + * added simple configuration file examples (in examples subdirectory) + * fixed driver_wext.c to filter wireless events based on ifindex to + avoid interfaces receiving events from other interfaces + * delay sending initial EAPOL-Start couple of seconds to speed up + authentication for the most common case of Authenticator starting + EAP authentication immediately after association + +2005-09-25 - v0.4.5 + * added a workaround for clearing keys with ndiswrapper to allow + roaming from WPA enabled AP to plaintext one + * added docbook documentation (doc/docbook) that can be used to + generate, e.g., man pages + * l2_packet_linux: use socket type SOCK_DGRAM instead of SOCK_RAW for + PF_PACKET in order to prepare for network devices that do not use + Ethernet headers (e.g., network stack with native IEEE 802.11 frames) + * use receipt of EAPOL-Key frame as a lower layer success indication + for EAP state machine to allow recovery from dropped EAP-Success + frame + * cleaned up internal EAPOL frame processing by not including link + layer (Ethernet) header during WPA and EAPOL/EAP processing; this + header is added only when transmitted the frame; this makes it easier + to use wpa_supplicant on link layers that use different header than + Ethernet + * updated EAP-PSK to use draft 9 by default since this can now be + tested with hostapd; removed support for draft 3, including + server_nai configuration option from network blocks + * driver_wired: add PAE address to the multicast address list in order + to be able to receive EAPOL frames with drivers that do not include + these multicast addresses by default + * driver_wext: add support for WE-19 + * added support for multiple configuration backends (CONFIG_BACKEND + option); currently, only 'file' is supported (i.e., the format used + in wpa_supplicant.conf) + * added support for updating configuration ('wpa_cli save_config'); + this is disabled by default and can be enabled with global + update_config=1 variable in wpa_supplicant.conf; this allows wpa_cli + and wpa_gui to store the configuration changes in a permanent store + * added GET_NETWORK ctrl_iface command + (e.g., 'wpa_cli get_network 0 ssid') + +2005-08-21 - v0.4.4 + * replaced OpenSSL patch for EAP-FAST support + (openssl-tls-extensions.patch) with a more generic and correct + patch (the new patch is not compatible with the previous one, so the + OpenSSL library will need to be patched with the new patch in order + to be able to build wpa_supplicant with EAP-FAST support) + * added support for using Windows certificate store (through CryptoAPI) + for client certificate and private key operations (EAP-TLS) + (see wpa_supplicant.conf for more information on how to configure + this with private_key) + * ported wpa_gui to Windows + * added Qt4 version of wpa_gui (wpa_gui-qt4 directory); this can be + built with the open source version of the Qt4 for Windows + * allow non-WPA modes (e.g., IEEE 802.1X with dynamic WEP) to be used + with drivers that do not support WPA + * ndis_events: fixed Windows 2000 support + * added support for enabling/disabling networks from the list of all + configured networks ('wpa_cli enable_network <network id>' and + 'wpa_cli disable_network <network id>') + * added support for adding and removing network from the current + configuration ('wpa_cli add_network' and 'wpa_cli remove_network + <network id>'); added networks are disabled by default and they can + be enabled with enable_network command once the configuration is done + for the new network; note: configuration file is not yet updated, so + these new networks are lost when wpa_supplicant is restarted + * added support for setting network configuration parameters through + the control interface, for example: + wpa_cli set_network 0 ssid "\"my network\"" + * fixed parsing of strings that include both " and # within double + quoted area (e.g., "start"#end") + * added EAP workaround for PEAP session resumption: allow outer, + i.e., not tunneled, EAP-Success to terminate session since; this can + be disabled with eap_workaround=0 + (this was allowed for PEAPv1 before, but now it is also allowed for + PEAPv0 since at least one RADIUS authentication server seems to be + doing this for PEAPv0, too) + * wpa_gui: added preliminary support for adding new networks to the + wpa_supplicant configuration (double click on the scan results to + open network configuration) + +2005-06-26 - v0.4.3 + * removed interface for external EAPOL/EAP supplicant (e.g., + Xsupplicant), (CONFIG_XSUPPLICANT_IFACE) since it is not required + anymore and is unlikely to be used by anyone + * driver_ndis: fixed WinPcap 3.0 support + * fixed build with CONFIG_DNET_PCAP=y on Linux + * l2_packet: moved different implementations into separate files + (l2_packet_*.c) + +2005-06-12 - v0.4.2 + * driver_ipw: updated driver structures to match with ipw2200-1.0.4 + (note: ipw2100-1.1.0 is likely to require an update to work with + this) + * added support for using ap_scan=2 mode with multiple network blocks; + wpa_supplicant will go through the networks one by one until the + driver reports a successful association; this uses the same order for + networks as scan_ssid=1 scans, i.e., the priority field is ignored + and the network block order in the file is used instead + * fixed a potential issue in RSN pre-authentication ending up using + freed memory if pre-authentication times out + * added support for matching alternative subject name extensions of the + authentication server certificate; new configuration variables + altsubject_match and altsubject_match2 + * driver_ndis: added support for IEEE 802.1X authentication with wired + NDIS drivers + * added support for querying private key password (EAP-TLS) through the + control interface (wpa_cli/wpa_gui) if one is not included in the + configuration file + * driver_broadcom: fixed couple of memory leaks in scan result + processing + * EAP-PAX is now registered as EAP type 46 + * fixed EAP-PAX MAC calculation + * fixed EAP-PAX CK and ICK key derivation + * added support for using password with EAP-PAX (as an alternative to + entering key with eappsk); SHA-1 hash of the password will be used as + the key in this case + * added support for arbitrary driver interface parameters through the + configuration file with a new driver_param field; this adds a new + driver_ops function set_param() + * added possibility to override l2_packet module with driver interface + API (new send_eapol handler); this can be used to implement driver + specific TX/RX functions for EAPOL frames + * fixed ctrl_interface_group processing for the case where gid is + entered as a number, not group name + * driver_test: added support for testing hostapd with wpa_supplicant + by using test driver interface without any kernel drivers or network + cards + +2005-05-22 - v0.4.1 + * driver_madwifi: fixed WPA/WPA2 mode configuration to allow EAPOL + packets to be encrypted; this was apparently broken by the changed + ioctl order in v0.4.0 + * driver_madwifi: added preliminary support for compiling against 'BSD' + branch of madwifi CVS tree + * added support for EAP-MSCHAPv2 password retries within the same EAP + authentication session + * added support for password changes with EAP-MSCHAPv2 (used when the + password has expired) + * added support for reading additional certificates from PKCS#12 files + and adding them to the certificate chain + * fixed association with IEEE 802.1X (no WPA) when dynamic WEP keys + were used + * fixed a possible double free in EAP-TTLS fast-reauthentication when + identity or password is entered through control interface + * display EAP Notification messages to user through control interface + with "CTRL-EVENT-EAP-NOTIFICATION" prefix + * added GUI version of wpa_cli, wpa_gui; this is not build + automatically with 'make'; use 'make wpa_gui' to build (this requires + Qt development tools) + * added 'disconnect' command to control interface for setting + wpa_supplicant in state where it will not associate before + 'reassociate' command has been used + * added support for selecting a network from the list of all configured + networks ('wpa_cli select_network <network id>'; this disabled all + other networks; to re-enable, 'wpa_cli select_network any') + * added support for getting scan results through control interface + * added EAP workaround for PEAPv1 session resumption: allow outer, + i.e., not tunneled, EAP-Success to terminate session since; this can + be disabled with eap_workaround=0 + +2005-04-25 - v0.4.0 (beginning of 0.4.x development releases) + * added a new build time option, CONFIG_NO_STDOUT_DEBUG, that can be + used to reduce the size of the wpa_supplicant considerably if + debugging code is not needed + * fixed EAPOL-Key validation to drop packets with invalid Key Data + Length; such frames could have crashed wpa_supplicant due to buffer + overflow + * added support for wired authentication (IEEE 802.1X on wired + Ethernet); driver interface 'wired' + * obsoleted set_wpa() handler in the driver interface API (it can be + replaced by moving enable/disable functionality into init()/deinit()) + (calls to set_wpa() are still present for backwards compatibility, + but they may be removed in the future) + * driver_madwifi: fixed association in plaintext mode + * modified the EAP workaround that accepts EAP-Success with incorrect + Identifier to be even less strict about verification in order to + interoperate with some authentication servers + * added support for sending TLS alerts + * added support for 'any' SSID wildcard; if ssid is not configured or + is set to an empty string, any SSID will be accepted for non-WPA AP + * added support for asking PIN (for SIM) from frontends (e.g., + wpa_cli); if a PIN is needed, but not included in the configuration + file, a control interface request is sent and EAP processing is + delayed until the PIN is available + * added support for using external devices (e.g., a smartcard) for + private key operations in EAP-TLS (CONFIG_SMARTCARD=y in .config); + new wpa_supplicant.conf variables: + - global: opensc_engine_path, pkcs11_engine_path, pkcs11_module_path + - network: engine, engine_id, key_id + * added experimental support for EAP-PAX + * added monitor mode for wpa_cli (-a<path to a program to run>) that + allows external commands (e.g., shell scripts) to be run based on + wpa_supplicant events, e.g., when authentication has been completed + and data connection is ready; other related wpa_cli arguments: + -B (run in background), -P (write PID file); wpa_supplicant has a new + command line argument (-W) that can be used to make it wait until a + control interface command is received in order to avoid missing + events + * added support for opportunistic WPA2 PMKSA key caching (disabled by + default, can be enabled with proactive_key_caching=1) + * fixed RSN IE in 4-Way Handshake message 2/4 for the case where + Authenticator rejects PMKSA caching attempt and the driver is not + using assoc_info events + * added -P<pid file> argument for wpa_supplicant to write the current + process id into a file + +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases) + * added new phase1 option parameter, include_tls_length=1, to force + wpa_supplicant to add TLS Message Length field to all TLS messages + even if the packet is not fragmented; this may be needed with some + authentication servers + * fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when + using drivers that take care of AP selection (e.g., when using + ap_scan=2) + * fixed reprocessing of pending request after ctrl_iface requests for + identity/password/otp + * fixed ctrl_iface requests for identity/password/otp in Phase 2 of + EAP-PEAP and EAP-TTLS + * all drivers using driver_wext: set interface up and select Managed + mode when starting wpa_supplicant; set interface down when exiting + * renamed driver_ipw2100.c to driver_ipw.c since it now supports both + ipw2100 and ipw2200; please note that this also changed the + configuration variable in .config to CONFIG_DRIVER_IPW + +2005-01-24 - v0.3.6 + * fixed a busy loop introduced in v0.3.5 for scan result processing + when no matching AP is found + +2005-01-23 - v0.3.5 + * added a workaround for an interoperability issue with a Cisco AP + when using WPA2-PSK + * fixed non-WPA IEEE 802.1X to use the same authentication timeout as + WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow + retransmission of dropped frames) + * fixed issues with 64-bit CPUs and SHA1 cleanup in previous version + (e.g., segfault when processing EAPOL-Key frames) + * fixed EAP workaround and fast reauthentication configuration for + RSN pre-authentication; previously these were disabled and + pre-authentication would fail if the used authentication server + requires EAP workarounds + * added support for blacklisting APs that fail or timeout + authentication in ap_scan=1 mode so that all APs are tried in cases + where the ones with strongest signal level are failing authentication + * fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS + authentication attempt + * allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded + in the previous authentication (previously, only Phase 1 success was + verified) + +2005-01-09 - v0.3.4 + * added preliminary support for IBSS (ad-hoc) mode configuration + (mode=1 in network block); this included a new key_mgmt mode + WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no + key management; see wpa_supplicant.conf for more details and an + example on how to configure this (note: this is currently implemented + only for driver_hostapd.c, but the changes should be trivial to add + in associate() handler for other drivers, too (assuming the driver + supports WPA-None) + * added preliminary port for native Windows (i.e., no cygwin) using + mingw + +2005-01-02 - v0.3.3 + * added optional support for GNU Readline and History Libraries for + wpa_cli (CONFIG_READLINE) + * cleaned up EAP state machine <-> method interface and number of + small problems with error case processing not terminating on + EAP-Failure but waiting for timeout + * added couple of workarounds for interoperability issues with a + Cisco AP when using WPA2 + * added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt); + Note: This requires a patch for openssl to add support for TLS + extensions and number of workarounds for operations without + certificates. Proof of concept type of experimental patch is + included in openssl-tls-extensions.patch. + +2004-12-19 - v0.3.2 + * fixed private key loading for cases where passphrase is not set + * fixed Windows/cygwin L2 packet handler freeing; previous version + could cause a segfault when RSN pre-authentication was completed + * added support for PMKSA caching with drivers that generate RSN IEs + (e.g., NDIS); currently, this is only implemented in driver_ndis.c, + but similar code can be easily added to driver_ndiswrapper.c once + ndiswrapper gets full support for RSN PMKSA caching + * improved recovery from PMKID mismatches by requesting full EAP + authentication in case of failed PMKSA caching attempt + * driver_ndis: added support for NDIS NdisMIncidateStatus() events + (this requires that ndis_events is ran while wpa_supplicant is + running) + * driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys + * added support for driver interfaces to replace the interface name + based on driver/OS specific mapping, e.g., in case of driver_ndis, + this allows the beginning of the adapter description to be used as + the interface name + * added support for CR+LF (Windows-style) line ends in configuration + file + * driver_ndis: enable radio before starting scanning, disable radio + when exiting + * modified association event handler to set portEnabled = FALSE before + clearing port Valid in order to reset EAP state machine and avoid + problems with new authentication getting ignored because of state + machines ending up in AUTHENTICATED/SUCCESS state based on old + information + * added support for driver events to add PMKID candidates in order to + allow drivers to give priority to most likely roaming candidates + * driver_hostap: moved PrivacyInvoked configuration to associate() + function so that this will not be set for plaintext connections + * added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver + interface can distinguish plaintext and IEEE 802.1X (no WPA) + authentication + * fixed static WEP key configuration to use broadcast/default type for + all keys (previously, the default TX key was configured as pairwise/ + unicast key) + * driver_ndis: added legacy WPA capability detection for non-WPA2 + drivers + * added support for setting static WEP keys for IEEE 802.1X without + dynamic WEP keying (eapol_flags=0) + +2004-12-12 - v0.3.1 + * added support for reading PKCS#12 (PFX) files (as a replacement for + PEM/DER) to get certificate and private key (CONFIG_PKCS12) + * fixed compilation with CONFIG_PCSC=y + * added new ap_scan mode, ap_scan=2, for drivers that take care of + association, but need to be configured with security policy and SSID, + e.g., ndiswrapper and NDIS driver; this mode should allow such + drivers to work with hidden SSIDs and optimized roaming; when + ap_scan=2 is used, only the first network block in the configuration + file is used and this configuration should have explicit security + policy (i.e., only one option in the lists) for key_mgmt, pairwise, + group, proto variables + * added experimental port of wpa_supplicant for Windows + - driver_ndis.c driver interface (NDIS OIDs) + - currently, this requires cygwin and WinPcap + - small utility, win_if_list, can be used to get interface name + * control interface can now be removed at build time; add + CONFIG_CTRL_IFACE=y to .config to maintain old functionality + * optional Xsupplicant interface can now be removed at build time; + (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back) + * added auth_alg to driver interface associate() parameters to make it + easier for drivers to configure authentication algorithm as part of + the association + +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases) + * driver_broadcom: added new driver interface for Broadcom wl.o driver + (a generic driver for Broadcom IEEE 802.11a/g cards) + * wpa_cli: fixed parsing of -p <path> command line argument + * PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS + ACK, not tunneled EAP-Success (of which only the first byte was + actually send due to a bug in previous code); this seems to + interoperate with most RADIUS servers that implements PEAPv1 + * PEAPv1: added support for terminating PEAP authentication on tunneled + EAP-Success message; this can be configured by adding + peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf + (some RADIUS servers require this whereas others require a tunneled + reply + * PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to + the old label for key derivation; previously, the default was 1, + but it looks like most existing PEAPv1 implementations use the old + label which is thus more suitable default option + * added support for EAP-PSK (draft-bersani-eap-psk-03.txt) + * fixed parsing of wep_tx_keyidx + * added support for configuring list of allowed Phase 2 EAP types + (for both EAP-PEAP and EAP-TTLS) instead of only one type + * added support for configuring IEEE 802.11 authentication algorithm + (auth_alg; mainly for using Shared Key authentication with static + WEP keys) + * added support for EAP-AKA (with UMTS SIM) + * fixed couple of errors in PCSC handling that could have caused + random-looking errors for EAP-SIM + * added support for EAP-SIM pseudonyms and fast re-authentication + * added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS + session resumption) + * added support for EAP-SIM with two challanges + (phase1="sim_min_num_chal=3" can be used to require three challenges) + * added support for configuring DH/DSA parameters for an ephemeral DH + key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters + dh_file and dh_file2 (phase 2); this adds support for using DSA keys + and optional DH key exchange to achieve forward secracy with RSA keys + * added support for matching subject of the authentication server + certificate with a substring when using EAP-TLS/PEAP/TTLS; new + configuration variables subject_match and subject_match2 + * changed SSID configuration in driver_wext.c (used by many driver + interfaces) to use ssid_len+1 as the length for SSID since some Linux + drivers expect this + * fixed couple of unaligned reads in scan result parsing to fix WPA + connection on some platforms (e.g., ARM) + * added driver interface for Intel ipw2100 driver + * added support for LEAP with WPA + * added support for larger scan results report (old limit was 4 kB of + data, i.e., about 35 or so APs) when using Linux wireless extensions + v17 or newer + * fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start + only if there is a PMKSA cache entry for the current AP + * fixed error handling for case where reading of scan results fails: + must schedule a new scan or wpa_supplicant will remain waiting + forever + * changed debug output to remove shared password/key material by + default; all key information can be included with -K command line + argument to match the previous behavior + * added support for timestamping debug log messages (disabled by + default, can be enabled with -t command line argument) + * set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104 + if keys are not configured to be used; this fixes IEEE 802.1X mode + with drivers that use this information to configure whether Privacy + bit can be in Beacon frames (e.g., ndiswrapper) + * avoid clearing driver keys if no keys have been configured since last + key clear request; this seems to improve reliability of group key + handshake for ndiswrapper & NDIS driver which seems to be suffering + of some kind of timing issue when the keys are cleared again after + association + * changed driver interface API: + - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which + version is being used (now, this is set to 2; previously, it was + not defined) + - pass pointer to private data structure to all calls + - the new API is not backwards compatible; all in-tree driver + interfaces has been converted to the new API + * added support for controlling multiple interfaces (radios) per + wpa_supplicant process; each interface needs to be listed on the + command line (-c, -i, -D arguments) with -N as a separator + (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi) + * added a workaround for EAP servers that incorrectly use same Id for + sequential EAP packets + * changed libpcap/libdnet configuration to use .config variable, + CONFIG_DNET_PCAP, instead of requiring Makefile modification + * improved downgrade attack detection in IE verification of msg 3/4: + verify both WPA and RSN IEs, if present, not only the selected one; + reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or + Probe Response frame, and RSN is enabled in wpa_supplicant + configuration + * fixed WPA msg 3/4 processing to allow Key Data field contain other + IEs than just one WPA IE + * added support for FreeBSD and driver interface for the BSD net80211 + layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the + required kernel mods have not yet been committed + * made EAP workarounds configurable; enabled by default, can be + disabled with network block option eap_workaround=0 + +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases) + * resolved couple of interoperability issues with EAP-PEAPv1 and + Phase 2 (inner EAP) fragment reassembly + * driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the + AP is using non-zero key index for the unicast key and key index zero + for the broadcast key + * driver_hostap: fixed IEEE 802.1X WEP key updates and + re-authentication by allowing unencrypted EAPOL frames when not using + WPA + * added a new driver interface, 'wext', which uses only standard, + driver independent functionality in Linux wireless extensions; + currently, this can be used only for non-WPA IEEE 802.1X mode, but + eventually, this is to be extended to support full WPA/WPA2 once + Linux wireless extensions get support for this + * added support for mode in which the driver is responsible for AP + scanning and selection; this is disabled by default and can be + enabled with global ap_scan=0 variable in wpa_supplicant.conf; + this mode can be used, e.g., with generic 'wext' driver interface to + use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver + supporting wireless extensions. + * driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g., + operation with an AP that does not include SSID in the Beacon frames) + * added support for new EAP authentication methods: + EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP + * added support for asking one-time-passwords from frontends (e.g., + wpa_cli); this 'otp' command works otherwise like 'password' command, + but the password is used only once and the frontend will be asked for + a new password whenever a request from authenticator requires a + password; this can be used with both EAP-OTP and EAP-GTC + * changed wpa_cli to automatically re-establish connection so that it + does not need to be re-started when wpa_supplicant is terminated and + started again + * improved user data (identity/password/otp) requests through + frontends: process pending EAPOL packets after getting new + information so that full authentication does not need to be + restarted; in addition, send pending requests again whenever a new + frontend is attached + * changed control frontends to use a new directory for socket files to + make it easier for wpa_cli to automatically select between interfaces + and to provide access control for the control interface; + wpa_supplicant.conf: ctrl_interface is now a path + (/var/run/wpa_supplicant is the recommended path) and + ctrl_interface_group can be used to select which group gets access to + the control interface; + wpa_cli: by default, try to connect to the first interface available + in /var/run/wpa_supplicant; this path can be overriden with -p option + and an interface can be selected with -i option (i.e., in most common + cases, wpa_cli does not need to get any arguments) + * added support for LEAP + * added driver interface for Linux ndiswrapper + * added priority option for network blocks in the configuration file; + this allows networks to be grouped based on priority (the scan + results are searched for matches with network blocks in this order) + +2004-06-20 - v0.2.3 + * sort scan results to improve AP selection + * fixed control interface socket removal for some error cases + * improved scan requesting and authentication timeout + * small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and + TLS processing + * PEAP version can now be forced with phase1="peapver=<ver>" + (mostly for testing; by default, the highest version supported by + both the Supplicant and Authentication Server is selected + automatically) + * added support for madwifi driver (Atheros ar521x) + * added a workaround for cases where AP sets Install Tx/Rx bit for + WPA Group Key messages when pairwise keys are used (without this, + the Group Key would be used for Tx and the AP would drop frames + from the station) + * added GSM SIM/USIM interface for GSM authentication algorithm for + EAP-SIM; this requires pcsc-lite + * added support for ATMEL AT76C5XXx driver + * fixed IEEE 802.1X WEP key derivation in the case where Authenticator + does not include key data in the EAPOL-Key frame (i.e., part of + EAP keying material is used as data encryption key) + * added support for using plaintext and static WEP networks + (key_mgmt=NONE) + +2004-05-31 - v0.2.2 + * added support for new EAP authentication methods: + EAP-TTLS/EAP-MD5-Challenge + EAP-TTLS/EAP-GTC + EAP-TTLS/EAP-MSCHAPv2 + EAP-TTLS/EAP-TLS + EAP-TTLS/MSCHAPv2 + EAP-TTLS/MSCHAP + EAP-TTLS/PAP + EAP-TTLS/CHAP + EAP-PEAP/TLS + EAP-PEAP/GTC + EAP-PEAP/MD5-Challenge + EAP-GTC + EAP-SIM (not yet complete; needs GSM/SIM authentication interface) + * added support for anonymous identity (to be used when identity is + sent in plaintext; real identity will be used within TLS protected + tunnel (e.g., with EAP-TTLS) + * added event messages from wpa_supplicant to frontends, e.g., wpa_cli + * added support for requesting identity and password information using + control interface; in other words, the password for EAP-PEAP or + EAP-TTLS does not need to be included in the configuration file since + a frontand (e.g., wpa_cli) can ask it from the user + * improved RSN pre-authentication to use a candidate list and process + all candidates from each scan; not only one per scan + * fixed RSN IE and WPA IE capabilities field parsing + * ignore Tx bit in GTK IE when Pairwise keys are used + * avoid making new scan requests during IEEE 802.1X negotiation + * use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant + with TLS support (this replaces the included implementation with + library code to save about 8 kB since the library code is needed + anyway for TLS) + * fixed WPA-PSK only mode when compiled without IEEE 802.1X support + (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config) + +2004-05-06 - v0.2.1 + * added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1) + Supplicant + - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1] + - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf] + - EAP-MD5 (cannot be used with WPA-RADIUS) + [draft-ietf-eap-rfc2284bis-09.txt] + - EAP-TLS [RFC 2716] + - EAP-MSCHAPv2 (currently used only with EAP-PEAP) + - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt] + [draft-kamath-pppext-eap-mschapv2-00.txt] + (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by + default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey) + - new configuration file options: eap, identity, password, ca_cert, + client_cert, privatekey, private_key_passwd + - Xsupplicant is not required anymore, but it can be used by + disabling the internal IEEE 802.1X Supplicant with -e command line + option + - this code is not included in the default build; Makefile need to + be edited for this (uncomment lines for selected functionality) + - EAP-TLS and EAP-PEAP require openssl libraries + * use module prefix in debug messages (WPA, EAP, EAP-TLS, ..) + * added support for non-WPA IEEE 802.1X mode with dynamic WEP keys + (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X + EAPOL-Key frames instead of WPA key handshakes) + * added support for IEEE 802.11i/RSN (WPA2) + - improved PTK Key Handshake + - PMKSA caching, pre-authentication + * fixed wpa_supplicant to ignore possible extra data after WPA + EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using + TPTK' error from message 3 of 4-Way Handshake in case the AP + includes extra data after the EAPOL-Key) + * added interface for external programs (frontends) to control + wpa_supplicant + - CLI example (wpa_cli) with interactive mode and command line + mode + - replaced SIGUSR1 status/statistics with the new control interface + * made some feature compile time configurable + - .config file for make + - driver interfaces (hostap, hermes, ..) + - EAPOL/EAP functions + +2004-02-15 - v0.2.0 + * Initial version of wpa_supplicant diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile new file mode 100644 index 0000000..aeb9ab9 --- /dev/null +++ b/wpa_supplicant/Makefile @@ -0,0 +1,1463 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +export LIBDIR ?= /usr/local/lib/ +export BINDIR ?= /usr/local/sbin/ + +CFLAGS += -I../src +CFLAGS += -I../src/utils + +ALL=wpa_supplicant wpa_passphrase wpa_cli + +all: verify_config $(ALL) dynamic_eap_methods + +verify_config: + @if [ ! -r .config ]; then \ + echo 'Building wpa_supplicant requires a configuration file'; \ + echo '(.config). See README for more instructions. You can'; \ + echo 'run "cp defconfig .config" to create an example'; \ + echo 'configuration.'; \ + exit 1; \ + fi + +mkconfig: + @if [ -f .config ]; then \ + echo '.config exists - did not replace it'; \ + exit 1; \ + fi + echo CONFIG_DRIVER_HOSTAP=y >> .config + echo CONFIG_DRIVER_WEXT=y >> .config + +install: all + mkdir -p $(DESTDIR)$(BINDIR) + for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done + $(MAKE) -C ../src install + +OBJS = config.o +OBJS += notify.o +OBJS += bss.o +OBJS += eap_register.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o +OBJS_p = wpa_passphrase.o +OBJS_p += ../src/utils/common.o +OBJS_p += ../src/utils/wpa_debug.o +OBJS_p += ../src/utils/wpabuf.o +OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o +OBJS_c += ../src/utils/wpa_debug.o + +-include .config + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += ../src/utils/os_$(CONFIG_OS).o +OBJS_p += ../src/utils/os_$(CONFIG_OS).o +OBJS_c += ../src/utils/os_$(CONFIG_OS).o + +ifdef CONFIG_WPA_TRACE +CFLAGS += -DWPA_TRACE +OBJS += ../src/utils/trace.o +OBJS_p += ../src/utils/trace.o +OBJS_c += ../src/utils/trace.o +LDFLAGS += -rdynamic +CFLAGS += -funwind-tables +ifdef CONFIG_WPA_TRACE_BFD +CFLAGS += -DWPA_TRACE_BFD +LIBS += -lbfd +LIBS_p += -lbfd +LIBS_c += -lbfd +endif +endif + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += ../src/utils/$(CONFIG_ELOOP).o +OBJS_c += ../src/utils/$(CONFIG_ELOOP).o + + +ifdef CONFIG_EAPOL_TEST +CFLAGS += -Werror -DEAPOL_TEST +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.o +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.o +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.o +endif + +ifdef CONFIG_NO_CONFIG_WRITE +CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_IEEE80211W +CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R +OBJS += ../src/rsn_supp/wpa_ft.o +NEED_80211_COMMON=y +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_TDLS +CFLAGS += -DCONFIG_TDLS +OBJS += ../src/rsn_supp/tdls.o +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_PEERKEY +CFLAGS += -DCONFIG_PEERKEY +endif + +ifndef CONFIG_NO_WPA +OBJS += ../src/rsn_supp/wpa.o +OBJS += ../src/rsn_supp/preauth.o +OBJS += ../src/rsn_supp/pmksa_cache.o +OBJS += ../src/rsn_supp/peerkey.o +OBJS += ../src/rsn_supp/wpa_ie.o +OBJS += ../src/common/wpa_common.o +NEED_AES=y +NEED_SHA1=y +NEED_MD5=y +NEED_RC4=y +else +CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +endif + +ifdef CONFIG_IBSS_RSN +NEED_RSN_AUTHENTICATOR=y +CFLAGS += -DCONFIG_IBSS_RSN +OBJS += ibss_rsn.o +endif + +ifdef CONFIG_P2P +OBJS += p2p_supplicant.o +OBJS += ../src/p2p/p2p.o +OBJS += ../src/p2p/p2p_utils.o +OBJS += ../src/p2p/p2p_parse.o +OBJS += ../src/p2p/p2p_build.o +OBJS += ../src/p2p/p2p_go_neg.o +OBJS += ../src/p2p/p2p_sd.o +OBJS += ../src/p2p/p2p_pd.o +OBJS += ../src/p2p/p2p_invitation.o +OBJS += ../src/p2p/p2p_dev_disc.o +OBJS += ../src/p2p/p2p_group.o +OBJS += ../src/ap/p2p_hostapd.o +CFLAGS += -DCONFIG_P2P +NEED_80211_COMMON=y +ifdef CONFIG_P2P_STRICT +CFLAGS += -DCONFIG_P2P_STRICT +endif +endif + +ifdef CONFIG_NO_WPA2 +CFLAGS += -DCONFIG_NO_WPA2 +endif + +include ../src/drivers/drivers.mak +ifdef CONFIG_AP +OBJS_d += $(DRV_BOTH_OBJS) +CFLAGS += $(DRV_BOTH_CFLAGS) +LDFLAGS += $(DRV_BOTH_LDFLAGS) +LIBS += $(DRV_BOTH_LIBS) +else +NEED_AP_MLME= +OBJS_d += $(DRV_WPA_OBJS) +CFLAGS += $(DRV_WPA_CFLAGS) +LDFLAGS += $(DRV_WPA_LDFLAGS) +LIBS += $(DRV_WPA_LIBS) +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += ../src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).o + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += ../src/eap_peer/eap_tls.so +else +CFLAGS += -DEAP_TLS +OBJS += ../src/eap_peer/eap_tls.o +OBJS_h += ../src/eap_server/eap_server_tls.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_peap.so +else +CFLAGS += -DEAP_PEAP +OBJS += ../src/eap_peer/eap_peap.o +OBJS += ../src/eap_common/eap_peap_common.o +OBJS_h += ../src/eap_server/eap_server_peap.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += ../src/eap_peer/eap_ttls.so +else +CFLAGS += -DEAP_TTLS +OBJS += ../src/eap_peer/eap_ttls.o +OBJS_h += ../src/eap_server/eap_server_ttls.o +endif +MS_FUNCS=y +TLS_FUNCS=y +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += ../src/eap_peer/eap_md5.so +else +CFLAGS += -DEAP_MD5 +OBJS += ../src/eap_peer/eap_md5.o +OBJS_h += ../src/eap_server/eap_server_md5.o +endif +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +ifndef CONFIG_EAP_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 +ifeq ($(CONFIG_EAP_MSCHAPV2), dyn) +CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += ../src/eap_peer/eap_mschapv2.so +EAPDYN += ../src/eap_peer/mschapv2.so +else +CFLAGS += -DEAP_MSCHAPv2 +OBJS += ../src/eap_peer/eap_mschapv2.o +OBJS += ../src/eap_peer/mschapv2.o +OBJS_h += ../src/eap_server/eap_server_mschapv2.o +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += ../src/eap_peer/eap_gtc.so +else +CFLAGS += -DEAP_GTC +OBJS += ../src/eap_peer/eap_gtc.o +OBJS_h += ../src/eap_server/eap_server_gtc.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_otp.so +else +CFLAGS += -DEAP_OTP +OBJS += ../src/eap_peer/eap_otp.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += ../src/eap_peer/eap_sim.so +else +CFLAGS += -DEAP_SIM +OBJS += ../src/eap_peer/eap_sim.o +OBJS_h += ../src/eap_server/eap_server_sim.o +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_leap.so +else +CFLAGS += -DEAP_LEAP +OBJS += ../src/eap_peer/eap_leap.o +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +CFLAGS += -DEAP_PSK_DYNAMIC +EAPDYN += ../src/eap_peer/eap_psk.so +else +CFLAGS += -DEAP_PSK +OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o +OBJS_h += ../src/eap_server/eap_server_psk.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +NEED_AES_OMAC1=y +NEED_AES_ENCBLOCK=y +NEED_AES_EAX=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += ../src/eap_peer/eap_aka.so +else +CFLAGS += -DEAP_AKA +OBJS += ../src/eap_peer/eap_aka.o +OBJS_h += ../src/eap_server/eap_server_aka.o +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += ../src/eap_common/eap_sim_common.o +OBJS_h += ../src/eap_server/eap_sim_db.o +NEED_AES=y +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += ../src/eap_peer/eap_fast.so +EAPDYN += ../src/eap_common/eap_fast_common.o +else +CFLAGS += -DEAP_FAST +OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o +OBJS += ../src/eap_common/eap_fast_common.o +OBJS_h += ../src/eap_server/eap_server_fast.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +CFLAGS += -DEAP_PAX_DYNAMIC +EAPDYN += ../src/eap_peer/eap_pax.so +else +CFLAGS += -DEAP_PAX +OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o +OBJS_h += ../src/eap_server/eap_server_pax.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +CFLAGS += -DEAP_SAKE_DYNAMIC +EAPDYN += ../src/eap_peer/eap_sake.so +else +CFLAGS += -DEAP_SAKE +OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o +OBJS_h += ../src/eap_server/eap_server_sake.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +CFLAGS += -DEAP_GPSK_DYNAMIC +EAPDYN += ../src/eap_peer/eap_gpsk.so +else +CFLAGS += -DEAP_GPSK +OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o +OBJS_h += ../src/eap_server/eap_server_gpsk.o +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_EAP_PWD +CFLAGS += -DEAP_PWD +OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o +OBJS_h += ../src/eap_server/eap_pwd.o +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + +ifdef CONFIG_WPS +ifdef CONFIG_WPS2 +CFLAGS += -DCONFIG_WPS2 +endif + +# EAP-WSC +CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.o +OBJS += ../src/utils/uuid.o +OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o +OBJS += ../src/wps/wps.o +OBJS += ../src/wps/wps_common.o +OBJS += ../src/wps/wps_attr_parse.o +OBJS += ../src/wps/wps_attr_build.o +OBJS += ../src/wps/wps_attr_process.o +OBJS += ../src/wps/wps_dev_attr.o +OBJS += ../src/wps/wps_enrollee.o +OBJS += ../src/wps/wps_registrar.o +OBJS_h += ../src/eap_server/eap_server_wsc.o +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y +NEED_80211_COMMON=y +NEED_AES_CBC=y +NEED_MODEXP=y + +ifdef CONFIG_WPS_UFD +CFLAGS += -DCONFIG_WPS_UFD +OBJS += ../src/wps/wps_ufd.o +NEED_WPS_OOB=y +endif + +ifdef CONFIG_WPS_NFC +CFLAGS += -DCONFIG_WPS_NFC +OBJS += ../src/wps/ndef.o +OBJS += ../src/wps/wps_nfc.o +NEED_WPS_OOB=y +ifdef CONFIG_WPS_NFC_PN531 +PN531_PATH ?= /usr/local/src/nfc +CFLAGS += -DCONFIG_WPS_NFC_PN531 +CFLAGS += -I${PN531_PATH}/inc +OBJS += ../src/wps/wps_nfc_pn531.o +LIBS += ${PN531_PATH}/lib/wpsnfc.dll +LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll +endif +endif + +ifdef NEED_WPS_OOB +CFLAGS += -DCONFIG_WPS_OOB +endif + +ifdef CONFIG_WPS_ER +CONFIG_WPS_UPNP=y +CFLAGS += -DCONFIG_WPS_ER +OBJS += ../src/wps/wps_er.o +OBJS += ../src/wps/wps_er_ssdp.o +endif + +ifdef CONFIG_WPS_UPNP +CFLAGS += -DCONFIG_WPS_UPNP +OBJS += ../src/wps/wps_upnp.o +OBJS += ../src/wps/wps_upnp_ssdp.o +OBJS += ../src/wps/wps_upnp_web.o +OBJS += ../src/wps/wps_upnp_event.o +OBJS += ../src/wps/wps_upnp_ap.o +OBJS += ../src/wps/upnp_xml.o +OBJS += ../src/wps/httpread.o +OBJS += ../src/wps/http_client.o +OBJS += ../src/wps/http_server.o +endif + +ifdef CONFIG_WPS_STRICT +CFLAGS += -DCONFIG_WPS_STRICT +OBJS += ../src/wps/wps_validate.o +endif + +ifdef CONFIG_WPS_TESTING +CFLAGS += -DCONFIG_WPS_TESTING +endif + +ifdef CONFIG_WPS_REG_DISABLE_OPEN +CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += ../src/eap_peer/eap_ikev2.so ../src/eap_peer/ikev2.o +EAPDYN += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +else +CFLAGS += -DEAP_IKEV2 +OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o +OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +OBJS_h += ../src/eap_server/eap_server_ikev2.o +OBJS_h += ../src/eap_server/ikev2.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_MODEXP=y +NEED_CIPHER=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +EAPDYN += ../src/eap_peer/eap_vendor_test.so +else +CFLAGS += -DEAP_VENDOR_TEST +OBJS += ../src/eap_peer/eap_vendor_test.o +OBJS_h += ../src/eap_server/eap_server_vendor_test.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +CFLAGS += -DEAP_TNC +OBJS += ../src/eap_peer/eap_tnc.o +OBJS += ../src/eap_peer/tncc.o +OBJS_h += ../src/eap_server/eap_server_tnc.o +OBJS_h += ../src/eap_server/tncs.o +NEED_BASE64=y +ifndef CONFIG_NATIVE_WINDOWS +ifndef CONFIG_DRIVER_BSD +LIBS += -ldl +endif +endif +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +CFLAGS += -DIEEE8021X_EAPOL +OBJS += ../src/eapol_supp/eapol_supp_sm.o +OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o +NEED_EAP_COMMON=y +ifdef CONFIG_DYNAMIC_EAP_METHODS +CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_AP +NEED_80211_COMMON=y +NEED_EAP_COMMON=y +NEED_RSN_AUTHENTICATOR=y +CFLAGS += -DCONFIG_AP +OBJS += ap.o +CFLAGS += -DCONFIG_NO_RADIUS +CFLAGS += -DCONFIG_NO_ACCOUNTING +CFLAGS += -DCONFIG_NO_VLAN +OBJS += ../src/ap/hostapd.o +OBJS += ../src/ap/wpa_auth_glue.o +OBJS += ../src/ap/utils.o +OBJS += ../src/ap/authsrv.o +OBJS += ../src/ap/ap_config.o +OBJS += ../src/utils/ip_addr.o +OBJS += ../src/ap/sta_info.o +OBJS += ../src/ap/tkip_countermeasures.o +OBJS += ../src/ap/ap_mlme.o +OBJS += ../src/ap/ieee802_1x.o +OBJS += ../src/eapol_auth/eapol_auth_sm.o +OBJS += ../src/ap/ieee802_11_auth.o +OBJS += ../src/ap/drv_callbacks.o +OBJS += ../src/ap/ap_drv_ops.o +ifdef CONFIG_IEEE80211N +OBJS += ../src/ap/ieee802_11_ht.o +endif +ifdef CONFIG_CTRL_IFACE +OBJS += ../src/ap/ctrl_iface_ap.o +endif + +CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY +OBJS += ../src/eap_server/eap_server.o +OBJS += ../src/eap_server/eap_server_identity.o +OBJS += ../src/eap_server/eap_server_methods.o + +ifdef CONFIG_IEEE80211N +CFLAGS += -DCONFIG_IEEE80211N +endif + +ifdef NEED_AP_MLME +OBJS += ../src/ap/beacon.o +OBJS += ../src/ap/wmm.o +OBJS += ../src/ap/ap_list.o +OBJS += ../src/ap/ieee802_11.o +OBJS += ../src/ap/hw_features.o +CFLAGS += -DNEED_AP_MLME +endif +ifdef CONFIG_WPS +CFLAGS += -DEAP_SERVER_WSC +OBJS += ../src/ap/wps_hostapd.o +OBJS += ../src/eap_server/eap_server_wsc.o +endif +endif + +ifdef NEED_RSN_AUTHENTICATOR +CFLAGS += -DCONFIG_NO_RADIUS +NEED_AES_WRAP=y +OBJS += ../src/ap/wpa_auth.o +OBJS += ../src/ap/wpa_auth_ie.o +OBJS += ../src/ap/pmksa_cache_auth.o +ifdef CONFIG_IEEE80211R +OBJS += ../src/ap/wpa_auth_ft.o +endif +ifdef CONFIG_PEERKEY +OBJS += ../src/ap/peerkey_auth.o +endif +endif + +ifdef CONFIG_EAP_SERVER +CFLAGS += -DEAP_SERVER +OBJS_h += ../src/eap_server/eap_server.o +OBJS_h += ../src/eap_server/eap_server_identity.o +OBJS_h += ../src/eap_server/eap_server_methods.o +endif + +ifdef CONFIG_RADIUS_CLIENT +OBJS_h += ../src/utils/ip_addr.o +OBJS_h += ../src/radius/radius.o +OBJS_h += ../src/radius/radius_client.o +endif + +ifdef CONFIG_AUTHENTICATOR +OBJS_h += ../src/eapol_auth/eapol_auth_sm.o +OBJS_h += ../src/ap/ieee802_1x.o +endif + +ifdef CONFIG_WPA_AUTHENTICATOR +OBJS_h += ../src/ap/wpa_auth.o +OBJS_h += ../src/ap/wpa_auth_ie.o +OBJS_h += ../src/ap/pmksa_cache_auth.o +ifdef CONFIG_IEEE80211R +OBJS_h += ../src/ap/wpa_auth_ft.o +endif +ifdef CONFIG_PEERKEY +OBJS_h += ../src/ap/peerkey_auth.o +endif +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += ../src/utils/pcsc_funcs.o +# -lpthread may not be needed depending on how pcsc-lite was configured +ifdef CONFIG_NATIVE_WINDOWS +#Once MinGW gets support for WinScard, -lwinscard could be used instead of the +#dynamic symbol loading that is now used in pcsc_funcs.c +#LIBS += -lwinscard +else +LIBS += -lpcsclite -lpthread +endif +endif + +ifdef CONFIG_SIM_SIMULATOR +CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += ../src/crypto/milenage.o +endif + +ifdef CONFIG_PKCS12 +CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += ../src/crypto/ms_funcs.o +NEED_DES=y +NEED_MD4=y +endif + +ifdef CHAP +OBJS += ../src/eap_common/chap.o +endif + +ifdef TLS_FUNCS +NEED_DES=y +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +OBJS += ../src/eap_peer/eap_tls_common.o +OBJS_h += ../src/eap_server/eap_server_tls_common.o +NEED_TLS_PRF=y +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifeq ($(CONFIG_TLS), openssl) +ifdef TLS_FUNCS +CFLAGS += -DEAP_TLS_OPENSSL +OBJS += ../src/crypto/tls_openssl.o +LIBS += -lssl +endif +OBJS += ../src/crypto/crypto_openssl.o +OBJS_p += ../src/crypto/crypto_openssl.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_openssl.o +endif +LIBS += -lcrypto +LIBS_p += -lcrypto +endif + +ifeq ($(CONFIG_TLS), gnutls) +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_gnutls.o +LIBS += -lgnutls -lgpg-error +ifdef CONFIG_GNUTLS_EXTRA +CFLAGS += -DCONFIG_GNUTLS_EXTRA +LIBS += -lgnutls-extra +endif +endif +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 +endif +LIBS += -lgcrypt +LIBS_p += -lgcrypt +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), schannel) +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_schannel.o +endif +OBJS += ../src/crypto/crypto_cryptoapi.o +OBJS_p += ../src/crypto/crypto_cryptoapi.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_cryptoapi.o +endif +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), nss) +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_nss.o +LIBS += -lssl3 +endif +OBJS += ../src/crypto/crypto_nss.o +OBJS_p += ../src/crypto/crypto_nss.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_nss.o +endif +LIBS += -lnss3 +LIBS_p += -lnss3 +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +ifdef TLS_FUNCS +OBJS += ../src/crypto/crypto_internal-rsa.o +OBJS += ../src/crypto/tls_internal.o +OBJS += ../src/tls/tlsv1_common.o +OBJS += ../src/tls/tlsv1_record.o +OBJS += ../src/tls/tlsv1_cred.o +OBJS += ../src/tls/tlsv1_client.o +OBJS += ../src/tls/tlsv1_client_write.o +OBJS += ../src/tls/tlsv1_client_read.o +OBJS += ../src/tls/asn1.o +OBJS += ../src/tls/rsa.o +OBJS += ../src/tls/x509v3.o +OBJS += ../src/tls/pkcs1.o +OBJS += ../src/tls/pkcs5.o +OBJS += ../src/tls/pkcs8.o +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +NEED_MODEXP=y +NEED_CIPHER=y +CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +endif +ifdef NEED_CIPHER +NEED_DES=y +OBJS += ../src/crypto/crypto_internal-cipher.o +endif +ifdef NEED_MODEXP +OBJS += ../src/crypto/crypto_internal-modexp.o +OBJS += ../src/tls/bignum.o +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += ../src/crypto/crypto_libtomcrypt.o +OBJS_p += ../src/crypto/crypto_libtomcrypt.o +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += ../src/crypto/crypto_internal.o +OBJS_p += ../src/crypto/crypto_internal.o +NEED_AES_ENC=y +CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +CFLAGS += -DLTM_FAST +endif +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += ../src/crypto/crypto_cryptoapi.o +OBJS_p += ../src/crypto/crypto_cryptoapi.o +CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif +endif + +ifeq ($(CONFIG_TLS), none) +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_none.o +CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif +OBJS += ../src/crypto/crypto_none.o +OBJS_p += ../src/crypto/crypto_none.o +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif + +ifdef TLS_FUNCS +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +endif + +ifndef TLS_FUNCS +OBJS += ../src/crypto/tls_none.o +ifeq ($(CONFIG_TLS), internal) +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_RC4=y +endif +endif + +AESOBJS = # none so far (see below) +ifdef CONFIG_INTERNAL_AES +AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o +endif + +AESOBJS += ../src/crypto/aes-unwrap.o +ifdef NEED_AES_EAX +AESOBJS += ../src/crypto/aes-eax.o +NEED_AES_CTR=y +endif +ifdef NEED_AES_CTR +AESOBJS += ../src/crypto/aes-ctr.o +endif +ifdef NEED_AES_ENCBLOCK +AESOBJS += ../src/crypto/aes-encblock.o +endif +ifdef NEED_AES_OMAC1 +NEED_AES_ENC=y +AESOBJS += ../src/crypto/aes-omac1.o +endif +ifdef NEED_AES_WRAP +NEED_AES_ENC=y +AESOBJS += ../src/crypto/aes-wrap.o +endif +ifdef NEED_AES_CBC +NEED_AES_ENC=y +AESOBJS += ../src/crypto/aes-cbc.o +endif +ifdef NEED_AES_ENC +ifdef CONFIG_INTERNAL_AES +AESOBJS += ../src/crypto/aes-internal-enc.o +endif +endif +ifdef NEED_AES +OBJS += $(AESOBJS) +endif + +ifdef NEED_SHA1 +SHA1OBJS += ../src/crypto/sha1.o +ifdef CONFIG_INTERNAL_SHA1 +SHA1OBJS += ../src/crypto/sha1-internal.o +ifdef NEED_FIPS186_2_PRF +SHA1OBJS += ../src/crypto/fips_prf_internal.o +endif +endif +ifndef CONFIG_NO_WPA_PASSPHRASE +SHA1OBJS += ../src/crypto/sha1-pbkdf2.o +endif +ifdef NEED_T_PRF +SHA1OBJS += ../src/crypto/sha1-tprf.o +endif +ifdef NEED_TLS_PRF +SHA1OBJS += ../src/crypto/sha1-tlsprf.o +endif +endif + +MD5OBJS = ../src/crypto/md5.o +ifdef NEED_MD5 +ifdef CONFIG_INTERNAL_MD5 +MD5OBJS += ../src/crypto/md5-internal.o +endif +ifdef CONFIG_FIPS +MD5OBJS += ../src/crypto/md5-non-fips.o +endif +OBJS += $(MD5OBJS) +OBJS_p += $(MD5OBJS) +endif + +ifdef NEED_MD4 +ifdef CONFIG_INTERNAL_MD4 +OBJS += ../src/crypto/md4-internal.o +endif +endif + +DESOBJS = # none needed when not internal +ifdef NEED_DES +ifdef CONFIG_INTERNAL_DES +DESOBJS += ../src/crypto/des-internal.o +endif +endif + +ifdef NEED_RC4 +ifdef CONFIG_INTERNAL_RC4 +OBJS += ../src/crypto/rc4.o +endif +endif + +SHA256OBJS = # none by default +ifdef NEED_SHA256 +CFLAGS += -DCONFIG_SHA256 +SHA256OBJS += ../src/crypto/sha256.o +ifdef CONFIG_INTERNAL_SHA256 +SHA256OBJS += ../src/crypto/sha256-internal.o +endif +OBJS += $(SHA256OBJS) +endif + +ifdef NEED_DH_GROUPS +OBJS += ../src/crypto/dh_groups.o +endif +ifdef NEED_DH_GROUPS_ALL +CFLAGS += -DALL_DH_GROUPS +endif +ifdef CONFIG_INTERNAL_DH_GROUP5 +ifdef NEED_DH_GROUPS +OBJS += ../src/crypto/dh_group5.o +endif +endif + +ifdef CONFIG_NO_RANDOM_POOL +CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +OBJS += ../src/crypto/random.o +endif + +ifdef CONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), y) +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_CTRL_IFACE=named_pipe +else +CONFIG_CTRL_IFACE=unix +endif +endif +CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o +endif + +ifdef CONFIG_CTRL_IFACE_DBUS +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE +DBUS_OBJS += dbus/dbus_old.o dbus/dbus_old_handlers.o +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_old_handlers_wps.o +endif +DBUS_OBJS += dbus/dbus_dict_helpers.o +ifndef DBUS_LIBS +DBUS_LIBS := $(shell pkg-config --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +endif +dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1)) +DBUS_VERSION_MAJOR=$(word 1,$(dbus_version)) +DBUS_VERSION_MINOR=$(word 2,$(dbus_version)) +ifeq ($(DBUS_VERSION_MAJOR),) +DBUS_VERSION_MAJOR=0 +endif +ifeq ($(DBUS_VERSION_MINOR),) +DBUS_VERSION_MINOR=0 +endif +DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR) +DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR) +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef CONFIG_CTRL_IFACE_DBUS_NEW +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +DBUS_OBJS ?= dbus/dbus_dict_helpers.o +DBUS_OBJS += dbus/dbus_new_helpers.o +DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_new_handlers_wps.o +endif +ifndef DBUS_LIBS +DBUS_LIBS := $(shell pkg-config --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +endif +ifdef CONFIG_CTRL_IFACE_DBUS_INTRO +DBUS_OBJS += dbus/dbus_new_introspect.o +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef DBUS +DBUS_CFLAGS += -DCONFIG_DBUS +DBUS_OBJS += dbus/dbus_common.o +endif + +OBJS += $(DBUS_OBJS) +CFLAGS += $(DBUS_CFLAGS) +LIBS += $(DBUS_LIBS) + +ifdef CONFIG_READLINE +OBJS_c += ../src/utils/edit_readline.o +LIBS_c += -lncurses -lreadline +else +ifdef CONFIG_WPA_CLI_EDIT +OBJS_c += ../src/utils/edit.o +else +OBJS_c += ../src/utils/edit_simple.o +endif +endif + +ifdef CONFIG_NATIVE_WINDOWS +CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 -lgdi32 -lcrypt32 +LIBS_c += -lws2_32 +LIBS_p += -lws2_32 -lgdi32 +ifeq ($(CONFIG_CRYPTO), cryptoapi) +LIBS_p += -lcrypt32 +endif +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +CFLAGS += -DCONFIG_IPV6 +endif + +ifdef NEED_BASE64 +OBJS += ../src/utils/base64.o +endif + +ifdef NEED_SME +NEED_80211_COMMON=y +OBJS += sme.o +CFLAGS += -DCONFIG_SME +endif + +ifdef CONFIG_CLIENT_MLME +OBJS += mlme.o +CFLAGS += -DCONFIG_CLIENT_MLME +NEED_80211_COMMON=y +endif + +ifdef NEED_80211_COMMON +OBJS += ../src/common/ieee802_11_common.o +endif + +ifdef NEED_EAP_COMMON +OBJS += ../src/eap_common/eap_common.o +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_SYSLOG +CFLAGS += -DCONFIG_DEBUG_SYSLOG +ifdef CONFIG_DEBUG_SYSLOG_FACILITY +CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)" +endif +endif + +ifdef CONFIG_DEBUG_FILE +CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +ifdef CONFIG_FIPS +CFLAGS += -DCONFIG_FIPS +endif + +OBJS += $(SHA1OBJS) $(DESOBJS) + +OBJS_p += $(SHA1OBJS) + +ifdef CONFIG_BGSCAN_SIMPLE +CFLAGS += -DCONFIG_BGSCAN_SIMPLE +OBJS += bgscan_simple.o +NEED_BGSCAN=y +endif + +ifdef CONFIG_BGSCAN_LEARN +CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.o +NEED_BGSCAN=y +endif + +ifdef NEED_BGSCAN +CFLAGS += -DCONFIG_BGSCAN +OBJS += bgscan.o +endif + +OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o +OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o +ifdef CONFIG_AUTHENTICATOR +OBJS_wpa += tests/link_test.o +endif +OBJS_wpa += $(OBJS_l2) +OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o +OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o +OBJS_t += ../src/radius/radius_client.o +OBJS_t += ../src/radius/radius.o +ifndef CONFIG_AP +OBJS_t += ../src/utils/ip_addr.o +endif +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o +OBJS += $(CONFIG_MAIN).o + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) ../src/drivers/drivers.o +OBJS_priv += $(OBJS_l2) +OBJS_priv += ../src/utils/os_$(CONFIG_OS).o +OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o +OBJS_priv += ../src/utils/common.o +OBJS_priv += ../src/utils/wpa_debug.o +OBJS_priv += ../src/utils/wpabuf.o +OBJS_priv += wpa_priv.o +ifdef CONFIG_DRIVER_TEST +OBJS_priv += $(SHA1OBJS) +OBJS_priv += $(MD5OBJS) +ifeq ($(CONFIG_TLS), openssl) +OBJS_priv += ../src/crypto/crypto_openssl.o +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS_priv += ../src/crypto/crypto_gnutls.o +endif +ifeq ($(CONFIG_TLS), nss) +OBJS_priv += ../src/crypto/crypto_nss.o +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS_priv += ../src/crypto/crypto_libtomcrypt.o +else +OBJS_priv += ../src/crypto/crypto_internal.o +endif +endif +endif # CONFIG_DRIVER_TEST +OBJS += ../src/l2_packet/l2_packet_privsep.o +OBJS += ../src/drivers/driver_privsep.o +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) ../src/drivers/drivers.o +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += ../src/drivers/ndis_events.o +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +Q=@ +E=echo +ifeq ($(V), 1) +Q= +E=true +endif + +dynamic_eap_methods: $(EAPDYN) + +../src/drivers/build.wpa_supplicant: + @if [ -f ../src/drivers/build.hostapd ]; then \ + $(MAKE) -C ../src/drivers clean; \ + fi + @touch ../src/drivers/build.wpa_supplicant + +BCHECK=../src/drivers/build.wpa_supplicant + +wpa_priv: $(BCHECK) $(OBJS_priv) + $(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS) + @$(E) " LD " $@ + +wpa_supplicant: .config $(BCHECK) $(OBJS) $(EXTRA_progs) + $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) + @$(E) " LD " $@ + +eapol_test: .config $(OBJS_t) + $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) + @$(E) " LD " $@ + +preauth_test: .config $(OBJS_t2) + $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS) + @$(E) " LD " $@ + +wpa_passphrase: $(OBJS_p) + $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + @$(E) " LD " $@ + +wpa_cli: $(OBJS_c) + $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c) + @$(E) " LD " $@ + +link_test: $(OBJS) $(OBJS_h) tests/link_test.o + $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS) + @$(E) " LD " $@ + +test_wpa: $(OBJS_wpa) $(OBJS_h) + $(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS) + @$(E) " LD " $@ + +win_if_list: win_if_list.c + $(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) + @$(E) " LD " $@ + +eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_psk_register=eap_peer_method_dynamic_init + +eap_pax.so: ../src/eap_peer/eap_pax.c ../src/eap_common/eap_pax_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_pax_register=eap_peer_method_dynamic_init + +eap_sake.so: ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_sake_register=eap_peer_method_dynamic_init + +eap_wsc.so: ../src/eap_peer/eap_wsc.c ../src/eap_common/eap_wsc_common.c ../src/wps/wps.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_wsc_register=eap_peer_method_dynamic_init + +eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_ikev2_register=eap_peer_method_dynamic_init + +%.so: %.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \ + -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init + +%.o: %.c + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + @$(E) " CC " $< + +wpa_supplicant.exe: wpa_supplicant + mv -f $< $@ +wpa_cli.exe: wpa_cli + mv -f $< $@ +wpa_passphrase.exe: wpa_passphrase + mv -f $< $@ +win_if_list.exe: win_if_list + mv -f $< $@ +eapol_test.exe: eapol_test + mv -f $< $@ + +WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe + +windows-bin: $(WINALL) + $(STRIP) $(WINALL) + +wpa_gui/Makefile: + qmake -o wpa_gui/Makefile wpa_gui/wpa_gui.pro + +wpa_gui: wpa_gui/Makefile + $(MAKE) -C wpa_gui + +wpa_gui-qt4/Makefile: + qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro + +wpa_gui-qt4/lang/wpa_gui_de.qm: wpa_gui-qt4/lang/wpa_gui_de.ts + lrelease wpa_gui-qt4/wpa_gui.pro + +wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm + $(MAKE) -C wpa_gui-qt4 + +TEST_EAP_SIM_COMMON_OBJS = $(SHA1OBJS) $(MD5OBJS) \ + ../src/utils/common.o ../src/utils/os_unix.o \ + ../src/utils/wpa_debug.o $(AESOBJS) \ + tests/test_eap_sim_common.o +test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_EAP_SIM_COMMON_OBJS) $(LIBS) + ./test-eap_sim_common + rm test-eap_sim_common + +tests: test-eap_sim_common + +clean: + $(MAKE) -C ../src clean + $(MAKE) -C dbus clean + rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test + rm -f wpa_priv + +-include $(OBJS:%.o=%.d) diff --git a/wpa_supplicant/README b/wpa_supplicant/README new file mode 100644 index 0000000..506a907 --- /dev/null +++ b/wpa_supplicant/README @@ -0,0 +1,1004 @@ +WPA Supplicant +============== + +Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + + + +License +------- + +GPL v2: + +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. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +(this copy of the license is in COPYING file) + + +Alternatively, this software may be distributed, used, and modified +under the terms of BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Features +-------- + +Supported WPA/IEEE 802.11i features: +- WPA-PSK ("WPA-Personal") +- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") + Following authentication methods are supported with an integrate IEEE 802.1X + Supplicant: + * EAP-TLS + * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1) + * EAP-PEAP/TLS (both PEAPv0 and PEAPv1) + * EAP-PEAP/GTC (both PEAPv0 and PEAPv1) + * EAP-PEAP/OTP (both PEAPv0 and PEAPv1) + * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1) + * EAP-TTLS/EAP-MD5-Challenge + * EAP-TTLS/EAP-GTC + * EAP-TTLS/EAP-OTP + * EAP-TTLS/EAP-MSCHAPv2 + * EAP-TTLS/EAP-TLS + * EAP-TTLS/MSCHAPv2 + * EAP-TTLS/MSCHAP + * EAP-TTLS/PAP + * EAP-TTLS/CHAP + * EAP-SIM + * EAP-AKA + * EAP-PSK + * EAP-PAX + * EAP-SAKE + * EAP-IKEv2 + * EAP-GPSK + * LEAP (note: requires special support from the driver for IEEE 802.11 + authentication) + (following methods are supported, but since they do not generate keying + material, they cannot be used with WPA or IEEE 802.1X WEP keying) + * EAP-MD5-Challenge + * EAP-MSCHAPv2 + * EAP-GTC + * EAP-OTP +- key management for CCMP, TKIP, WEP104, WEP40 +- RSN/WPA2 (IEEE 802.11i) + * pre-authentication + * PMKSA caching + +Supported TLS/crypto libraries: +- OpenSSL (default) +- GnuTLS + +Internal TLS/crypto implementation (optional): +- can be used in place of an external TLS/crypto library +- TLSv1 +- X.509 certificate processing +- PKCS #1 +- ASN.1 +- RSA +- bignum +- minimal size (ca. 50 kB binary, parts of which are already needed for WPA; + TLSv1/X.509/ASN.1/RSA/bignum parts are about 25 kB on x86) + + +Requirements +------------ + +Current hardware/software requirements: +- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer +- FreeBSD 6-CURRENT +- NetBSD-current +- Microsoft Windows with WinPcap (at least WinXP, may work with other versions) +- drivers: + Linux drivers that support WPA/WPA2 configuration with the generic + Linux wireless extensions (WE-18 or newer). Even though there are + number of driver specific interface included in wpa_supplicant, please + note that Linux drivers are moving to use generic wireless extensions + and driver_wext (-Dwext on wpa_supplicant command line) should be the + default option to start with before falling back to driver specific + interface. + + Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x) + (http://hostap.epitest.fi/) + Driver need to be set in Managed mode ('iwconfig wlan0 mode managed'). + Please note that station firmware version needs to be 1.7.0 or newer + to work in WPA mode. + + Linuxant DriverLoader (http://www.linuxant.com/driverloader/) + with Windows NDIS driver for your wlan card supporting WPA. + + madwifi driver for cards based on Atheros chip set (ar521x) + (http://sourceforge.net/projects/madwifi/) + Please note that you will need to modify the wpa_supplicant .config + file to use the correct path for the madwifi driver root directory + (CFLAGS += -I../madwifi/wpa line in example defconfig). + + Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with + Windows NDIS driver. + + Broadcom wl.o driver (old version only) + This is a generic Linux driver for Broadcom IEEE 802.11a/g cards. + However, it is proprietary driver that is not publicly available + except for couple of exceptions, mainly Broadcom-based APs/wireless + routers that use Linux. The driver binary can be downloaded, e.g., + from Linksys support site (http://www.linksys.com/support/gpl.asp) + for Linksys WRT54G. The GPL tarball includes cross-compiler and + the needed header file, wlioctl.h, for compiling wpa_supplicant. + This driver support in wpa_supplicant is expected to work also with + other devices based on Broadcom driver (assuming the driver includes + client mode support). Please note that the newer Broadcom driver + ("hybrid Linux driver") supports Linux wireless extensions and does + not need (or even work) with the specific driver wrapper. Use -Dwext + with that driver. + + In theory, any driver that supports Linux wireless extensions can be + used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in + configuration file. + + Wired Ethernet drivers (with ap_scan=0) + + BSD net80211 layer (e.g., Atheros driver) + At the moment, this is for FreeBSD 6-CURRENT branch and NetBSD-current. + + Windows NDIS + The current Windows port requires WinPcap (http://winpcap.polito.it/). + See README-Windows.txt for more information. + +wpa_supplicant was designed to be portable for different drivers and +operating systems. Hopefully, support for more wlan cards and OSes will be +added in the future. See developer's documentation +(http://hostap.epitest.fi/wpa_supplicant/devel/) for more information about the +design of wpa_supplicant and porting to other drivers. One main goal +is to add full WPA/WPA2 support to Linux wireless extensions to allow +new drivers to be supported without having to implement new +driver-specific interface code in wpa_supplicant. + +Optional libraries for layer2 packet processing: +- libpcap (tested with 0.7.2, most relatively recent versions assumed to work, + this is likely to be available with most distributions, + http://tcpdump.org/) +- libdnet (tested with v1.4, most versions assumed to work, + http://libdnet.sourceforge.net/) + +These libraries are _not_ used in the default Linux build. Instead, +internal Linux specific implementation is used. libpcap/libdnet are +more portable and they can be used by adding CONFIG_L2_PACKET=pcap into +.config. They may also be selected automatically for other operating +systems. In case of Windows builds, WinPcap is used by default +(CONFIG_L2_PACKET=winpcap). + + +Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS: +- OpenSSL (tested with 0.9.7c and 0.9.7d, and 0.9.8 versions; assumed to + work with most relatively recent versions; this is likely to be + available with most distributions, http://www.openssl.org/) +- GnuTLS +- internal TLSv1 implementation + +TLS options for EAP-FAST: +- OpenSSL 0.9.8d _with_ openssl-0.9.8d-tls-extensions.patch applied + (i.e., the default OpenSSL package does not include support for + extensions needed for EAP-FAST) +- internal TLSv1 implementation + +One of these libraries is needed when EAP-TLS, EAP-PEAP, EAP-TTLS, or +EAP-FAST support is enabled. WPA-PSK mode does not require this or EAPOL/EAP +implementation. A configuration file, .config, for compilation is +needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5, +EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so +they should only be enabled if testing the EAPOL/EAP state +machines. However, there can be used as inner authentication +algorithms with EAP-PEAP and EAP-TTLS. + +See Building and installing section below for more detailed +information about the wpa_supplicant build time configuration. + + + +WPA +--- + +The original security mechanism of IEEE 802.11 standard was not +designed to be strong and has proven to be insufficient for most +networks that require some kind of security. Task group I (Security) +of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked +to address the flaws of the base standard and has in practice +completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +802.11 standard was approved in June 2004 and published in July 2004. + +Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the +IEEE 802.11i work (draft 3.0) to define a subset of the security +enhancements that can be implemented with existing wlan hardware. This +is called Wi-Fi Protected Access<TM> (WPA). This has now become a +mandatory component of interoperability testing and certification done +by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web +site (http://www.wi-fi.org/OpenSection/protected_access.asp). + +IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm +for protecting wireless networks. WEP uses RC4 with 40-bit keys, +24-bit initialization vector (IV), and CRC32 to protect against packet +forgery. All these choices have proven to be insufficient: key space is +too small against current attacks, RC4 key scheduling is insufficient +(beginning of the pseudorandom stream should be skipped), IV space is +too small and IV reuse makes attacks easier, there is no replay +protection, and non-keyed authentication does not protect against bit +flipping packet data. + +WPA is an intermediate solution for the security issues. It uses +Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a +compromise on strong security and possibility to use existing +hardware. It still uses RC4 for the encryption like WEP, but with +per-packet RC4 keys. In addition, it implements replay protection, +keyed packet authentication mechanism (Michael MIC). + +Keys can be managed using two different mechanisms. WPA can either use +an external authentication server (e.g., RADIUS) and EAP just like +IEEE 802.1X is using or pre-shared keys without need for additional +servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", +respectively. Both mechanisms will generate a master session key for +the Authenticator (AP) and Supplicant (client station). + +WPA implements a new key handshake (4-Way Handshake and Group Key +Handshake) for generating and exchanging data encryption keys between +the Authenticator and Supplicant. This handshake is also used to +verify that both Authenticator and Supplicant know the master session +key. These handshakes are identical regardless of the selected key +management mechanism (only the method for generating master session +key changes). + + + +IEEE 802.11i / WPA2 +------------------- + +The design for parts of IEEE 802.11i that were not included in WPA has +finished (May 2004) and this amendment to IEEE 802.11 was approved in +June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new +version of WPA called WPA2. This includes, e.g., support for more +robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) +to replace TKIP and optimizations for handoff (reduced number of +messages in initial key handshake, pre-authentication, and PMKSA caching). + + + +wpa_supplicant +-------------- + +wpa_supplicant is an implementation of the WPA Supplicant component, +i.e., the part that runs in the client stations. It implements WPA key +negotiation with a WPA Authenticator and EAP authentication with +Authentication Server. In addition, it controls the roaming and IEEE +802.11 authentication/association of the wlan driver. + +wpa_supplicant is designed to be a "daemon" program that runs in the +background and acts as the backend component controlling the wireless +connection. wpa_supplicant supports separate frontend programs and an +example text-based frontend, wpa_cli, is included with wpa_supplicant. + +Following steps are used when associating with an AP using WPA: + +- wpa_supplicant requests the kernel driver to scan neighboring BSSes +- wpa_supplicant selects a BSS based on its configuration +- wpa_supplicant requests the kernel driver to associate with the chosen + BSS +- If WPA-EAP: integrated IEEE 802.1X Supplicant completes EAP + authentication with the authentication server (proxied by the + Authenticator in the AP) +- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant +- If WPA-PSK: wpa_supplicant uses PSK as the master session key +- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake + with the Authenticator (AP) +- wpa_supplicant configures encryption keys for unicast and broadcast +- normal data packets can be transmitted and received + + + +Building and installing +----------------------- + +In order to be able to build wpa_supplicant, you will first need to +select which parts of it will be included. This is done by creating a +build time configuration file, .config, in the wpa_supplicant root +directory. Configuration options are text lines using following +format: CONFIG_<option>=y. Lines starting with # are considered +comments and are ignored. See defconfig file for an example configuration +and a list of available options and additional notes. + +The build time configuration can be used to select only the needed +features and limit the binary size and requirements for external +libraries. The main configuration parts are the selection of which +driver interfaces (e.g., hostap, madwifi, ..) and which authentication +methods (e.g., EAP-TLS, EAP-PEAP, ..) are included. + +Following build time configuration options are used to control IEEE +802.1X/EAPOL and EAP state machines and all EAP methods. Including +TLS, PEAP, or TTLS will require linking wpa_supplicant with OpenSSL +library for TLS implementation. Alternatively, GnuTLS or the internal +TLSv1 implementation can be used for TLS functionaly. + +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_MD5=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_EAP_TLS=y +CONFIG_EAP_PEAP=y +CONFIG_EAP_TTLS=y +CONFIG_EAP_GTC=y +CONFIG_EAP_OTP=y +CONFIG_EAP_SIM=y +CONFIG_EAP_AKA=y +CONFIG_EAP_PSK=y +CONFIG_EAP_SAKE=y +CONFIG_EAP_GPSK=y +CONFIG_EAP_PAX=y +CONFIG_EAP_LEAP=y +CONFIG_EAP_IKEV2=y + +Following option can be used to include GSM SIM/USIM interface for GSM/UMTS +authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite +(http://www.linuxnet.com/) for smart card access. + +CONFIG_PCSC=y + +Following options can be added to .config to select which driver +interfaces are included. + +CONFIG_DRIVER_HOSTAP=y +CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_RALINK=y +CONFIG_DRIVER_BROADCOM=y +CONFIG_DRIVER_BSD=y +CONFIG_DRIVER_NDIS=y + +Following example includes all features and driver interfaces that are +included in the wpa_supplicant package: + +CONFIG_DRIVER_HOSTAP=y +CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_BROADCOM=y +CONFIG_DRIVER_BSD=y +CONFIG_DRIVER_NDIS=y +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_MD5=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_EAP_TLS=y +CONFIG_EAP_PEAP=y +CONFIG_EAP_TTLS=y +CONFIG_EAP_GTC=y +CONFIG_EAP_OTP=y +CONFIG_EAP_SIM=y +CONFIG_EAP_AKA=y +CONFIG_EAP_PSK=y +CONFIG_EAP_SAKE=y +CONFIG_EAP_GPSK=y +CONFIG_EAP_PAX=y +CONFIG_EAP_LEAP=y +CONFIG_EAP_IKEV2=y +CONFIG_PCSC=y + +EAP-PEAP and EAP-TTLS will automatically include configured EAP +methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection. + + +After you have created a configuration file, you can build +wpa_supplicant and wpa_cli with 'make' command. You may then install +the binaries to a suitable system directory, e.g., /usr/local/bin. + +Example commands: + +# build wpa_supplicant and wpa_cli +make +# install binaries (this may need root privileges) +cp wpa_cli wpa_supplicant /usr/local/bin + + +You will need to make a configuration file, e.g., +/etc/wpa_supplicant.conf, with network configuration for the networks +you are going to use. Configuration file section below includes +explanation fo the configuration file format and includes various +examples. Once the configuration is ready, you can test whether the +configuration work by first running wpa_supplicant with following +command to start it on foreground with debugging enabled: + +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d + +Assuming everything goes fine, you can start using following command +to start wpa_supplicant on background without debugging: + +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B + +Please note that if you included more than one driver interface in the +build time configuration (.config), you may need to specify which +interface to use by including -D<driver name> option on the command +line. See following section for more details on command line options +for wpa_supplicant. + + + +Command line options +-------------------- + +usage: + wpa_supplicant [-BddfhKLqqtuvwW] [-P<pid file>] [-g<global ctrl>] \ + -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>] ...] + +options: + -b = optional bridge interface name + -B = run daemon in the background + -c = Configuration file + -C = ctrl_interface parameter (only used if -c is not) + -i = interface name + -d = increase debugging verbosity (-dd even more) + -D = driver name (can be multiple drivers: nl80211,wext) + -f = Log output to default log location (normally /tmp) + -g = global ctrl_interface + -K = include keys (passwords, etc.) in debug output + -t = include timestamp in debug messages + -h = show this help text + -L = show license (GPL and BSD) + -p = driver parameters + -P = PID file + -q = decrease debugging verbosity (-qq even less) + -u = enable DBus control interface + -v = show version + -w = wait for interface to be added, if needed + -W = wait for a control interface monitor before starting + -N = start describing new interface + +drivers: + hostap = Host AP driver (Intersil Prism2/2.5/3) [default] + (this can also be used with Linuxant DriverLoader) + madwifi = MADWIFI 802.11 support (Atheros, etc.) (deprecated; use wext) + wext = Linux wireless extensions (generic) + ralink = Ralink Client driver + broadcom = Broadcom wl.o driver + wired = wpa_supplicant wired Ethernet driver + roboswitch = wpa_supplicant Broadcom switch driver + bsd = BSD 802.11 support (Atheros, etc.) + ndis = Windows NDIS driver + +In most common cases, wpa_supplicant is started with + +wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0 + +This makes the process fork into background. + +The easiest way to debug problems, and to get debug log for bug +reports, is to start wpa_supplicant on foreground with debugging +enabled: + +wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d + +If the specific driver wrapper is not known beforehand, it is possible +to specify multiple comma separated driver wrappers on the command +line. wpa_supplicant will use the first driver wrapper that is able to +initialize the interface. + +wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0 + + +wpa_supplicant can control multiple interfaces (radios) either by +running one process for each interface separately or by running just +one process and list of options at command line. Each interface is +separated with -N argument. As an example, following command would +start wpa_supplicant for two interfaces: + +wpa_supplicant \ + -c wpa1.conf -i wlan0 -D hostap -N \ + -c wpa2.conf -i ath0 -D madwifi + + +If the interface is added in a Linux bridge (e.g., br0), the bridge +interface needs to be configured to wpa_supplicant in addition to the +main interface: + +wpa_supplicant -cw.conf -Dmadwifi -iath0 -bbr0 + + +Configuration file +------------------ + +wpa_supplicant is configured using a text file that lists all accepted +networks and security policies, including pre-shared keys. See +example configuration file, wpa_supplicant.conf, for detailed +information about the configuration format and supported fields. + +Changes to configuration file can be reloaded be sending SIGHUP signal +to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarly, +reloading can be triggered with 'wpa_cli reconfigure' command. + +Configuration file can include one or more network blocks, e.g., one +for each used SSID. wpa_supplicant will automatically select the best +betwork based on the order of network blocks in the configuration +file, network security level (WPA/WPA2 is preferred), and signal +strength. + +Example configuration files for some common configurations: + +1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work + network + +# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +# +# home network; allow all valid ciphers +network={ + ssid="home" + scan_ssid=1 + key_mgmt=WPA-PSK + psk="very secret passphrase" +} +# +# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers +network={ + ssid="work" + scan_ssid=1 + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" +} + + +2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel + (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series) + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=0" + phase2="auth=MSCHAPV2" +} + + +3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the + unencrypted use. Real identity is sent only within an encrypted TLS tunnel. + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MD5" +} + + +4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and + broadcast); use EAP-TLS for authentication + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="1x-test" + scan_ssid=1 + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} + + +5) Catch all example that allows more or less all configuration modes. The + configuration options are used based on what security policy is used in the + selected SSID. This is mostly for testing and is not recommended for normal + use. + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" +} + + +6) Authentication for wired Ethernet. This can be used with 'wired' or + 'roboswitch' interface (-Dwired or -Droboswitch on command line). + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +ap_scan=0 +network={ + key_mgmt=IEEE8021X + eap=MD5 + identity="user" + password="password" + eapol_flags=0 +} + + + +Certificates +------------ + +Some EAP authentication methods require use of certificates. EAP-TLS +uses both server side and client certificates whereas EAP-PEAP and +EAP-TTLS only require the server side certificate. When client +certificate is used, a matching private key file has to also be +included in configuration. If the private key uses a passphrase, this +has to be configured in wpa_supplicant.conf ("private_key_passwd"). + +wpa_supplicant supports X.509 certificates in PEM and DER +formats. User certificate and private key can be included in the same +file. + +If the user certificate and private key is received in PKCS#12/PFX +format, they need to be converted to suitable PEM/DER format for +wpa_supplicant. This can be done, e.g., with following commands: + +# convert client certificate and private key to PEM format +openssl pkcs12 -in example.pfx -out user.pem -clcerts +# convert CA certificate (if included in PFX file) to PEM format +openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys + + + +wpa_cli +------- + +wpa_cli is a text-based frontend program for interacting with +wpa_supplicant. It is used to query current status, change +configuration, trigger events, and request interactive user input. + +wpa_cli can show the current authentication status, selected security +mode, dot11 and dot1x MIBs, etc. In addition, it can configure some +variables like EAPOL state machine parameters and trigger events like +reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user +interface to request authentication information, like username and +password, if these are not included in the configuration. This can be +used to implement, e.g., one-time-passwords or generic token card +authentication where the authentication is based on a +challenge-response that uses an external device for generating the +response. + +The control interface of wpa_supplicant can be configured to allow +non-root user access (ctrl_interface_group in the configuration +file). This makes it possible to run wpa_cli with a normal user +account. + +wpa_cli supports two modes: interactive and command line. Both modes +share the same command set and the main difference is in interactive +mode providing access to unsolicited messages (event messages, +username/password requests). + +Interactive mode is started when wpa_cli is executed without including +the command as a command line parameter. Commands are then entered on +the wpa_cli prompt. In command line mode, the same commands are +entered as command line arguments for wpa_cli. + + +Interactive authentication parameters request + +When wpa_supplicant need authentication parameters, like username and +password, which are not present in the configuration file, it sends a +request message to all attached frontend programs, e.g., wpa_cli in +interactive mode. wpa_cli shows these requests with +"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or +OTP (one-time-password). <id> is a unique identifier for the current +network. <text> is description of the request. In case of OTP request, +it includes the challenge from the authentication server. + +The reply to these requests can be given with 'identity', 'password', +and 'otp' commands. <id> needs to be copied from the the matching +request. 'password' and 'otp' commands can be used regardless of +whether the request was for PASSWORD or OTP. The main difference +between these two commands is that values given with 'password' are +remembered as long as wpa_supplicant is running whereas values given +with 'otp' are used only once and then forgotten, i.e., wpa_supplicant +will ask frontend for a new value for every use. This can be used to +implement one-time-password lists and generic token card -based +authentication. + +Example request for password and a matching reply: + +CTRL-REQ-PASSWORD-1:Password needed for SSID foobar +> password 1 mysecretpassword + +Example request for generic token card challenge-response: + +CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar +> otp 2 9876 + + +wpa_cli commands + + status = get current WPA/EAPOL/EAP status + mib = get MIB variables (dot1x, dot11) + help = show this usage help + interface [ifname] = show interfaces/select interface + level <debug level> = change debug level + license = show full wpa_cli license + logoff = IEEE 802.1X EAPOL state machine logoff + logon = IEEE 802.1X EAPOL state machine logon + set = set variables (shows list of variables when run without arguments) + pmksa = show PMKSA cache + reassociate = force reassociation + reconfigure = force wpa_supplicant to re-read its configuration file + preauthenticate <BSSID> = force preauthentication + identity <network id> <identity> = configure identity for an SSID + password <network id> <password> = configure password for an SSID + pin <network id> <pin> = configure pin for an SSID + otp <network id> <password> = configure one-time-password for an SSID + passphrase <network id> <passphrase> = configure private key passphrase + for an SSID + bssid <network id> <BSSID> = set preferred BSSID for an SSID + list_networks = list configured networks + select_network <network id> = select a network (disable others) + enable_network <network id> = enable a network + disable_network <network id> = disable a network + add_network = add a network + remove_network <network id> = remove a network + set_network <network id> <variable> <value> = set network variables (shows + list of variables when run without arguments) + get_network <network id> <variable> = get network variables + save_config = save the current configuration + disconnect = disconnect and wait for reassociate command before connecting + scan = request new BSS scan + scan_results = get latest scan results + get_capability <eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies + terminate = terminate wpa_supplicant + quit = exit wpa_cli + + +wpa_cli command line options + +wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] [-a<action file>] \ + [-P<pid file>] [-g<global ctrl>] [command..] + -h = help (show this usage text) + -v = shown version information + -a = run in daemon mode executing the action file based on events from + wpa_supplicant + -B = run a daemon in the background + default path: /var/run/wpa_supplicant + default interface: first interface found in socket path + + +Using wpa_cli to run external program on connect/disconnect +----------------------------------------------------------- + +wpa_cli can used to run external programs whenever wpa_supplicant +connects or disconnects from a network. This can be used, e.g., to +update network configuration and/or trigget DHCP client to update IP +addresses, etc. + +One wpa_cli process in "action" mode needs to be started for each +interface. For example, the following command starts wpa_cli for the +default ingterface (-i can be used to select the interface in case of +more than one interface being used at the same time): + +wpa_cli -a/sbin/wpa_action.sh -B + +The action file (-a option, /sbin/wpa_action.sh in this example) will +be executed whenever wpa_supplicant completes authentication (connect +event) or detects disconnection). The action script will be called +with two command line arguments: interface name and event (CONNECTED +or DISCONNECTED). If the action script needs to get more information +about the current network, it can use 'wpa_cli status' to query +wpa_supplicant for more information. + +Following example can be used as a simple template for an action +script: + +#!/bin/sh + +IFNAME=$1 +CMD=$2 + +if [ "$CMD" = "CONNECTED" ]; then + SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=` + # configure network, signal DHCP client, etc. +fi + +if [ "$CMD" = "DISCONNECTED" ]; then + # remove network configuration, if needed + SSID= +fi + + + +Integrating with pcmcia-cs/cardmgr scripts +------------------------------------------ + +wpa_supplicant needs to be running when using a wireless network with +WPA. It can be started either from system startup scripts or from +pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be +completed before data frames can be exchanged, so wpa_supplicant +should be started before DHCP client. + +For example, following small changes to pcmcia-cs scripts can be used +to enable WPA support: + +Add MODE="Managed" and WPA="y" to the network scheme in +/etc/pcmcia/wireless.opts. + +Add the following block to the end of 'start' action handler in +/etc/pcmcia/wireless: + + if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf \ + -i$DEVICE + fi + +Add the following block to the end of 'stop' action handler (may need +to be separated from other actions) in /etc/pcmcia/wireless: + + if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + killall wpa_supplicant + fi + +This will make cardmgr start wpa_supplicant when the card is plugged +in. + + + +Dynamic interface add and operation without configuration files +--------------------------------------------------------------- + +wpa_supplicant can be started without any configuration files or +network interfaces. When used in this way, a global (i.e., per +wpa_supplicant process) control interface is used to add and remove +network interfaces. Each network interface can then be configured +through a per-network interface control interface. For example, +following commands show how to start wpa_supplicant without any +network interfaces and then add a network interface and configure a +network (SSID): + +# Start wpa_supplicant in the background +wpa_supplicant -g/var/run/wpa_supplicant-global -B + +# Add a new interface (wlan0, no configuration file, driver=wext, and +# enable control interface) +wpa_cli -g/var/run/wpa_supplicant-global interface_add wlan0 \ + "" wext /var/run/wpa_supplicant + +# Configure a network using the newly added network interface: +wpa_cli -iwlan0 add_network +wpa_cli -iwlan0 set_network 0 ssid '"test"' +wpa_cli -iwlan0 set_network 0 key_mgmt WPA-PSK +wpa_cli -iwlan0 set_network 0 psk '"12345678"' +wpa_cli -iwlan0 set_network 0 pairwise TKIP +wpa_cli -iwlan0 set_network 0 group TKIP +wpa_cli -iwlan0 set_network 0 proto WPA +wpa_cli -iwlan0 enable_network 0 + +# At this point, the new network interface should start trying to associate +# with the WPA-PSK network using SSID test. + +# Remove network interface +wpa_cli -g/var/run/wpa_supplicant-global interface_remove wlan0 + + +Privilege separation +-------------------- + +To minimize the size of code that needs to be run with root privileges +(e.g., to control wireless interface operation), wpa_supplicant +supports optional privilege separation. If enabled, this separates the +privileged operations into a separate process (wpa_priv) while leaving +rest of the code (e.g., EAP authentication and WPA handshakes) into an +unprivileged process (wpa_supplicant) that can be run as non-root +user. Privilege separation restricts the effects of potential software +errors by containing the majority of the code in an unprivileged +process to avoid full system compromise. + +Privilege separation is not enabled by default and it can be enabled +by adding CONFIG_PRIVSEP=y to the build configuration (.config). When +enabled, the privileged operations (driver wrapper and l2_packet) are +linked into a separate daemon program, wpa_priv. The unprivileged +program, wpa_supplicant, will be built with a special driver/l2_packet +wrappers that communicate with the privileged wpa_priv process to +perform the needed operations. wpa_priv can control what privileged +are allowed. + +wpa_priv needs to be run with network admin privileges (usually, root +user). It opens a UNIX domain socket for each interface that is +included on the command line; any other interface will be off limits +for wpa_supplicant in this kind of configuration. After this, +wpa_supplicant can be run as a non-root user (e.g., all standard users +on a laptop or as a special non-privileged user account created just +for this purpose to limit access to user files even further). + + +Example configuration: +- create user group for users that are allowed to use wpa_supplicant + ('wpapriv' in this example) and assign users that should be able to + use wpa_supplicant into that group +- create /var/run/wpa_priv directory for UNIX domain sockets and control + user access by setting it accessible only for the wpapriv group: + mkdir /var/run/wpa_priv + chown root:wpapriv /var/run/wpa_priv + chmod 0750 /var/run/wpa_priv +- start wpa_priv as root (e.g., from system startup scripts) with the + enabled interfaces configured on the command line: + wpa_priv -B -P /var/run/wpa_priv.pid wext:ath0 +- run wpa_supplicant as non-root with a user that is in wpapriv group: + wpa_supplicant -i ath0 -c wpa_supplicant.conf + +wpa_priv does not use the network interface before wpa_supplicant is +started, so it is fine to include network interfaces that are not +available at the time wpa_priv is started. As an alternative, wpa_priv +can be started when an interface is added (hotplug/udev/etc. scripts). +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. diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P new file mode 100644 index 0000000..0c1ca03 --- /dev/null +++ b/wpa_supplicant/README-P2P @@ -0,0 +1,522 @@ +wpa_supplicant and Wi-Fi P2P +============================ + +This document describes how the Wi-Fi P2P implementation in +wpa_supplicant can be configured and how an external component on the +client (e.g., management GUI) is used to enable WPS enrollment and +registrar registration. + + +Introduction to Wi-Fi P2P +------------------------- + +TODO + +More information about Wi-Fi P2P is available from Wi-Fi Alliance: +http://www.wi-fi.org/Wi-Fi_Direct.php + + +wpa_supplicant implementation +----------------------------- + +TODO + + +wpa_supplicant configuration +---------------------------- + +Wi-Fi P2P is an optional component that needs to be enabled in the +wpa_supplicant build configuration (.config). Here is an example +configuration that includes Wi-Fi P2P support and Linux nl80211 +-based driver interface: + +CONFIG_DRIVER_NL80211=y +CONFIG_CTRL_IFACE=y +CONFIG_P2P=y +CONFIG_AP=y +CONFIG_WPS=y + + +In run-time configuration file (wpa_supplicant.conf), some parameters +for P2P may be set. In order to make the devices easier to recognize, +device_name and device_type should be specified. For example, +something like this should be included: + +ctrl_interface=/var/run/wpa_supplicant +device_name=My P2P Device +device_type=1-0050F204-1 + + +wpa_cli +------- + +Actual Wi-Fi P2P operations are requested during runtime. These can be +done for example using wpa_cli (which is described below) or a GUI +like wpa_gui-qt4. + + +wpa_cli starts in interactive mode if no command string is included on +the command line. By default, it will select the first network interface +that it can find (and that wpa_supplicant controls). If more than one +interface is in use, it may be necessary to select one of the explicitly +by adding -i argument on the command line (e.g., 'wpa_cli -i wlan1'). + +Most of the P2P operations are done on the main interface (e.g., the +interface that is automatically added when the driver is loaded, e.g., +wlan0). When using a separate virtual interface for group operations +(e.g., wlan1), the control interface for that group interface may need +to be used for some operations (mainly WPS activation in GO). This may +change in the future so that all the needed operations could be done +over the main control interface. + +Device Discovery + +p2p_find [timeout in seconds] [type=<social|progressive>] + +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 +channels, i.e., it skips the initial full scan. type=progressive is +like the default behavior, but it will scan through all the channels +progressively one channel at the time in the Search state rounds. This +will help in finding new groups or groups missed during the initial +full scan. + +p2p_listen [timeout in seconds] + +Start Listen-only state (become discoverable without searching for +other devices). Optional parameter can be used to specify the duration +for the Listen operation in seconds. This command may not be of that +much use during normal operations and is mainly designed for +testing. It can also be used to keep the device discoverable without +having to maintain a group. + +p2p_stop_find + +Stop ongoing P2P device discovery or other operation (connect, listen +mode). + +p2p_flush + +Flush P2P peer table and state. + +Group Formation + +p2p_prov_disc <peer device address> <display|keypad|pbc> + +Send P2P provision discovery request to the specified peer. The +parameters for this command are the P2P device address of the peer and +the desired configuration method. For example, "p2p_prov_disc +02:01:02:03:04:05 display" would request the peer to display a PIN for +us and "p2p_prov_disc 02:01:02:03:04:05 keypad" would request the peer +to enter a PIN that we display. + +p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad] + [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] + +Start P2P group formation with a discovered P2P peer. This includes +optional group owner negotiation, group interface setup, provisioning, +and establishing data connection. + +The <pbc|pin|PIN#> parameter specifies the WPS provisioning +method. "pbc" string starts pushbutton method, "pin" string start PIN +method using an automatically generated PIN (which will be returned as +the command return code), PIN# means that a pre-selected PIN can be +used (e.g., 12345670). [label|display|keypad] is used with PIN method +to specify which PIN is used (label=PIN from local label, +display=dynamically generated random PIN from local display, +keypad=PIN entered from peer device label or display). "persistent" +parameter can be used to request a persistent group to be formed. + +"join" indicates that this is a command to join an existing group as a +client. It skips the GO Negotiation part. This will send a Provision +Discovery Request message to the target GO before associating for WPS +provisioning. + +"auth" indicates that the WPS parameters are authorized for the peer +device without actually starting GO Negotiation (i.e., the peer is +expected to initiate GO Negotiation). This is mainly for testing +purposes. + +"go_intent" can be used to override the default GO Intent for this GO +Negotiation. + +"freq" can be used to set a forced operating channel (e.g., freq=2412 +to select 2.4 GHz channel 1). + +p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] + +Set up a P2P group owner manually (i.e., without group owner +negotiation with a specific peer). This is also known as autonomous +GO. Optional persistent=<network id> can be used to specify restart of +a persistent group. Optional freq=<freq in MHz> can be used to force +the GO to be started on a specific frequency. Special freq=2 or freq=5 +options can be used to request the best 2.4 GHz or 5 GHz band channel +to be selected automatically. + +p2p_reject <peer device address> + +Reject connection attempt from a peer (specified with a device +address). This is a mechanism to reject a pending GO Negotiation with +a peer and request to automatically block any further connection or +discovery of the peer. + +p2p_group_remove <group interface> + +Terminate a P2P group. If a new virtual network interface was used for +the group, it will also be removed. The network interface name of the +group interface is used as a parameter for this command. + +p2p_cancel + +Cancel an ongoing P2P group formation related operation. + +Service Discovery + +p2p_serv_disc_req + +Schedule a P2P service discovery request. The parameters for this +command are the device address of the peer device (or 00:00:00:00:00:00 +for wildcard query that is sent to every discovered P2P peer that +supports service discovery) and P2P Service Query TLV(s) as hexdump. For +example, + +p2p_serv_disc_req 00:00:00:00:00:00 02000001 + +schedules a request for listing all available services of all service +discovery protocols and requests this to be sent to all discovered +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). + +This command returns an identifier for the pending query (e.g., +"1f77628") that can be used to cancel the request. Directed requests +will be automatically removed when the specified peer has replied to +it. + +For UPnP, an alternative command format can be used to specify a +single query TLV (i.e., a service discovery for a specific UPnP +service): + +p2p_serv_disc_req 00:00:00:00:00:00 upnp <version hex> <ST: from M-SEARCH> + +For example: + +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1 + +Additional examples for queries: + +# list of all Bonjour services +p2p_serv_disc_req 00:00:00:00:00:00 02000101 + +# list of all UPnP services +p2p_serv_disc_req 00:00:00:00:00:00 02000201 + +# list of all WS-Discovery services +p2p_serv_disc_req 00:00:00:00:00:00 02000301 + +# list of all Bonjour and UPnP services +p2p_serv_disc_req 00:00:00:00:00:00 0200010102000202 + +# Apple File Sharing over TCP +p2p_serv_disc_req 00:00:00:00:00:00 130001010b5f6166706f766572746370c00c000c01 + +# Bonjour SSTH (supported service type hash) +p2p_serv_disc_req 00:00:00:00:00:00 05000101000000 + +# UPnP examples +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 ssdp:all +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 upnp:rootdevice +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:service:ContentDirectory:2 +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012 +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1 + +p2p_serv_disc_cancel_req <query identifier> + +Cancel a pending P2P service discovery request. This command takes a +single parameter: identifier for the pending query (the value returned +by p2p_serv_disc_req, e.g., "p2p_serv_disc_cancel_req 1f77628". + +p2p_serv_disc_resp + +Reply to a service discovery query. This command takes following +parameters: frequency in MHz, destination address, dialog token, +response TLV(s). The first three parameters are copied from the +request event. For example, "p2p_serv_disc_resp 2437 02:40:61:c2:f3:b7 +1 0300000101". This command is used only if external program is used +to process the request (see p2p_serv_disc_external). + +p2p_service_update + +Indicate that local services have changed. This is used to increment +the P2P service indicator value so that peers know when previously +cached information may have changed. This is only needed when external +service discovery processing is enabled since the commands to +pre-configure services for internal processing will increment the +indicator automatically. + +p2p_serv_disc_external <0|1> + +Configure external processing of P2P service requests: 0 (default) = +no external processing of requests (i.e., internal code will process +each request based on pre-configured services), 1 = external +processing of requests (external program is responsible for replying +to service discovery requests with p2p_serv_disc_resp). Please note +that there is quite strict limit on how quickly the response needs to +be transmitted, so use of the internal processing is strongly +recommended. + +p2p_service_add bonjour <query hexdump> <RDATA hexdump> + +Add a local Bonjour service for internal SD query processing. + +Examples: + +# AFP Over TCP (PTR) +p2p_service_add bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027 +# AFP Over TCP (TXT) (RDATA=null) +p2p_service_add bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00 + +# IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.) +p2p_service_add bonjour 045f697070c00c000c01 094d795072696e746572c027 +# IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript) +p2p_service_add bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074 + +# Supported Service Type Hash (SSTH) +p2p_service_add bonjour 000000 <32-byte bitfield as hexdump> +(note: see P2P spec Annex E.4 for information on how to construct the bitfield) + +p2p_service_del bonjour <query hexdump> + +Remove a local Bonjour service from internal SD query processing. + +p2p_service_add upnp <version hex> <service> + +Add a local UPnP service for internal SD query processing. + +Examples: + +p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice +p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice +p2p_service_add upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2 +p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2 +p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1 + +p2p_service_del upnp <version hex> <service> + +Remove a local UPnP service from internal SD query processing. + +p2p_service_flush + +Remove all local services from internal SD query processing. + +Invitation + +p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address] + [go_dev_addr=address] + +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 +the persisten group, the peer parameter is not needed. Otherwise it is +used to specify which device to invite. go_dev_addr parameter can be +used to override the GO device address for Invitation Request should +it be not known for some reason (this should not be needed in most +cases). + +Group Operations + +(These are used on the group interface.) + +wps_pin <any|address> <PIN> + +Start WPS PIN method. This allows a single WPS Enrollee to connect to +the AP/GO. This is used on the GO when a P2P client joins an existing +group. The second parameter is the address of the Enrollee or a string +"any" to allow any station to use the entered PIN (which will restrict +the PIN for one-time-use). PIN is the Enrollee PIN read either from a +label or display on the P2P Client/WPS Enrollee. + +wps_pbc + +Start WPS PBC method (i.e., push the button). This allows a single WPS +Enrollee to connect to the AP/GO. This is used on the GO when a P2P +client joins an existing group. + +p2p_get_passphrase + +Get the passphrase for a group (only available when acting as a GO). + +p2p_presence_req [<duration> <interval>] [<duration> <interval>] + +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 +interval values in microseconds. If the second pair is included, that +indicates which value would be acceptable. + +Parameters + +p2p_ext_listen [<period> <interval>] + +Configure Extended Listen Timing. If the parameters are omitted, this +feature is disabled. If the parameters are included, Listen State will +be entered every interval msec for at least period msec. Both values +have acceptable range of 1-65535 (with interval obviously having to be +larger than or equal to duration). If the P2P module is not idle at +the time the Extended Listen Timing timeout occurs, the Listen State +operation will be skipped. + +The configured values will also be advertised to other P2P Devices. The +received values are available in the p2p_peer command output: + +ext_listen_period=100 ext_listen_interval=5000 + +p2p_set <field> <value> + +Change dynamic P2P parameters + +p2p_set discoverability <0/1> + +Disable/enable advertisement of client discoverability. This is +enabled by default and this parameter is mainly used to allow testing +of device discoverability. + +p2p_set managed <0/1> + +Disable/enable managed P2P Device operations. This is disabled by +default. + +p2p_set listen_channel <1/6/11> + +Set P2P Listen channel. This is mainly meant for testing purposes and +changing the Listen channel during normal operations can result in +protocol failures. + +p2p_set ssid_postfix <postfix> + +Set postfix string to be added to the automatically generated P2P SSID +(DIRECT-<two random characters>). For example, postfix of "-testing" +could result in the SSID becoming DIRECT-ab-testing. + +set <field> <value> + +Set global configuration parameters which may also affect P2P +operations. The format on these parameters is same as is used in +wpa_supplicant.conf. Only the parameters listen here should be +changed. Modifying other parameters may result in incorrect behavior +since not all existing users of the parameters are updated. + +set uuid <UUID> + +Set WPS UUID (by default, this is generated based on the MAC address). + +set device_name <device name> + +Set WPS Device Name (also included in some P2P messages). + +set manufacturer <manufacturer> + +Set WPS Manufacturer. + +set model_name <model name> + +Set WPS Model Name. + +set model_number <model number> + +Set WPS Model Number. + +set serial_number <serial number> + +Set WPS Serial Number. + +set device_type <device type> + +Set WPS Device Type. + +set os_version <OS version> + +Set WPS OS Version. + +set config_methods <config methods> + +Set WPS Configuration Methods. + +set sec_device_type <device type> + +Add a new Secondary Device Type. + +set p2p_go_intent <GO intent> + +Set the default P2P GO Intent. Note: This value can be overridden in +p2p_connect command and as such, there should be no need to change the +default value here during normal operations. + +set p2p_ssid_postfix <P2P SSID postfix> + +Set P2P SSID postfix. + +set persistent_reconnect <0/1> + +Disable/enabled persistent reconnect for reinvocation of persistent +groups. If enabled, invitations to reinvoke a persistent group will be +accepted without separate authorization (e.g., user interaction). + +set country <two character country code> + +Set country code (this is included in some P2P messages). + +Status + +p2p_peers [discovered] + +List P2P Device Addresses of all the P2P peers we know. The optional +"discovered" parameter filters out the peers that we have not fully +discovered, i.e., which we have only seen in a received Probe Request +frame. + +p2p_peer <P2P Device Address> + +Fetch information about a known P2P peer. + +Group Status + +(These are used on the group interface.) + +status + +Show status information (connection state, role, use encryption +parameters, IP address, etc.). + +sta + +Show information about an associated station (when acting in AP/GO role). + +all_sta + +Lists the currently associated stations. + +Configuration data + +list_networks + +Lists the configured networks, including stored information for +persistent groups. The identifier in this list is used with +p2p_group_add and p2p_invite to indicate which persistent group is to +be reinvoked. + +remove_network <network id> + +Remove a network entry from configuration. + + +wpa_cli action script +--------------------- + +See examples/p2p-action.sh + +TODO: describe DHCP/DNS setup +TODO: cross-connection diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS new file mode 100644 index 0000000..6aa3a7b --- /dev/null +++ b/wpa_supplicant/README-WPS @@ -0,0 +1,297 @@ +wpa_supplicant and Wi-Fi Protected Setup (WPS) +============================================== + +This document describes how the WPS implementation in wpa_supplicant +can be configured and how an external component on the client (e.g., +management GUI) is used to enable WPS enrollment and registrar +registration. + + +Introduction to WPS +------------------- + +Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a +wireless network. It allows automated generation of random keys (WPA +passphrase/PSK) and configuration of an access point and client +devices. WPS includes number of methods for setting up connections +with PIN method and push-button configuration (PBC) being the most +commonly deployed options. + +While WPS can enable more home networks to use encryption in the +wireless network, it should be noted that the use of the PIN and +especially PBC mechanisms for authenticating the initial key setup is +not very secure. As such, use of WPS may not be suitable for +environments that require secure network access without chance for +allowing outsiders to gain access during the setup phase. + +WPS uses following terms to describe the entities participating in the +network setup: +- access point: the WLAN access point +- Registrar: a device that control a network and can authorize + addition of new devices); this may be either in the AP ("internal + Registrar") or in an external device, e.g., a laptop, ("external + Registrar") +- Enrollee: a device that is being authorized to use the network + +It should also be noted that the AP and a client device may change +roles (i.e., AP acts as an Enrollee and client device as a Registrar) +when WPS is used to configure the access point. + + +More information about WPS is available from Wi-Fi Alliance: +http://www.wi-fi.org/wifi-protected-setup + + +wpa_supplicant implementation +----------------------------- + +wpa_supplicant includes an optional WPS component that can be used as +an Enrollee to enroll new network credential or as a Registrar to +configure an AP. The current version of wpa_supplicant does not +support operation as an external WLAN Management Registrar for adding +new client devices or configuring the AP over UPnP. + + +wpa_supplicant configuration +---------------------------- + +WPS is an optional component that needs to be enabled in +wpa_supplicant build configuration (.config). Here is an example +configuration that includes WPS support and Linux wireless extensions +-based driver interface: + +CONFIG_DRIVER_WEXT=y +CONFIG_WPS=y +CONFIG_WPS2=y + + +WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for +the device. This is configured in the runtime configuration for +wpa_supplicant (if not set, UUID will be generated based on local MAC +address): + +# example UUID for WPS +uuid=12345678-9abc-def0-1234-56789abcdef0 + +The network configuration blocks needed for WPS are added +automatically based on control interface commands, so they do not need +to be added explicitly in the configuration file. + +WPS registration will generate new network blocks for the acquired +credentials. If these are to be stored for future use (after +restarting wpa_supplicant), wpa_supplicant will need to be configured +to allow configuration file updates: + +update_config=1 + + + +External operations +------------------- + +WPS requires either a device PIN code (usually, 8-digit number) or a +pushbutton event (for PBC) to allow a new WPS Enrollee to join the +network. wpa_supplicant uses the control interface as an input channel +for these events. + +The PIN value used in the commands must be processed by an UI to +remove non-digit characters and potentially, to verify the checksum +digit. "wpa_cli wps_check_pin <PIN>" can be used to do such processing. +It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if the checksum +digit is incorrect, or the processed PIN (non-digit characters removed) +if the PIN is valid. + +If the client device has a display, a random PIN has to be generated +for each WPS registration session. wpa_supplicant can do this with a +control interface request, e.g., by calling wpa_cli: + +wpa_cli wps_pin any + +This will return the generated 8-digit PIN which will then need to be +entered at the Registrar to complete WPS registration. At that point, +the client will be enrolled with credentials needed to connect to the +AP to access the network. + + +If the client device does not have a display that could show the +random PIN, a hardcoded PIN that is printed on a label can be +used. wpa_supplicant is notified this with a control interface +request, e.g., by calling wpa_cli: + +wpa_cli wps_pin any 12345670 + +This starts the WPS negotiation in the same way as above with the +generated PIN. + + +If the client design wants to support optional WPS PBC mode, this can +be enabled by either a physical button in the client device or a +virtual button in the user interface. The PBC operation requires that +a button is also pressed at the AP/Registrar at about the same time (2 +minute window). wpa_supplicant is notified of the local button event +over the control interface, e.g., by calling wpa_cli: + +wpa_cli wps_pbc + +At this point, the AP/Registrar has two minutes to complete WPS +negotiation which will generate a new WPA PSK in the same way as the +PIN method described above. + + +If the client wants to operate in the Registrar role to learn the +current AP configuration and optionally, to configure an AP, +wpa_supplicant is notified over the control interface, e.g., with +wpa_cli: + +wpa_cli wps_reg <AP BSSID> <AP PIN> +(example: wpa_cli wps_reg 02:34:56:78:9a:bc 12345670) + +This is used to fetch the current AP settings instead of actually +changing them. The main difference with the wps_pin command is that +wps_reg uses the AP PIN (e.g., from a label on the AP) instead of a +PIN generated at the client. + +In order to change the AP configuration, the new configuration +parameters are given to the wps_reg command: + +wpa_cli wps_reg <AP BSSID> <AP PIN> <new SSID> <auth> <encr> <new key> +examples: + wpa_cli wps_reg 02:34:56:78:9a:bc 12345670 testing WPA2PSK CCMP 12345678 + wpa_cli wps_reg 02:34:56:78:9a:bc 12345670 clear OPEN NONE "" + +<auth> must be one of the following: OPEN WPAPSK WPA2PSK +<encr> must be one of the following: NONE WEP TKIP CCMP + + +Scanning +-------- + +Scan results ('wpa_cli scan_results' or 'wpa_cli bss <idx>') include a +flags field that is used to indicate whether the BSS support WPS. If +the AP support WPS, but has not recently activated a Registrar, [WPS] +flag will be included. If PIN method has been recently selected, +[WPS-PIN] is shown instead. Similarly, [WPS-PBC] is shown if PBC mode +is in progress. GUI programs can use these as triggers for suggesting +a guided WPS configuration to the user. In addition, control interface +monitor events WPS-AP-AVAILABLE{,-PBC,-PIN} can be used to find out if +there are WPS enabled APs in scan results without having to go through +all the details in the GUI. These notification could be used, e.g., to +suggest possible WPS connection to the user. + + +wpa_gui +------- + +wpa_gui-qt4 directory contains a sample GUI that shows an example of +how WPS support can be integrated into the GUI. Its main window has a +WPS tab that guides user through WPS registration with automatic AP +selection. In addition, it shows how WPS can be started manually by +selecting an AP from scan results. + + +Credential processing +--------------------- + +By default, wpa_supplicant processes received credentials and updates +its configuration internally. However, it is possible to +control these operations from external programs, if desired. + +This internal processing can be disabled with wps_cred_processing=1 +option. When this is used, an external program is responsible for +processing the credential attributes and updating wpa_supplicant +configuration based on them. + +Following control interface messages are sent out for external programs: + +WPS-CRED-RECEIVED <hexdump of Credential attribute(s)> +For example: +<2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727 + + +wpa_supplicant as WPS External Registrar (ER) +--------------------------------------------- + +wpa_supplicant can be used as a WPS ER to configure an AP or enroll +new Enrollee to join the network. This functionality uses UPnP and +requires that a working IP connectivity is available with the AP (this +can be either over a wired or wireless connection). + +Separate wpa_supplicant process can be started for WPS ER +operations. A special "none" driver can be used in such a case to +indicate that no local network interface is actually controlled. For +example, following command could be used to start the ER: + +wpa_supplicant -Dnone -c er.conf -ieth0 + +Sample er.conf: + +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin +device_name=WPS External Registrar + + +wpa_cli commands for ER functionality: + +wps_er_start [IP address] +- start WPS ER functionality +- the optional IP address parameter can be used to filter operations only + to include a single AP +- if run again while ER is active, the stored information (discovered APs + and Enrollees) are shown again + +wps_er_stop +- stop WPS ER functionality + +wps_er_learn <UUID> <AP PIN> +- learn AP configuration + +wps_er_set_config <UUID> <network id> +- use AP configuration from a locally configured network (e.g., from + wps_reg command); this does not change the AP's configuration, but + only prepares a configuration to be used when enrolling a new device + to the AP + +wps_er_config <UUID> <AP PIN> <new SSID> <auth> <encr> <new key> +- examples: + wps_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 testing WPA2PSK CCMP 12345678 + wpa_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 clear OPEN NONE "" + +<auth> must be one of the following: OPEN WPAPSK WPA2PSK +<encr> must be one of the following: NONE WEP TKIP CCMP + + +wps_er_pbc <Enrollee UUID> +- accept an Enrollee PBC using External Registrar + +wps_er_pin <Enrollee UUID> <PIN> [Enrollee MAC address] +- add an Enrollee PIN to External Registrar +- if Enrollee UUID is not known, "any" can be used to add a wildcard PIN +- if the MAC address of the enrollee is known, it should be configured + to allow the AP to advertise list of authorized enrollees + + +WPS ER events: + +WPS_EVENT_ER_AP_ADD +- WPS ER discovered an AP + +WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1 |Very friendly name|Company|Long description of the model|WAP|http://w1.fi/|http://w1.fi/hostapd/ + +WPS_EVENT_ER_AP_REMOVE +- WPS ER removed an AP entry + +WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 + +WPS_EVENT_ER_ENROLLEE_ADD +- WPS ER discovered a new Enrollee + +WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0 pri_dev_type=1-0050F204-1 |Wireless Client|Company|cmodel|123|12345| + +WPS_EVENT_ER_ENROLLEE_REMOVE +- WPS ER removed an Enrollee entry + +WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27 + +WPS-ER-AP-SETTINGS +- WPS ER learned AP settings + +WPS-ER-AP-SETTINGS uuid=fd91b4ec-e3fa-5891-a57d-8c59efeed1d2 ssid=test-wps auth_type=0x0020 encr_type=0x0008 key=12345678 diff --git a/wpa_supplicant/README-Windows.txt b/wpa_supplicant/README-Windows.txt new file mode 100644 index 0000000..292223d --- /dev/null +++ b/wpa_supplicant/README-Windows.txt @@ -0,0 +1,450 @@ +wpa_supplicant for Windows +========================== + +Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + +This product includes software developed by the OpenSSL Project +for use in the OpenSSL Toolkit (http://www.openssl.org/). This +product includes cryptographic software written by Eric Young +(eay@cryptsoft.com). + + +wpa_supplicant has support for being used as a WPA/WPA2/IEEE 802.1X +Supplicant on Windows. The current port requires that WinPcap +(http://winpcap.polito.it/) is installed for accessing packets and the +driver interface. Both release versions 3.0 and 3.1 are supported. + +The current port is still somewhat experimental. It has been tested +mainly on Windows XP (SP2) with limited set of NDIS drivers. In +addition, the current version has been reported to work with Windows +2000. + +All security modes have been verified to work (at least complete +authentication and successfully ping a wired host): +- plaintext +- static WEP / open system authentication +- static WEP / shared key authentication +- IEEE 802.1X with dynamic WEP keys +- WPA-PSK, TKIP, CCMP, TKIP+CCMP +- WPA-EAP, TKIP, CCMP, TKIP+CCMP +- WPA2-PSK, TKIP, CCMP, TKIP+CCMP +- WPA2-EAP, TKIP, CCMP, TKIP+CCMP + + +Binary version +-------------- + +Compiled binary version of the wpa_supplicant and additional tools is +available from http://w1.fi/wpa_supplicant/. These binaries can be +used after installing WinPcap. + +wpa_gui uses Qt 4 framework and may need additional dynamic libraries +(DLLs). These libraries are available from +http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip +You can copy the DLL files from this ZIP package into the same directory +with wpa_gui.exe to allow wpa_gui to be started. + + +Building wpa_supplicant with mingw +---------------------------------- + +The default build setup for wpa_supplicant is to use MinGW and +cross-compiling from Linux to MinGW/Windows. It should also be +possible to build this under Windows using the MinGW tools, but that +is not tested nor supported and is likely to require some changes to +the Makefile unless cygwin is used. + + +Building wpa_supplicant with MSVC +--------------------------------- + +wpa_supplicant can be built with Microsoft Visual C++ compiler. This +has been tested with Microsoft Visual C++ Toolkit 2003 and Visual +Studio 2005 using the included nmake.mak as a Makefile for nmake. IDE +can also be used by creating a project that includes the files and +defines mentioned in nmake.mak. Example VS2005 solution and project +files are included in vs2005 subdirectory. This can be used as a +starting point for building the programs with VS2005 IDE. Visual Studio +2008 Express Edition is also able to use these project files. + +WinPcap development package is needed for the build and this can be +downloaded from http://www.winpcap.org/install/bin/WpdPack_4_0_2.zip. The +default nmake.mak expects this to be unpacked into C:\dev\WpdPack so +that Include and Lib directories are in this directory. The files can be +stored elsewhere as long as the WINPCAPDIR in nmake.mak is updated to +match with the selected directory. In case a project file in the IDE is +used, these Include and Lib directories need to be added to project +properties as additional include/library directories. + +OpenSSL source package can be downloaded from +http://www.openssl.org/source/openssl-0.9.8i.tar.gz and built and +installed following instructions in INSTALL.W32. Note that if EAP-FAST +support will be included in the wpa_supplicant, OpenSSL needs to be +patched to# support it openssl-0.9.8i-tls-extensions.patch. The example +nmake.mak file expects OpenSSL to be installed into C:\dev\openssl, but +this directory can be modified by changing OPENSSLDIR variable in +nmake.mak. + +If you do not need EAP-FAST support, you may also be able to use Win32 +binary installation package of OpenSSL from +http://www.slproweb.com/products/Win32OpenSSL.html instead of building +the library yourself. In this case, you will need to copy Include and +Lib directories in suitable directory, e.g., C:\dev\openssl for the +default nmake.mak. Copy {Win32OpenSSLRoot}\include into +C:\dev\openssl\include and make C:\dev\openssl\lib subdirectory with +files from {Win32OpenSSLRoot}\VC (i.e., libeay*.lib and ssleay*.lib). +This will end up using dynamically linked OpenSSL (i.e., .dll files are +needed) for it. Alternative, you can copy files from +{Win32OpenSSLRoot}\VC\static to create a static build (no OpenSSL .dll +files needed). + + +Building wpa_supplicant for cygwin +---------------------------------- + +wpa_supplicant can be built for cygwin by installing the needed +development packages for cygwin. This includes things like compiler, +make, openssl development package, etc. In addition, developer's pack +for WinPcap (WPdpack.zip) from +http://winpcap.polito.it/install/default.htm is needed. + +.config file should enable only one driver interface, +CONFIG_DRIVER_NDIS. In addition, include directories may need to be +added to match the system. An example configuration is available in +defconfig. The library and include files for WinPcap will either need +to be installed in compiler/linker default directories or their +location will need to be adding to .config when building +wpa_supplicant. + +Othen than this, the build should be more or less identical to Linux +version, i.e., just run make after having created .config file. An +additional tool, win_if_list.exe, can be built by running "make +win_if_list". + + +Building wpa_gui +---------------- + +wpa_gui uses Qt application framework from Trolltech. It can be built +with the open source version of Qt4 and MinGW. Following commands can +be used to build the binary in the Qt 4 Command Prompt: + +# go to the root directory of wpa_supplicant source code +cd wpa_gui-qt4 +qmake -o Makefile wpa_gui.pro +make +# the wpa_gui.exe binary is created into 'release' subdirectory + + +Using wpa_supplicant for Windows +-------------------------------- + +wpa_supplicant, wpa_cli, and wpa_gui behave more or less identically to +Linux version, so instructions in README and example wpa_supplicant.conf +should be applicable for most parts. In addition, there is another +version of wpa_supplicant, wpasvc.exe, which can be used as a Windows +service and which reads its configuration from registry instead of +text file. + +When using access points in "hidden SSID" mode, ap_scan=2 mode need to +be used (see wpa_supplicant.conf for more information). + +Windows NDIS/WinPcap uses quite long interface names, so some care +will be needed when starting wpa_supplicant. Alternatively, the +adapter description can be used as the interface name which may be +easier since it is usually in more human-readable +format. win_if_list.exe can be used to find out the proper interface +name. + +Example steps in starting up wpa_supplicant: + +# win_if_list.exe +ifname: \Device\NPF_GenericNdisWanAdapter +description: Generic NdisWan adapter + +ifname: \Device\NPF_{769E012B-FD17-4935-A5E3-8090C38E25D2} +description: Atheros Wireless Network Adapter (Microsoft's Packet Scheduler) + +ifname: \Device\NPF_{732546E7-E26C-48E3-9871-7537B020A211} +description: Intel 8255x-based Integrated Fast Ethernet (Microsoft's Packet Scheduler) + + +Since the example configuration used Atheros WLAN card, the middle one +is the correct interface in this case. The interface name for -i +command line option is the full string following "ifname:" (the +"\Device\NPF_" prefix can be removed). In other words, wpa_supplicant +would be started with the following command: + +# wpa_supplicant.exe -i'{769E012B-FD17-4935-A5E3-8090C38E25D2}' -c wpa_supplicant.conf -d + +-d optional enables some more debugging (use -dd for even more, if +needed). It can be left out if debugging information is not needed. + +With the alternative mechanism for selecting the interface, this +command has identical results in this case: + +# wpa_supplicant.exe -iAtheros -c wpa_supplicant.conf -d + + +Simple configuration example for WPA-PSK: + +#ap_scan=2 +ctrl_interface= +network={ + ssid="test" + key_mgmt=WPA-PSK + proto=WPA + pairwise=TKIP + psk="secret passphrase" +} + +(remove '#' from the comment out ap_scan line to enable mode in which +wpa_supplicant tries to associate with the SSID without doing +scanning; this allows APs with hidden SSIDs to be used) + + +wpa_cli.exe and wpa_gui.exe can be used to interact with the +wpa_supplicant.exe program in the same way as with Linux. Note that +ctrl_interface is using UNIX domain sockets when built for cygwin, but +the native build for Windows uses named pipes and the contents of the +ctrl_interface configuration item is used to control access to the +interface. Anyway, this variable has to be included in the configuration +to enable the control interface. + + +Example SDDL string formats: + +(local admins group has permission, but nobody else): + +ctrl_interface=SDDL=D:(A;;GA;;;BA) + +("A" == "access allowed", "GA" == GENERIC_ALL == all permissions, and +"BA" == "builtin administrators" == the local admins. The empty fields +are for flags and object GUIDs, none of which should be required in this +case.) + +(local admins and the local "power users" group have permissions, +but nobody else): + +ctrl_interface=SDDL=D:(A;;GA;;;BA)(A;;GA;;;PU) + +(One ACCESS_ALLOWED ACE for GENERIC_ALL for builtin administrators, and +one ACCESS_ALLOWED ACE for GENERIC_ALL for power users.) + +(close to wide open, but you have to be a valid user on +the machine): + +ctrl_interface=SDDL=D:(A;;GA;;;AU) + +(One ACCESS_ALLOWED ACE for GENERIC_ALL for the "authenticated users" +group.) + +This one would allow absolutely everyone (including anonymous +users) -- this is *not* recommended, since named pipes can be attached +to from anywhere on the network (i.e. there's no "this machine only" +like there is with 127.0.0.1 sockets): + +ctrl_interface=SDDL=D:(A;;GA;;;BU)(A;;GA;;;AN) + +(BU == "builtin users", "AN" == "anonymous") + +See also [1] for the format of ACEs, and [2] for the possible strings +that can be used for principal names. + +[1] +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp +[2] +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/sid_strings.asp + + +Starting wpa_supplicant as a Windows service (wpasvc.exe) +--------------------------------------------------------- + +wpa_supplicant can be started as a Windows service by using wpasvc.exe +program that is alternative build of wpa_supplicant.exe. Most of the +core functionality of wpasvc.exe is identical to wpa_supplicant.exe, +but it is using Windows registry for configuration information instead +of a text file and command line parameters. In addition, it can be +registered as a service that can be started automatically or manually +like any other Windows service. + +The root of wpa_supplicant configuration in registry is +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant. This level includes global +parameters and a 'interfaces' subkey with all the interface configuration +(adapter to confname mapping). Each such mapping is a subkey that has +'adapter', 'config', and 'ctrl_interface' values. + +This program can be run either as a normal command line application, +e.g., for debugging, with 'wpasvc.exe app' or as a Windows service. +Service need to be registered with 'wpasvc.exe reg <full path to +wpasvc.exe>'. Alternatively, 'wpasvc.exe reg' can be used to register +the service with the current location of wpasvc.exe. After this, wpasvc +can be started like any other Windows service (e.g., 'net start wpasvc') +or it can be configured to start automatically through the Services tool +in administrative tasks. The service can be unregistered with +'wpasvc.exe unreg'. + +If the service is set to start during system bootup to make the +network connection available before any user has logged in, there may +be a long (half a minute or so) delay in starting up wpa_supplicant +due to WinPcap needing a driver called "Network Monitor Driver" which +is started by default on demand. + +To speed up wpa_supplicant start during system bootup, "Network +Monitor Driver" can be configured to be started sooner by setting its +startup type to System instead of the default Demand. To do this, open +up Device Manager, select Show Hidden Devices, expand the "Non +Plug-and-Play devices" branch, double click "Network Monitor Driver", +go to the Driver tab, and change the Demand setting to System instead. + +Configuration data is in HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs +key. Each configuration profile has its own key under this. In terms of text +files, each profile would map to a separate text file with possibly multiple +networks. Under each profile, there is a networks key that lists all +networks as a subkey. Each network has set of values in the same way as +network block in the configuration file. In addition, blobs subkey has +possible blobs as values. + +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000 + ssid="example" + key_mgmt=WPA-PSK + +See win_example.reg for an example on how to setup wpasvc.exe +parameters in registry. It can also be imported to registry as a +starting point for the configuration. + + + +License information for third party software used in this product: + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2004 The OpenSSL Project. 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. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED 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 OpenSSL PROJECT OR + * ITS 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. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + + + + Qt Open Source Edition + ---------------------- + +The Qt GUI Toolkit is Copyright (C) 1994-2007 Trolltech ASA. +Qt Open Source Edition is licensed under GPL version 2. + +Source code for the library is available at +http://w1.fi/wpa_supplicant/qt4/qt-win-opensource-src-4.3.3.zip diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c new file mode 100644 index 0000000..95279d3 --- /dev/null +++ b/wpa_supplicant/ap.c @@ -0,0 +1,866 @@ +/* + * WPA Supplicant - Basic AP mode support routines + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, Atheros Communications + * + * 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "ap/hostapd.h" +#include "ap/ap_config.h" +#include "ap/ap_drv_ops.h" +#ifdef NEED_AP_MLME +#include "ap/ieee802_11.h" +#endif /* NEED_AP_MLME */ +#include "ap/beacon.h" +#include "ap/ieee802_1x.h" +#include "ap/wps_hostapd.h" +#include "ap/ctrl_iface_ap.h" +#include "eap_common/eap_defs.h" +#include "eap_server/eap_methods.h" +#include "eap_common/eap_wsc_common.h" +#include "wps/wps.h" +#include "common/ieee802_11_defs.h" +#include "config_ssid.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "p2p_supplicant.h" +#include "ap.h" +#include "ap/sta_info.h" +#include "notify.h" + + +#ifdef CONFIG_WPS +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); +#endif /* CONFIG_WPS */ + + +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]; + int pairwise; + + conf->driver = wpa_s->driver; + + os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); + + if (ssid->frequency == 0) { + /* default channel 11 */ + conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + conf->channel = 11; + } else if (ssid->frequency >= 2412 && ssid->frequency <= 2472) { + conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + conf->channel = (ssid->frequency - 2407) / 5; + } else if ((ssid->frequency >= 5180 && ssid->frequency <= 5240) || + (ssid->frequency >= 5745 && ssid->frequency <= 5825)) { + conf->hw_mode = HOSTAPD_MODE_IEEE80211A; + conf->channel = (ssid->frequency - 5000) / 5; + } else { + wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", + ssid->frequency); + return -1; + } + + /* TODO: enable HT if driver supports it; + * drop to 11b if driver does not support 11g */ + +#ifdef CONFIG_P2P + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { + /* Remove 802.11b rates from supported and basic rate sets */ + int *list = os_malloc(4 * sizeof(int)); + if (list) { + list[0] = 60; + list[1] = 120; + list[2] = 240; + list[3] = -1; + } + conf->basic_rates = list; + + list = os_malloc(9 * sizeof(int)); + if (list) { + list[0] = 60; + list[1] = 90; + list[2] = 120; + list[3] = 180; + list[4] = 240; + list[5] = 360; + list[6] = 480; + list[7] = 540; + list[8] = -1; + } + conf->supported_rates = list; + } +#endif /* CONFIG_P2P */ + + if (ssid->ssid_len == 0) { + wpa_printf(MSG_ERROR, "No SSID configured for AP mode"); + return -1; + } + os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len); + bss->ssid.ssid[ssid->ssid_len] = '\0'; + bss->ssid.ssid_len = ssid->ssid_len; + bss->ssid.ssid_set = 1; + + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) + bss->wpa = ssid->proto; + bss->wpa_key_mgmt = ssid->key_mgmt; + bss->wpa_pairwise = ssid->pairwise_cipher; + if (ssid->passphrase) { + bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); + } else if (ssid->psk_set) { + 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; + os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN); + bss->ssid.wpa_psk->group = 1; + } + + /* Select group cipher based on the enabled pairwise cipher suites */ + pairwise = 0; + if (bss->wpa & 1) + pairwise |= bss->wpa_pairwise; + if (bss->wpa & 2) { + if (bss->rsn_pairwise == 0) + bss->rsn_pairwise = bss->wpa_pairwise; + pairwise |= bss->rsn_pairwise; + } + if (pairwise & WPA_CIPHER_TKIP) + bss->wpa_group = WPA_CIPHER_TKIP; + else + bss->wpa_group = WPA_CIPHER_CCMP; + + 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) { + bss->ssid.security_policy = SECURITY_IEEE_802_1X; + bss->ssid.wep.default_len = bss->default_wep_key_len; + } else if (bss->ssid.wep.keys_set) + bss->ssid.security_policy = SECURITY_STATIC_WEP; + else + bss->ssid.security_policy = SECURITY_PLAINTEXT; + +#ifdef CONFIG_WPS + /* + * Enable WPS by default, but require user interaction to actually use + * it. Only the internal Registrar is supported. + */ + bss->eap_server = 1; + bss->wps_state = 2; + bss->ap_setup_locked = 2; + if (wpa_s->conf->config_methods) + bss->config_methods = os_strdup(wpa_s->conf->config_methods); + os_memcpy(bss->device_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + if (wpa_s->conf->device_name) { + bss->device_name = os_strdup(wpa_s->conf->device_name); + bss->friendly_name = os_strdup(wpa_s->conf->device_name); + } + if (wpa_s->conf->manufacturer) + bss->manufacturer = os_strdup(wpa_s->conf->manufacturer); + if (wpa_s->conf->model_name) + bss->model_name = os_strdup(wpa_s->conf->model_name); + if (wpa_s->conf->model_number) + bss->model_number = os_strdup(wpa_s->conf->model_number); + if (wpa_s->conf->serial_number) + bss->serial_number = os_strdup(wpa_s->conf->serial_number); + if (is_nil_uuid(wpa_s->conf->uuid)) + os_memcpy(bss->uuid, wpa_s->wps->uuid, WPS_UUID_LEN); + else + os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); + os_memcpy(bss->os_version, wpa_s->conf->os_version, 4); +#endif /* CONFIG_WPS */ + + if (wpa_s->max_stations && + wpa_s->max_stations < wpa_s->conf->max_num_sta) + bss->max_num_sta = wpa_s->max_stations; + else + bss->max_num_sta = wpa_s->conf->max_num_sta; + + bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack; + + return 0; +} + + +static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq) +{ +#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) + 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); +#endif /* CONFIG_P2P */ +} + + +static void ap_wps_event_cb(void *ctx, enum wps_event event, + union wps_event_data *data) +{ +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + + if (event == WPS_EV_FAIL && wpa_s->parent && wpa_s->parent != wpa_s && + wpa_s == wpa_s->global->p2p_group_formation) { + struct wps_event_fail *fail = &data->fail; + + /* + * src/ap/wps_hostapd.c has already sent this on the main + * interface, so only send on the parent interface here if + * needed. + */ + wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + "msg=%d config_error=%d", + fail->msg, fail->config_error); + } +#endif /* CONFIG_P2P */ +} + + +static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr, + int authorized) +{ + wpas_notify_sta_authorized(ctx, mac_addr, authorized); +} + + +static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq) +{ +#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) + 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); +#endif /* CONFIG_P2P */ + return 0; +} + + +static int ap_probe_req_rx(void *ctx, const u8 *addr, const u8 *ie, + size_t ie_len) +{ +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + return wpas_p2p_probe_req_rx(wpa_s, addr, ie, ie_len); +#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 */ +} + + +static void wpas_ap_configured_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + + if (wpa_s->ap_configured_cb) + wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx, + wpa_s->ap_configured_cb_data); +} + + +int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_driver_associate_params params; + struct hostapd_iface *hapd_iface; + struct hostapd_config *conf; + size_t i; + + if (ssid->ssid == NULL || ssid->ssid_len == 0) { + wpa_printf(MSG_ERROR, "No SSID configured for AP mode"); + return -1; + } + + wpa_supplicant_ap_deinit(wpa_s); + + wpa_printf(MSG_DEBUG, "Setting up AP (SSID='%s')", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + + os_memset(¶ms, 0, sizeof(params)); + 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; + } + params.freq = ssid->frequency; + + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) + 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); + + if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) + wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; + else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP) + wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; + else if (ssid->pairwise_cipher & WPA_CIPHER_NONE) + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + else { + wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise " + "cipher."); + return -1; + } + params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher); + params.group_suite = params.pairwise_suite; + +#ifdef CONFIG_P2P + if (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) + params.p2p = 1; + wpa_drv_set_intra_bss(wpa_s, wpa_s->conf->p2p_intra_bss); +#endif /* CONFIG_P2P */ + + if (wpa_s->parent->set_ap_uapsd) + params.uapsd = wpa_s->parent->ap_uapsd; + else + params.uapsd = -1; + + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { + wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); + return -1; + } + + wpa_s->ap_iface = hapd_iface = os_zalloc(sizeof(*wpa_s->ap_iface)); + if (hapd_iface == NULL) + return -1; + hapd_iface->owner = wpa_s; + + wpa_s->ap_iface->conf = conf = hostapd_config_defaults(); + if (conf == NULL) { + wpa_supplicant_ap_deinit(wpa_s); + return -1; + } + + if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) { + wpa_printf(MSG_ERROR, "Failed to create AP configuration"); + wpa_supplicant_ap_deinit(wpa_s); + return -1; + } + +#ifdef CONFIG_P2P + if (ssid->mode == WPAS_MODE_P2P_GO) + 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 | + P2P_GROUP_FORMATION; +#endif /* CONFIG_P2P */ + + hapd_iface->num_bss = conf->num_bss; + hapd_iface->bss = os_zalloc(conf->num_bss * + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) { + wpa_supplicant_ap_deinit(wpa_s); + return -1; + } + + for (i = 0; i < conf->num_bss; i++) { + hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, + &conf->bss[i]); + if (hapd_iface->bss[i] == NULL) { + wpa_supplicant_ap_deinit(wpa_s); + return -1; + } + + hapd_iface->bss[i]->msg_ctx = wpa_s; + hapd_iface->bss[i]->public_action_cb = ap_public_action_rx; + hapd_iface->bss[i]->public_action_cb_ctx = wpa_s; + hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx; + hapd_iface->bss[i]->vendor_action_cb_ctx = wpa_s; + hostapd_register_probereq_cb(hapd_iface->bss[i], + ap_probe_req_rx, wpa_s); + hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb; + hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s; + hapd_iface->bss[i]->wps_event_cb = ap_wps_event_cb; + hapd_iface->bss[i]->wps_event_cb_ctx = wpa_s; + hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb; + hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s; +#ifdef CONFIG_P2P + hapd_iface->bss[i]->p2p = wpa_s->global->p2p; + hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init( + wpa_s, ssid->p2p_persistent_group, + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION); +#endif /* CONFIG_P2P */ + hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb; + hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s; + } + + os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN); + hapd_iface->bss[0]->driver = wpa_s->driver; + hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv; + + wpa_s->current_ssid = ssid; + os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN); + wpa_s->assoc_freq = ssid->frequency; + + if (hostapd_setup_interface(wpa_s->ap_iface)) { + wpa_printf(MSG_ERROR, "Failed to initialize AP interface"); + wpa_supplicant_ap_deinit(wpa_s); + return -1; + } + + return 0; +} + + +void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WPS + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); +#endif /* CONFIG_WPS */ + + if (wpa_s->ap_iface == NULL) + return; + + wpa_s->current_ssid = 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 */ + hostapd_interface_deinit(wpa_s->ap_iface); + hostapd_interface_free(wpa_s->ap_iface); + wpa_s->ap_iface = NULL; + wpa_drv_deinit_ap(wpa_s); +} + + +void ap_tx_status(void *ctx, const u8 *addr, + const u8 *buf, size_t len, int ack) +{ +#ifdef NEED_AP_MLME + struct wpa_supplicant *wpa_s = ctx; + hostapd_tx_status(wpa_s->ap_iface->bss[0], addr, buf, len, ack); +#endif /* NEED_AP_MLME */ +} + + +void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len) +{ +#ifdef NEED_AP_MLME + struct wpa_supplicant *wpa_s = ctx; + const struct ieee80211_hdr *hdr = + (const struct ieee80211_hdr *) frame; + u16 fc = le_to_host16(hdr->frame_control); + ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], hdr->addr2, + (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == + (WLAN_FC_TODS | WLAN_FC_FROMDS)); +#endif /* NEED_AP_MLME */ +} + + +void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt) +{ +#ifdef NEED_AP_MLME + struct wpa_supplicant *wpa_s = ctx; + struct hostapd_frame_info fi; + os_memset(&fi, 0, sizeof(fi)); + fi.datarate = rx_mgmt->datarate; + fi.ssi_signal = rx_mgmt->ssi_signal; + ieee802_11_mgmt(wpa_s->ap_iface->bss[0], rx_mgmt->frame, + rx_mgmt->frame_len, &fi); +#endif /* NEED_AP_MLME */ +} + + +void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok) +{ +#ifdef NEED_AP_MLME + struct wpa_supplicant *wpa_s = ctx; + ieee802_11_mgmt_cb(wpa_s->ap_iface->bss[0], buf, len, stype, ok); +#endif /* NEED_AP_MLME */ +} + + +void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s, + const u8 *src_addr, const u8 *buf, size_t len) +{ + ieee802_1x_receive(wpa_s->ap_iface->bss[0], src_addr, buf, len); +} + + +#ifdef CONFIG_WPS + +int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *p2p_dev_addr) +{ + if (!wpa_s->ap_iface) + return -1; + return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0], + p2p_dev_addr); +} + + +static int wpa_supplicant_ap_wps_sta_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (sta && (sta->flags & WLAN_STA_WPS)) { + ap_sta_deauthenticate(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR, + __func__, MAC2STR(sta->addr)); + return 1; + } + + return 0; +} + + +int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s) +{ + struct wps_registrar *reg; + int reg_sel = 0, wps_sta = 0; + + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps) + return -1; + + reg = wpa_s->ap_iface->bss[0]->wps->registrar; + reg_sel = wps_registrar_wps_cancel(reg); + wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0], + wpa_supplicant_ap_wps_sta_cancel, NULL); + + if (!reg_sel && !wps_sta) { + wpa_printf(MSG_DEBUG, "No WPS operation in progress at this " + "time"); + return -1; + } + + /* + * There are 2 cases to return wps cancel as success: + * 1. When wps cancel was initiated but no connection has been + * established with client yet. + * 2. Client is in the middle of exchanging WPS messages. + */ + + return 0; +} + + +int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin, char *buf, size_t buflen) +{ + int ret, ret_len = 0; + + if (!wpa_s->ap_iface) + return -1; + + if (pin == NULL) { + unsigned int rpin = wps_generate_pin(); + ret_len = os_snprintf(buf, buflen, "%d", rpin); + pin = buf; + } else + ret_len = os_snprintf(buf, buflen, "%s", pin); + + ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin, + 0); + if (ret) + return -1; + return ret_len; +} + + +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_data; + wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); + wpas_wps_ap_pin_disable(wpa_s); +} + + +static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + hapd = wpa_s->ap_iface->bss[0]; + wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); + hapd->ap_pin_failures = 0; + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); + if (timeout > 0) + eloop_register_timeout(timeout, 0, + wpas_wps_ap_pin_timeout, wpa_s, NULL); +} + + +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + hapd = wpa_s->ap_iface->bss[0]; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); +} + + +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout) +{ + struct hostapd_data *hapd; + unsigned int pin; + char pin_txt[9]; + + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + pin = wps_generate_pin(); + os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin); + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin_txt); + if (hapd->conf->ap_pin == NULL) + return NULL; + wpas_wps_ap_pin_enable(wpa_s, timeout); + + return hapd->conf->ap_pin; +} + + +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + return hapd->conf->ap_pin; +} + + +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, + int timeout) +{ + struct hostapd_data *hapd; + char pin_txt[9]; + int ret; + + if (wpa_s->ap_iface == NULL) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin); + if (ret < 0 || ret >= (int) sizeof(pin_txt)) + return -1; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin_txt); + if (hapd->conf->ap_pin == NULL) + return -1; + wpas_wps_ap_pin_enable(wpa_s, timeout); + + return 0; +} + + +void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + hapd = wpa_s->ap_iface->bss[0]; + + /* + * Registrar failed to prove its knowledge of the AP PIN. Disable AP + * PIN if this happens multiple times to slow down brute force attacks. + */ + hapd->ap_pin_failures++; + wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u", + hapd->ap_pin_failures); + if (hapd->ap_pin_failures < 3) + return; + + wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN"); + hapd->ap_pin_failures = 0; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; +} + +#endif /* CONFIG_WPS */ + + +#ifdef CONFIG_CTRL_IFACE + +int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + if (wpa_s->ap_iface == NULL) + return -1; + return hostapd_ctrl_iface_sta_first(wpa_s->ap_iface->bss[0], + buf, buflen); +} + + +int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr, + char *buf, size_t buflen) +{ + if (wpa_s->ap_iface == NULL) + return -1; + return hostapd_ctrl_iface_sta(wpa_s->ap_iface->bss[0], txtaddr, + buf, buflen); +} + + +int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr, + char *buf, size_t buflen) +{ + if (wpa_s->ap_iface == NULL) + return -1; + return hostapd_ctrl_iface_sta_next(wpa_s->ap_iface->bss[0], txtaddr, + buf, buflen); +} + + +int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen, int verbose) +{ + char *pos = buf, *end = buf + buflen; + int ret; + struct hostapd_bss_config *conf; + + if (wpa_s->ap_iface == NULL) + return -1; + + conf = wpa_s->ap_iface->bss[0]->conf; + if (conf->wpa == 0) + return 0; + + ret = os_snprintf(pos, end - pos, + "pairwise_cipher=%s\n" + "group_cipher=%s\n" + "key_mgmt=%s\n", + wpa_cipher_txt(conf->rsn_pairwise), + wpa_cipher_txt(conf->wpa_group), + wpa_key_mgmt_txt(conf->wpa_key_mgmt, + conf->wpa)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + return pos - buf; +} + +#endif /* CONFIG_CTRL_IFACE */ + + +int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s) +{ + struct hostapd_iface *iface = wpa_s->ap_iface; + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_data *hapd; + + if (ssid == NULL || wpa_s->ap_iface == NULL) + return -1; + +#ifdef CONFIG_P2P + if (ssid->mode == WPAS_MODE_P2P_GO) + 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 | + P2P_GROUP_FORMATION; +#endif /* CONFIG_P2P */ + + ieee802_11_set_beacons(iface); + hapd = iface->bss[0]; + hostapd_set_ap_wps_ie(hapd); + + return 0; +} + + +int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + struct hostapd_data *hapd; + struct hostapd_bss_config *conf; + + if (!wpa_s->ap_iface) + return -1; + + if (addr) + wpa_printf(MSG_DEBUG, "AP: Set MAC address filter: " MACSTR, + MAC2STR(addr)); + else + wpa_printf(MSG_DEBUG, "AP: Clear MAC address filter"); + + hapd = wpa_s->ap_iface->bss[0]; + conf = hapd->conf; + + os_free(conf->accept_mac); + conf->accept_mac = NULL; + conf->num_accept_mac = 0; + os_free(conf->deny_mac); + conf->deny_mac = NULL; + conf->num_deny_mac = 0; + + if (addr == NULL) { + conf->macaddr_acl = ACCEPT_UNLESS_DENIED; + return 0; + } + + conf->macaddr_acl = DENY_UNLESS_ACCEPTED; + conf->accept_mac = os_zalloc(sizeof(struct mac_acl_entry)); + if (conf->accept_mac == NULL) + return -1; + os_memcpy(conf->accept_mac[0].addr, addr, ETH_ALEN); + conf->num_accept_mac = 1; + + return 0; +} diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h new file mode 100644 index 0000000..b913be3 --- /dev/null +++ b/wpa_supplicant/ap.h @@ -0,0 +1,52 @@ +/* + * WPA Supplicant - Basic AP mode support routines + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, Atheros Communications + * + * 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 AP_H +#define AP_H + +int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s); +void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s, + const u8 *src_addr, const u8 *buf, size_t len); +int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *p2p_dev_addr); +int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin, char *buf, size_t buflen); +int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s); +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s); +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout); +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s); +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, + int timeout); +int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen); +int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr, + char *buf, size_t buflen); +int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr, + char *buf, size_t buflen); +int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen, int verbose); +void ap_tx_status(void *ctx, const u8 *addr, + const u8 *buf, size_t len, int ack); +void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len); +void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt); +void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok); +int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s); +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); + +#endif /* AP_H */ diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c new file mode 100644 index 0000000..5661830 --- /dev/null +++ b/wpa_supplicant/bgscan.c @@ -0,0 +1,123 @@ +/* + * WPA Supplicant - background scan and roaming interface + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "config_ssid.h" +#include "bgscan.h" + +#ifdef CONFIG_BGSCAN_SIMPLE +extern const struct bgscan_ops bgscan_simple_ops; +#endif /* CONFIG_BGSCAN_SIMPLE */ +#ifdef CONFIG_BGSCAN_LEARN +extern const struct bgscan_ops bgscan_learn_ops; +#endif /* CONFIG_BGSCAN_LEARN */ + +static const struct bgscan_ops * bgscan_modules[] = { +#ifdef CONFIG_BGSCAN_SIMPLE + &bgscan_simple_ops, +#endif /* CONFIG_BGSCAN_SIMPLE */ +#ifdef CONFIG_BGSCAN_LEARN + &bgscan_learn_ops, +#endif /* CONFIG_BGSCAN_LEARN */ + NULL +}; + + +int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + const char *name = ssid->bgscan; + const char *params; + size_t nlen; + int i; + const struct bgscan_ops *ops = NULL; + + bgscan_deinit(wpa_s); + if (name == NULL) + return 0; + + params = os_strchr(name, ':'); + if (params == NULL) { + params = ""; + nlen = os_strlen(name); + } else { + nlen = params - name; + params++; + } + + for (i = 0; bgscan_modules[i]; i++) { + if (os_strncmp(name, bgscan_modules[i]->name, nlen) == 0) { + ops = bgscan_modules[i]; + break; + } + } + + if (ops == NULL) { + wpa_printf(MSG_ERROR, "bgscan: Could not find module " + "matching the parameter '%s'", name); + return -1; + } + + wpa_s->bgscan_priv = ops->init(wpa_s, params, ssid); + if (wpa_s->bgscan_priv == NULL) + return -1; + wpa_s->bgscan = ops; + wpa_printf(MSG_DEBUG, "bgscan: Initialized module '%s' with " + "parameters '%s'", ops->name, params); + + return 0; +} + + +void bgscan_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan && wpa_s->bgscan_priv) { + wpa_printf(MSG_DEBUG, "bgscan: Deinitializing module '%s'", + wpa_s->bgscan->name); + wpa_s->bgscan->deinit(wpa_s->bgscan_priv); + wpa_s->bgscan = NULL; + wpa_s->bgscan_priv = NULL; + } +} + + +int bgscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + if (wpa_s->bgscan && wpa_s->bgscan_priv) + return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv, + scan_res); + return 0; +} + + +void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan && wpa_s->bgscan_priv) + wpa_s->bgscan->notify_beacon_loss(wpa_s->bgscan_priv); +} + + +void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above, + int current_signal, int current_noise, + int current_txrate) +{ + if (wpa_s->bgscan && wpa_s->bgscan_priv) + wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above, + current_signal, + current_noise, + current_txrate); +} diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h new file mode 100644 index 0000000..ae94a48 --- /dev/null +++ b/wpa_supplicant/bgscan.h @@ -0,0 +1,78 @@ +/* + * WPA Supplicant - background scan and roaming interface + * Copyright (c) 2009-2010, 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. + */ + +#ifndef BGSCAN_H +#define BGSCAN_H + +struct wpa_supplicant; +struct wpa_ssid; + +struct bgscan_ops { + const char *name; + + void * (*init)(struct wpa_supplicant *wpa_s, const char *params, + const struct wpa_ssid *ssid); + void (*deinit)(void *priv); + + int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res); + void (*notify_beacon_loss)(void *priv); + void (*notify_signal_change)(void *priv, int above, + int current_signal, + int current_noise, + int current_txrate); +}; + +#ifdef CONFIG_BGSCAN + +int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void bgscan_deinit(struct wpa_supplicant *wpa_s); +int bgscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); +void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s); +void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above, + int current_signal, int current_noise, + int current_txrate); + +#else /* CONFIG_BGSCAN */ + +static inline int bgscan_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + return 0; +} + +static inline void bgscan_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + return 0; +} + +static inline void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s) +{ +} + +static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, + int above, int current_signal, + int current_noise, + int current_txrate) +{ +} + +#endif /* CONFIG_BGSCAN */ + +#endif /* BGSCAN_H */ diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c new file mode 100644 index 0000000..ee79511 --- /dev/null +++ b/wpa_supplicant/bgscan_learn.c @@ -0,0 +1,614 @@ +/* + * WPA Supplicant - background scan and roaming module: learn + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "list.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" +#include "config_ssid.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "scan.h" +#include "bgscan.h" + +struct bgscan_learn_bss { + struct dl_list list; + u8 bssid[ETH_ALEN]; + int freq; + u8 *neigh; /* num_neigh * ETH_ALEN buffer */ + size_t num_neigh; +}; + +struct bgscan_learn_data { + struct wpa_supplicant *wpa_s; + const struct wpa_ssid *ssid; + int scan_interval; + int signal_threshold; + int short_interval; /* use if signal < threshold */ + int long_interval; /* use if signal > threshold */ + struct os_time last_bgscan; + char *fname; + struct dl_list bss; + int *supp_freqs; + int probe_idx; +}; + + +static void bss_free(struct bgscan_learn_bss *bss) +{ + os_free(bss->neigh); + os_free(bss); +} + + +static int bssid_in_array(u8 *array, size_t array_len, const u8 *bssid) +{ + size_t i; + + if (array == NULL || array_len == 0) + return 0; + + for (i = 0; i < array_len; i++) { + if (os_memcmp(array + i * ETH_ALEN, bssid, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss, + const u8 *bssid) +{ + u8 *n; + + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + return; + if (bssid_in_array(bss->neigh, bss->num_neigh, bssid)) + return; + + n = os_realloc(bss->neigh, (bss->num_neigh + 1) * ETH_ALEN); + if (n == NULL) + return; + + os_memcpy(n + bss->num_neigh * ETH_ALEN, bssid, ETH_ALEN); + bss->neigh = n; + bss->num_neigh++; +} + + +static struct bgscan_learn_bss * bgscan_learn_get_bss( + struct bgscan_learn_data *data, const u8 *bssid) +{ + struct bgscan_learn_bss *bss; + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + return bss; + } + return NULL; +} + + +static int bgscan_learn_load(struct bgscan_learn_data *data) +{ + FILE *f; + char buf[128]; + struct bgscan_learn_bss *bss; + + if (data->fname == NULL) + return 0; + + f = fopen(data->fname, "r"); + if (f == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "bgscan learn: Loading data from %s", + data->fname); + + if (fgets(buf, sizeof(buf), f) == NULL || + os_strncmp(buf, "wpa_supplicant-bgscan-learn\n", 28) != 0) { + wpa_printf(MSG_INFO, "bgscan learn: Invalid data file %s", + data->fname); + fclose(f); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + if (os_strncmp(buf, "BSS ", 4) == 0) { + bss = os_zalloc(sizeof(*bss)); + if (!bss) + continue; + if (hwaddr_aton(buf + 4, bss->bssid) < 0) { + bss_free(bss); + continue; + } + bss->freq = atoi(buf + 4 + 18); + dl_list_add(&data->bss, &bss->list); + wpa_printf(MSG_DEBUG, "bgscan learn: Loaded BSS " + "entry: " MACSTR " freq=%d", + MAC2STR(bss->bssid), bss->freq); + } + + if (os_strncmp(buf, "NEIGHBOR ", 9) == 0) { + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(buf + 9, addr) < 0) + continue; + bss = bgscan_learn_get_bss(data, addr); + if (bss == NULL) + continue; + if (hwaddr_aton(buf + 9 + 18, addr) < 0) + continue; + + bgscan_learn_add_neighbor(bss, addr); + } + } + + fclose(f); + return 0; +} + + +static void bgscan_learn_save(struct bgscan_learn_data *data) +{ + FILE *f; + struct bgscan_learn_bss *bss; + + if (data->fname == NULL) + return; + + wpa_printf(MSG_DEBUG, "bgscan learn: Saving data to %s", + data->fname); + + f = fopen(data->fname, "w"); + if (f == NULL) + return; + fprintf(f, "wpa_supplicant-bgscan-learn\n"); + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + fprintf(f, "BSS " MACSTR " %d\n", + MAC2STR(bss->bssid), bss->freq); + } + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + size_t i; + for (i = 0; i < bss->num_neigh; i++) { + fprintf(f, "NEIGHBOR " MACSTR " " MACSTR "\n", + MAC2STR(bss->bssid), + MAC2STR(bss->neigh + i * ETH_ALEN)); + } + } + + fclose(f); +} + + +static int in_array(int *array, int val) +{ + int i; + + if (array == NULL) + return 0; + + for (i = 0; array[i]; i++) { + if (array[i] == val) + return 1; + } + + return 0; +} + + +static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data, + size_t *count) +{ + struct bgscan_learn_bss *bss; + int *freqs = NULL, *n; + + *count = 0; + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + if (in_array(freqs, bss->freq)) + continue; + n = os_realloc(freqs, (*count + 2) * sizeof(int)); + if (n == NULL) + return freqs; + freqs = n; + freqs[*count] = bss->freq; + (*count)++; + freqs[*count] = 0; + } + + return freqs; +} + + +static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data, + int *freqs, size_t count) +{ + int idx, *n; + + if (data->supp_freqs == NULL) + return freqs; + + idx = data->probe_idx + 1; + while (idx != data->probe_idx) { + if (data->supp_freqs[idx] == 0) + idx = 0; + 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; + n = os_realloc(freqs, (count + 2) * sizeof(int)); + if (n == NULL) + return freqs; + freqs = n; + freqs[count] = data->supp_freqs[idx]; + count++; + freqs[count] = 0; + break; + } + + idx++; + } + + return freqs; +} + + +static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct bgscan_learn_data *data = eloop_ctx; + struct wpa_supplicant *wpa_s = data->wpa_s; + struct wpa_driver_scan_params params; + int *freqs = NULL; + size_t count, i; + char msg[100], *pos; + + os_memset(¶ms, 0, sizeof(params)); + params.num_ssids = 1; + params.ssids[0].ssid = data->ssid->ssid; + params.ssids[0].ssid_len = data->ssid->ssid_len; + if (data->ssid->scan_freq) + params.freqs = data->ssid->scan_freq; + else { + freqs = bgscan_learn_get_freqs(data, &count); + wpa_printf(MSG_DEBUG, "bgscan learn: BSSes in this ESS have " + "been seen on %u channels", (unsigned int) count); + freqs = bgscan_learn_get_probe_freq(data, freqs, count); + + msg[0] = '\0'; + pos = msg; + for (i = 0; freqs && freqs[i]; i++) { + int ret; + ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d", + freqs[i]); + if (ret < 0 || ret >= msg + sizeof(msg) - pos) + break; + pos += ret; + } + pos[0] = '\0'; + wpa_printf(MSG_DEBUG, "bgscan learn: Scanning frequencies:%s", + msg); + params.freqs = freqs; + } + + wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan"); + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { + wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan"); + eloop_register_timeout(data->scan_interval, 0, + bgscan_learn_timeout, data, NULL); + } else + os_get_time(&data->last_bgscan); + os_free(freqs); +} + + +static int bgscan_learn_get_params(struct bgscan_learn_data *data, + const char *params) +{ + const char *pos; + + if (params == NULL) + return 0; + + data->short_interval = atoi(params); + + pos = os_strchr(params, ':'); + if (pos == NULL) + return 0; + pos++; + data->signal_threshold = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval " + "for high signal"); + return -1; + } + pos++; + data->long_interval = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos) { + pos++; + data->fname = os_strdup(pos); + } + + return 0; +} + + +static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s) +{ + struct hostapd_hw_modes *modes; + u16 num_modes, flags; + int i, j, *freqs = NULL, *n; + size_t count = 0; + + modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, &flags); + if (!modes) + return NULL; + + for (i = 0; i < num_modes; i++) { + for (j = 0; j < modes[i].num_channels; j++) { + if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + n = os_realloc(freqs, (count + 2) * sizeof(int)); + if (!n) + continue; + + freqs = n; + freqs[count] = modes[i].channels[j].freq; + count++; + freqs[count] = 0; + } + os_free(modes[i].channels); + os_free(modes[i].rates); + } + os_free(modes); + + return freqs; +} + + +static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, + const char *params, + const struct wpa_ssid *ssid) +{ + struct bgscan_learn_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + dl_list_init(&data->bss); + data->wpa_s = wpa_s; + data->ssid = ssid; + if (bgscan_learn_get_params(data, params) < 0) { + os_free(data->fname); + os_free(data); + return NULL; + } + if (data->short_interval <= 0) + data->short_interval = 30; + if (data->long_interval <= 0) + data->long_interval = 30; + + if (bgscan_learn_load(data) < 0) { + os_free(data->fname); + os_free(data); + return NULL; + } + + wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d " + "Short bgscan interval %d Long bgscan interval %d", + data->signal_threshold, data->short_interval, + data->long_interval); + + if (data->signal_threshold && + wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) { + wpa_printf(MSG_ERROR, "bgscan learn: Failed to enable " + "signal strength monitoring"); + } + + data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s); + data->scan_interval = data->short_interval; + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, + data, NULL); + + /* + * This function is called immediately after an association, so it is + * reasonable to assume that a scan was completed recently. This makes + * us skip an immediate new scan in cases where the current signal + * level is below the bgscan threshold. + */ + os_get_time(&data->last_bgscan); + + return data; +} + + +static void bgscan_learn_deinit(void *priv) +{ + struct bgscan_learn_data *data = priv; + struct bgscan_learn_bss *bss, *n; + + bgscan_learn_save(data); + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + if (data->signal_threshold) + wpa_drv_signal_monitor(data->wpa_s, 0, 0); + os_free(data->fname); + dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss, + list) { + dl_list_del(&bss->list); + bss_free(bss); + } + os_free(data->supp_freqs); + os_free(data); +} + + +static int bgscan_learn_bss_match(struct bgscan_learn_data *data, + struct wpa_scan_res *bss) +{ + const u8 *ie; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + if (ie == NULL) + return 0; + + if (data->ssid->ssid_len != ie[1] || + os_memcmp(data->ssid->ssid, ie + 2, ie[1]) != 0) + return 0; /* SSID mismatch */ + + return 1; +} + + +static int bgscan_learn_notify_scan(void *priv, + struct wpa_scan_results *scan_res) +{ + struct bgscan_learn_data *data = priv; + size_t i, j; +#define MAX_BSS 50 + u8 bssid[MAX_BSS * ETH_ALEN]; + size_t num_bssid = 0; + + wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification"); + + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, + data, NULL); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *res = scan_res->res[i]; + if (!bgscan_learn_bss_match(data, res)) + continue; + + if (num_bssid < MAX_BSS) { + os_memcpy(bssid + num_bssid * ETH_ALEN, res->bssid, + ETH_ALEN); + num_bssid++; + } + } + wpa_printf(MSG_DEBUG, "bgscan learn: %u matching BSSes in scan " + "results", (unsigned int) num_bssid); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *res = scan_res->res[i]; + struct bgscan_learn_bss *bss; + + if (!bgscan_learn_bss_match(data, res)) + continue; + + bss = bgscan_learn_get_bss(data, res->bssid); + if (bss && bss->freq != res->freq) { + wpa_printf(MSG_DEBUG, "bgscan learn: Update BSS " + MACSTR " freq %d -> %d", + MAC2STR(res->bssid), bss->freq, res->freq); + bss->freq = res->freq; + } else if (!bss) { + wpa_printf(MSG_DEBUG, "bgscan learn: Add BSS " MACSTR + " freq=%d", MAC2STR(res->bssid), res->freq); + bss = os_zalloc(sizeof(*bss)); + if (!bss) + continue; + os_memcpy(bss->bssid, res->bssid, ETH_ALEN); + bss->freq = res->freq; + dl_list_add(&data->bss, &bss->list); + } + + for (j = 0; j < num_bssid; j++) { + u8 *addr = bssid + j * ETH_ALEN; + bgscan_learn_add_neighbor(bss, addr); + } + } + + /* + * A more advanced bgscan could process scan results internally, select + * the BSS and request roam if needed. This sample uses the existing + * BSS/ESS selection routine. Change this to return 1 if selection is + * done inside the bgscan module. + */ + + return 0; +} + + +static void bgscan_learn_notify_beacon_loss(void *priv) +{ + wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss"); + /* TODO: speed up background scanning */ +} + + +static void bgscan_learn_notify_signal_change(void *priv, int above, + int current_signal, + int current_noise, + int current_txrate) +{ + struct bgscan_learn_data *data = priv; + int scan = 0; + struct os_time now; + + if (data->short_interval == data->long_interval || + data->signal_threshold == 0) + return; + + wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed " + "(above=%d current_signal=%d current_noise=%d " + "current_txrate=%d)", above, current_signal, + current_noise, current_txrate); + if (data->scan_interval == data->long_interval && !above) { + wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan " + "interval"); + data->scan_interval = data->short_interval; + os_get_time(&now); + if (now.sec > data->last_bgscan.sec + 1) + scan = 1; + } else if (data->scan_interval == data->short_interval && above) { + wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan " + "interval"); + data->scan_interval = data->long_interval; + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, + bgscan_learn_timeout, data, NULL); + } else if (!above) { + /* + * Signal dropped further 4 dB. Request a new scan if we have + * not yet scanned in a while. + */ + os_get_time(&now); + if (now.sec > data->last_bgscan.sec + 10) + scan = 1; + } + + if (scan) { + wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan"); + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(0, 0, bgscan_learn_timeout, data, NULL); + } +} + + +const struct bgscan_ops bgscan_learn_ops = { + .name = "learn", + .init = bgscan_learn_init, + .deinit = bgscan_learn_deinit, + .notify_scan = bgscan_learn_notify_scan, + .notify_beacon_loss = bgscan_learn_notify_beacon_loss, + .notify_signal_change = bgscan_learn_notify_signal_change, +}; diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c new file mode 100644 index 0000000..eedc961 --- /dev/null +++ b/wpa_supplicant/bgscan_simple.c @@ -0,0 +1,274 @@ +/* + * WPA Supplicant - background scan and roaming module: simple + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "drivers/driver.h" +#include "config_ssid.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "scan.h" +#include "bgscan.h" + +struct bgscan_simple_data { + struct wpa_supplicant *wpa_s; + const struct wpa_ssid *ssid; + int scan_interval; + int signal_threshold; + int short_scan_count; /* counter for scans using short scan interval */ + int short_interval; /* use if signal < threshold */ + int long_interval; /* use if signal > threshold */ + struct os_time last_bgscan; +}; + + +static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct bgscan_simple_data *data = eloop_ctx; + struct wpa_supplicant *wpa_s = data->wpa_s; + struct wpa_driver_scan_params params; + + os_memset(¶ms, 0, sizeof(params)); + params.num_ssids = 1; + params.ssids[0].ssid = data->ssid->ssid; + params.ssids[0].ssid_len = data->ssid->ssid_len; + params.freqs = data->ssid->scan_freq; + + /* + * A more advanced bgscan module would learn about most like channels + * over time and request scans only for some channels (probing others + * every now and then) to reduce effect on the data connection. + */ + + wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan"); + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { + wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan"); + eloop_register_timeout(data->scan_interval, 0, + bgscan_simple_timeout, data, NULL); + } else { + if (data->scan_interval == data->short_interval) { + data->short_scan_count++; + /* + * Spend at most the duration of a long scan interval + * scanning at the short scan interval. After that, + * revert to the long scan interval. + */ + if (data->short_scan_count > + data->long_interval / data->short_interval + 1) { + data->scan_interval = data->long_interval; + wpa_printf(MSG_DEBUG, "bgscan simple: Backing " + "off to long scan interval"); + } + } + os_get_time(&data->last_bgscan); + } +} + + +static int bgscan_simple_get_params(struct bgscan_simple_data *data, + const char *params) +{ + const char *pos; + + if (params == NULL) + return 0; + + data->short_interval = atoi(params); + + pos = os_strchr(params, ':'); + if (pos == NULL) + return 0; + pos++; + data->signal_threshold = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "bgscan simple: Missing scan interval " + "for high signal"); + return -1; + } + pos++; + data->long_interval = atoi(pos); + + return 0; +} + + +static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, + const char *params, + const struct wpa_ssid *ssid) +{ + struct bgscan_simple_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->wpa_s = wpa_s; + data->ssid = ssid; + if (bgscan_simple_get_params(data, params) < 0) { + os_free(data); + return NULL; + } + if (data->short_interval <= 0) + data->short_interval = 30; + if (data->long_interval <= 0) + data->long_interval = 30; + + wpa_printf(MSG_DEBUG, "bgscan simple: Signal strength threshold %d " + "Short bgscan interval %d Long bgscan interval %d", + data->signal_threshold, data->short_interval, + data->long_interval); + + if (data->signal_threshold && + wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) { + wpa_printf(MSG_ERROR, "bgscan simple: Failed to enable " + "signal strength monitoring"); + } + + 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; + } + wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d", + data->scan_interval); + eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, + data, NULL); + + /* + * This function is called immediately after an association, so it is + * reasonable to assume that a scan was completed recently. This makes + * us skip an immediate new scan in cases where the current signal + * level is below the bgscan threshold. + */ + os_get_time(&data->last_bgscan); + + return data; +} + + +static void bgscan_simple_deinit(void *priv) +{ + struct bgscan_simple_data *data = priv; + eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); + if (data->signal_threshold) + wpa_drv_signal_monitor(data->wpa_s, 0, 0); + os_free(data); +} + + +static int bgscan_simple_notify_scan(void *priv, + struct wpa_scan_results *scan_res) +{ + struct bgscan_simple_data *data = priv; + + wpa_printf(MSG_DEBUG, "bgscan simple: scan result notification"); + + eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, + data, NULL); + + /* + * A more advanced bgscan could process scan results internally, select + * the BSS and request roam if needed. This sample uses the existing + * BSS/ESS selection routine. Change this to return 1 if selection is + * done inside the bgscan module. + */ + + return 0; +} + + +static void bgscan_simple_notify_beacon_loss(void *priv) +{ + wpa_printf(MSG_DEBUG, "bgscan simple: beacon loss"); + /* TODO: speed up background scanning */ +} + + +static void bgscan_simple_notify_signal_change(void *priv, int above, + int current_signal, + int current_noise, + int current_txrate) +{ + struct bgscan_simple_data *data = priv; + int scan = 0; + struct os_time now; + + if (data->short_interval == data->long_interval || + data->signal_threshold == 0) + return; + + wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed " + "(above=%d current_signal=%d current_noise=%d " + "current_txrate=%d))", above, current_signal, + current_noise, current_txrate); + if (data->scan_interval == data->long_interval && !above) { + wpa_printf(MSG_DEBUG, "bgscan simple: Start using short " + "bgscan interval"); + data->scan_interval = data->short_interval; + data->short_scan_count = 0; + os_get_time(&now); + if (now.sec > data->last_bgscan.sec + 1) + scan = 1; + else if (data->last_bgscan.sec + data->long_interval > + now.sec + data->scan_interval) { + /* + * Restart scan interval timer if currently scheduled + * scan is too far in the future. + */ + eloop_cancel_timeout(bgscan_simple_timeout, data, + NULL); + eloop_register_timeout(data->scan_interval, 0, + bgscan_simple_timeout, data, + NULL); + } + } else if (data->scan_interval == data->short_interval && above) { + wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan " + "interval"); + data->scan_interval = data->long_interval; + eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, + bgscan_simple_timeout, data, NULL); + } else if (!above) { + /* + * Signal dropped further 4 dB. Request a new scan if we have + * not yet scanned in a while. + */ + os_get_time(&now); + if (now.sec > data->last_bgscan.sec + 10) + scan = 1; + } + + if (scan) { + wpa_printf(MSG_DEBUG, "bgscan simple: Trigger immediate scan"); + eloop_cancel_timeout(bgscan_simple_timeout, data, NULL); + eloop_register_timeout(0, 0, bgscan_simple_timeout, data, + NULL); + } +} + + +const struct bgscan_ops bgscan_simple_ops = { + .name = "simple", + .init = bgscan_simple_init, + .deinit = bgscan_simple_deinit, + .notify_scan = bgscan_simple_notify_scan, + .notify_beacon_loss = bgscan_simple_notify_beacon_loss, + .notify_signal_change = bgscan_simple_notify_signal_change, +}; diff --git a/wpa_supplicant/blacklist.c b/wpa_supplicant/blacklist.c new file mode 100644 index 0000000..8f12ac9 --- /dev/null +++ b/wpa_supplicant/blacklist.c @@ -0,0 +1,133 @@ +/* + * wpa_supplicant - Temporary BSSID blacklist + * Copyright (c) 2003-2007, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "blacklist.h" + +/** + * wpa_blacklist_get - Get the blacklist entry for a BSSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID + * Returns: Matching blacklist entry for the BSSID or %NULL if not found + */ +struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + struct wpa_blacklist *e; + + e = wpa_s->blacklist; + while (e) { + if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) + return e; + e = e->next; + } + + return NULL; +} + + +/** + * wpa_blacklist_add - Add an BSSID to the blacklist + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID to be added to the blacklist + * Returns: Current blacklist count on success, -1 on failure + * + * This function adds the specified BSSID to the blacklist or increases the + * blacklist count if the BSSID was already listed. It should be called when + * an association attempt fails either due to the selected BSS rejecting + * association or due to timeout. + * + * This blacklist is used to force %wpa_supplicant to go through all available + * BSSes before retrying to associate with an BSS that rejected or timed out + * association. It does not prevent the listed BSS from being used; it only + * changes the order in which they are tried. + */ +int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e; + + e = wpa_blacklist_get(wpa_s, bssid); + if (e) { + e->count++; + wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count " + "incremented to %d", + MAC2STR(bssid), e->count); + return e->count; + } + + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return -1; + os_memcpy(e->bssid, bssid, ETH_ALEN); + e->count = 1; + e->next = wpa_s->blacklist; + wpa_s->blacklist = e; + wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist", + MAC2STR(bssid)); + + return e->count; +} + + +/** + * wpa_blacklist_del - Remove an BSSID from the blacklist + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID to be removed from the blacklist + * Returns: 0 on success, -1 on failure + */ +int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e, *prev = NULL; + + e = wpa_s->blacklist; + while (e) { + if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) { + if (prev == NULL) { + wpa_s->blacklist = e->next; + } else { + prev->next = e->next; + } + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " + "blacklist", MAC2STR(bssid)); + os_free(e); + return 0; + } + prev = e; + e = e->next; + } + return -1; +} + + +/** + * wpa_blacklist_clear - Clear the blacklist of all entries + * @wpa_s: Pointer to wpa_supplicant data + */ +void wpa_blacklist_clear(struct wpa_supplicant *wpa_s) +{ + struct wpa_blacklist *e, *prev; + + e = wpa_s->blacklist; + wpa_s->blacklist = NULL; + while (e) { + prev = e; + e = e->next; + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " + "blacklist (clear)", MAC2STR(prev->bssid)); + os_free(prev); + } +} diff --git a/wpa_supplicant/blacklist.h b/wpa_supplicant/blacklist.h new file mode 100644 index 0000000..de280cd --- /dev/null +++ b/wpa_supplicant/blacklist.h @@ -0,0 +1,30 @@ +/* + * wpa_supplicant - Temporary BSSID blacklist + * Copyright (c) 2003-2007, 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. + */ + +#ifndef BLACKLIST_H +#define BLACKLIST_H + +struct wpa_blacklist { + struct wpa_blacklist *next; + u8 bssid[ETH_ALEN]; + int count; +}; + +struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, + const u8 *bssid); +int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid); +void wpa_blacklist_clear(struct wpa_supplicant *wpa_s); + +#endif /* BLACKLIST_H */ diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c new file mode 100644 index 0000000..5b48951 --- /dev/null +++ b/wpa_supplicant/bss.c @@ -0,0 +1,615 @@ +/* + * BSS table + * Copyright (c) 2009-2010, 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "notify.h" +#include "scan.h" +#include "bss.h" + + +/** + * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds + */ +#define WPA_BSS_EXPIRATION_PERIOD 10 + +#define WPA_BSS_FREQ_CHANGED_FLAG BIT(0) +#define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1) +#define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2) +#define WPA_BSS_MODE_CHANGED_FLAG BIT(3) +#define WPA_BSS_WPAIE_CHANGED_FLAG BIT(4) +#define WPA_BSS_RSNIE_CHANGED_FLAG BIT(5) +#define WPA_BSS_WPS_CHANGED_FLAG BIT(6) +#define WPA_BSS_RATES_CHANGED_FLAG BIT(7) +#define WPA_BSS_IES_CHANGED_FLAG BIT(8) + + +static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + dl_list_del(&bss->list); + dl_list_del(&bss->list_id); + wpa_s->num_bss--; + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR + " SSID '%s'", bss->id, MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id); + os_free(bss); +} + + +struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + struct wpa_bss *bss; + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && + bss->ssid_len == ssid_len && + os_memcmp(bss->ssid, ssid, ssid_len) == 0) + return bss; + } + return NULL; +} + + +static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) +{ + os_time_t usec; + + dst->flags = src->flags; + os_memcpy(dst->bssid, src->bssid, ETH_ALEN); + dst->freq = src->freq; + dst->beacon_int = src->beacon_int; + dst->caps = src->caps; + dst->qual = src->qual; + dst->noise = src->noise; + dst->level = src->level; + dst->tsf = src->tsf; + + os_get_time(&dst->last_update); + dst->last_update.sec -= src->age / 1000; + usec = (src->age % 1000) * 1000; + if (dst->last_update.usec < usec) { + dst->last_update.sec--; + dst->last_update.usec += 1000000; + } + dst->last_update.usec -= usec; +} + + +static void wpa_bss_add(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len, + struct wpa_scan_res *res) +{ + struct wpa_bss *bss; + + bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len); + if (bss == NULL) + return; + bss->id = wpa_s->bss_next_id++; + bss->last_update_idx = wpa_s->bss_update_idx; + wpa_bss_copy_res(bss, res); + os_memcpy(bss->ssid, ssid, ssid_len); + bss->ssid_len = ssid_len; + bss->ie_len = res->ie_len; + bss->beacon_ie_len = res->beacon_ie_len; + os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); + + dl_list_add_tail(&wpa_s->bss, &bss->list); + dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); + wpa_s->num_bss++; + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR + " SSID '%s'", + bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); + wpas_notify_bss_added(wpa_s, bss->bssid, bss->id); + if (wpa_s->num_bss > wpa_s->conf->bss_max_count) { + /* Remove the oldest entry */ + wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss, + struct wpa_bss, list)); + } +} + + +static int are_ies_equal(const struct wpa_bss *old, + const struct wpa_scan_res *new, u32 ie) +{ + const u8 *old_ie, *new_ie; + struct wpabuf *old_ie_buff = NULL; + struct wpabuf *new_ie_buff = NULL; + int new_ie_len, old_ie_len, ret, is_multi; + + switch (ie) { + case WPA_IE_VENDOR_TYPE: + old_ie = wpa_bss_get_vendor_ie(old, ie); + new_ie = wpa_scan_get_vendor_ie(new, ie); + is_multi = 0; + break; + case WPS_IE_VENDOR_TYPE: + old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie); + new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie); + is_multi = 1; + break; + case WLAN_EID_RSN: + case WLAN_EID_SUPP_RATES: + case WLAN_EID_EXT_SUPP_RATES: + old_ie = wpa_bss_get_ie(old, ie); + new_ie = wpa_scan_get_ie(new, ie); + is_multi = 0; + break; + default: + wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__); + return 0; + } + + if (is_multi) { + /* in case of multiple IEs stored in buffer */ + old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL; + new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL; + old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0; + new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0; + } else { + /* in case of single IE */ + old_ie_len = old_ie ? old_ie[1] + 2 : 0; + new_ie_len = new_ie ? new_ie[1] + 2 : 0; + } + + if (!old_ie || !new_ie) + ret = !old_ie && !new_ie; + else + ret = (old_ie_len == new_ie_len && + os_memcmp(old_ie, new_ie, old_ie_len) == 0); + + wpabuf_free(old_ie_buff); + wpabuf_free(new_ie_buff); + + return ret; +} + + +static u32 wpa_bss_compare_res(const struct wpa_bss *old, + const struct wpa_scan_res *new) +{ + u32 changes = 0; + int caps_diff = old->caps ^ new->caps; + + if (old->freq != new->freq) + changes |= WPA_BSS_FREQ_CHANGED_FLAG; + + if (old->level != new->level) + changes |= WPA_BSS_SIGNAL_CHANGED_FLAG; + + if (caps_diff & IEEE80211_CAP_PRIVACY) + changes |= WPA_BSS_PRIVACY_CHANGED_FLAG; + + if (caps_diff & IEEE80211_CAP_IBSS) + changes |= WPA_BSS_MODE_CHANGED_FLAG; + + if (old->ie_len == new->ie_len && + os_memcmp(old + 1, new + 1, old->ie_len) == 0) + return changes; + changes |= WPA_BSS_IES_CHANGED_FLAG; + + if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE)) + changes |= WPA_BSS_WPAIE_CHANGED_FLAG; + + if (!are_ies_equal(old, new, WLAN_EID_RSN)) + changes |= WPA_BSS_RSNIE_CHANGED_FLAG; + + if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE)) + changes |= WPA_BSS_WPS_CHANGED_FLAG; + + if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) || + !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES)) + changes |= WPA_BSS_RATES_CHANGED_FLAG; + + return changes; +} + + +static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, + const struct wpa_bss *bss) +{ + if (changes & WPA_BSS_FREQ_CHANGED_FLAG) + wpas_notify_bss_freq_changed(wpa_s, bss->id); + + if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG) + wpas_notify_bss_signal_changed(wpa_s, bss->id); + + if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG) + wpas_notify_bss_privacy_changed(wpa_s, bss->id); + + if (changes & WPA_BSS_MODE_CHANGED_FLAG) + wpas_notify_bss_mode_changed(wpa_s, bss->id); + + if (changes & WPA_BSS_WPAIE_CHANGED_FLAG) + wpas_notify_bss_wpaie_changed(wpa_s, bss->id); + + if (changes & WPA_BSS_RSNIE_CHANGED_FLAG) + wpas_notify_bss_rsnie_changed(wpa_s, bss->id); + + if (changes & WPA_BSS_WPS_CHANGED_FLAG) + wpas_notify_bss_wps_changed(wpa_s, bss->id); + + if (changes & WPA_BSS_IES_CHANGED_FLAG) + wpas_notify_bss_ies_changed(wpa_s, bss->id); + + if (changes & WPA_BSS_RATES_CHANGED_FLAG) + wpas_notify_bss_rates_changed(wpa_s, bss->id); +} + + +static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + struct wpa_scan_res *res) +{ + u32 changes; + + changes = wpa_bss_compare_res(bss, res); + bss->scan_miss_count = 0; + bss->last_update_idx = wpa_s->bss_update_idx; + wpa_bss_copy_res(bss, res); + /* Move the entry to the end of the list */ + dl_list_del(&bss->list); + 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); + bss->ie_len = res->ie_len; + bss->beacon_ie_len = res->beacon_ie_len; + } else { + struct wpa_bss *nbss; + struct dl_list *prev = bss->list_id.prev; + dl_list_del(&bss->list_id); + nbss = os_realloc(bss, sizeof(*bss) + res->ie_len + + res->beacon_ie_len); + if (nbss) { + bss = nbss; + os_memcpy(bss + 1, res + 1, + res->ie_len + res->beacon_ie_len); + bss->ie_len = res->ie_len; + bss->beacon_ie_len = res->beacon_ie_len; + } + dl_list_add(prev, &bss->list_id); + } + dl_list_add_tail(&wpa_s->bss, &bss->list); + + notify_bss_changes(wpa_s, changes, bss); +} + + +static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + return bss == wpa_s->current_bss || + os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; +} + + +void wpa_bss_update_start(struct wpa_supplicant *wpa_s) +{ + wpa_s->bss_update_idx++; + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u", + wpa_s->bss_update_idx); +} + + +void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) +{ + const u8 *ssid, *p2p; + struct wpa_bss *bss; + + ssid = wpa_scan_get_ie(res, WLAN_EID_SSID); + if (ssid == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for " + MACSTR, MAC2STR(res->bssid)); + return; + } + if (ssid[1] > 32) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for " + MACSTR, MAC2STR(res->bssid)); + return; + } + + p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE); + if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN && + os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) + return; /* Skip P2P listen discovery results here */ + + /* TODO: add option for ignoring BSSes we are not interested in + * (to save memory) */ + bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); + if (bss == NULL) + wpa_bss_add(wpa_s, ssid + 2, ssid[1], res); + else + wpa_bss_update(wpa_s, bss, res); +} + + +static int wpa_bss_included_in_scan(const struct wpa_bss *bss, + const struct scan_info *info) +{ + int found; + size_t i; + + if (info == NULL) + return 1; + + if (info->num_freqs) { + found = 0; + for (i = 0; i < info->num_freqs; i++) { + if (bss->freq == info->freqs[i]) { + found = 1; + break; + } + } + if (!found) + return 0; + } + + if (info->num_ssids) { + found = 0; + for (i = 0; i < info->num_ssids; i++) { + const struct wpa_driver_scan_ssid *s = &info->ssids[i]; + if ((s->ssid == NULL || s->ssid_len == 0) || + (s->ssid_len == bss->ssid_len && + os_memcmp(s->ssid, bss->ssid, bss->ssid_len) == + 0)) { + found = 1; + break; + } + } + if (!found) + return 0; + } + + return 1; +} + + +void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, + int new_scan) +{ + struct wpa_bss *bss, *n; + + if (!new_scan) + return; /* do not expire entries without new scan */ + + dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { + if (wpa_bss_in_use(wpa_s, bss)) + continue; + if (!wpa_bss_included_in_scan(bss, info)) + continue; /* expire only BSSes that were scanned */ + if (bss->last_update_idx < wpa_s->bss_update_idx) + bss->scan_miss_count++; + if (bss->scan_miss_count >= + wpa_s->conf->bss_expiration_scan_count) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to " + "no match in scan", bss->id); + wpa_bss_remove(wpa_s, bss); + } + } +} + + +void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) +{ + struct wpa_bss *bss, *n; + struct os_time t; + + if (dl_list_empty(&wpa_s->bss)) + return; + + os_get_time(&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)) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to " + "age", bss->id); + wpa_bss_remove(wpa_s, bss); + } else + break; + } +} + + +static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); + eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, + wpa_bss_timeout, wpa_s, NULL); +} + + +int wpa_bss_init(struct wpa_supplicant *wpa_s) +{ + dl_list_init(&wpa_s->bss); + dl_list_init(&wpa_s->bss_id); + eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, + wpa_bss_timeout, wpa_s, NULL); + return 0; +} + + +void wpa_bss_flush(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss, *n; + + if (wpa_s->bss.next == NULL) + return; /* BSS table not yet initialized */ + + dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { + if (wpa_bss_in_use(wpa_s, bss)) + continue; + wpa_bss_remove(wpa_s, bss); + } +} + + +void wpa_bss_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); + wpa_bss_flush(wpa_s); +} + + +struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + struct wpa_bss *bss; + dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + return bss; + } + return NULL; +} + + +struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id) +{ + struct wpa_bss *bss; + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss->id == id) + return bss; + } + return NULL; +} + + +const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (bss + 1); + end = pos + bss->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) +{ + const u8 *end, *pos; + + pos = (const u8 *) (bss + 1); + end = pos + bss->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; +} + + +struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, + u32 vendor_type) +{ + struct wpabuf *buf; + const u8 *end, *pos; + + buf = wpabuf_alloc(bss->ie_len); + if (buf == NULL) + return NULL; + + pos = (const u8 *) (bss + 1); + end = pos + bss->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])) + wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(buf) == 0) { + wpabuf_free(buf); + buf = NULL; + } + + return buf; +} + + +int wpa_bss_get_max_rate(const struct wpa_bss *bss) +{ + int rate = 0; + const u8 *ie; + int i; + + ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + for (i = 0; ie && i < ie[1]; i++) { + if ((ie[i + 2] & 0x7f) > rate) + rate = ie[i + 2] & 0x7f; + } + + ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); + for (i = 0; ie && i < ie[1]; i++) { + if ((ie[i + 2] & 0x7f) > rate) + rate = ie[i + 2] & 0x7f; + } + + return rate; +} + + +int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates) +{ + const u8 *ie, *ie2; + int i, j; + unsigned int len; + u8 *r; + + ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); + + len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0); + + r = os_malloc(len); + if (!r) + return -1; + + for (i = 0; ie && i < ie[1]; i++) + r[i] = ie[i + 2] & 0x7f; + + for (j = 0; ie2 && j < ie2[1]; j++) + r[i + j] = ie2[j + 2] & 0x7f; + + *rates = r; + return len; +} diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h new file mode 100644 index 0000000..992b9c0 --- /dev/null +++ b/wpa_supplicant/bss.h @@ -0,0 +1,95 @@ +/* + * BSS table + * Copyright (c) 2009-2010, 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. + */ + +#ifndef BSS_H +#define BSS_H + +struct wpa_scan_res; + +#define WPA_BSS_QUAL_INVALID BIT(0) +#define WPA_BSS_NOISE_INVALID BIT(1) +#define WPA_BSS_LEVEL_INVALID BIT(2) +#define WPA_BSS_LEVEL_DBM BIT(3) +#define WPA_BSS_AUTHENTICATED BIT(4) +#define WPA_BSS_ASSOCIATED BIT(5) + +/** + * struct wpa_bss - BSS table + * @list: List entry for struct wpa_supplicant::bss + * @list_id: List entry for struct wpa_supplicant::bss_id + * @id: Unique identifier for this BSS entry + * @scan_miss_count: Number of counts without seeing this BSS + * @flags: information flags about the BSS/IBSS (WPA_BSS_*) + * @last_update_idx: Index of the last scan update + * @bssid: BSSID + * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) + * @beacon_int: beacon interval in TUs (host byte order) + * @caps: capability information field in host byte order + * @qual: signal quality + * @noise: noise level + * @level: signal level + * @tsf: Timestamp of last Beacon/Probe Response frame + * @last_update: Time of the last update (i.e., Beacon or Probe Response RX) + * @ie_len: length of the following IE field in octets (from Probe Response) + * @beacon_ie_len: length of the following Beacon IE field in octets + * + * This structure is used to store information about neighboring BSSes in + * generic format. It is mainly updated based on scan results from the driver. + */ +struct wpa_bss { + struct dl_list list; + struct dl_list list_id; + unsigned int id; + unsigned int scan_miss_count; + unsigned int last_update_idx; + unsigned int flags; + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + int freq; + u16 beacon_int; + u16 caps; + int qual; + int noise; + int level; + u64 tsf; + struct os_time last_update; + size_t ie_len; + size_t beacon_ie_len; + /* followed by ie_len octets of IEs */ + /* followed by beacon_ie_len octets of IEs */ +}; + +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); +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); +void wpa_bss_deinit(struct wpa_supplicant *wpa_s); +void wpa_bss_flush(struct wpa_supplicant *wpa_s); +void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age); +struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *ssid, size_t ssid_len); +struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, + const u8 *bssid); +struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id); +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); +struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, + u32 vendor_type); +int wpa_bss_get_max_rate(const struct wpa_bss *bss); +int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates); + +#endif /* BSS_H */ diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c new file mode 100644 index 0000000..7ec4531 --- /dev/null +++ b/wpa_supplicant/config.c @@ -0,0 +1,2490 @@ +/* + * WPA Supplicant / Configuration parser and common functions + * Copyright (c) 2003-2008, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/uuid.h" +#include "crypto/sha1.h" +#include "rsn_supp/wpa.h" +#include "eap_peer/eap.h" +#include "config.h" + + +#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE) +#define NO_CONFIG_WRITE +#endif + +/* + * Structure for network configuration parsing. This data is used to implement + * a generic parser for each network block variable. The table of configuration + * variables is defined below in this file (ssid_fields[]). + */ +struct parse_data { + /* Configuration variable name */ + char *name; + + /* Parser function for this variable */ + int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid, + int line, const char *value); + +#ifndef NO_CONFIG_WRITE + /* Writer function (i.e., to get the variable in text format from + * internal presentation). */ + char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid); +#endif /* NO_CONFIG_WRITE */ + + /* Variable specific parameters for the parser. */ + void *param1, *param2, *param3, *param4; + + /* 0 = this variable can be included in debug output and ctrl_iface + * 1 = this variable contains key/private data and it must not be + * included in debug output unless explicitly requested. In + * addition, this variable will not be readable through the + * ctrl_iface. + */ + int key_data; +}; + + +static char * wpa_config_parse_string(const char *value, size_t *len) +{ + if (*value == '"') { + const char *pos; + char *str; + value++; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + *len = pos - value; + str = os_malloc(*len + 1); + if (str == NULL) + return NULL; + os_memcpy(str, value, *len); + str[*len] = '\0'; + return str; + } else { + u8 *str; + size_t tlen, hlen = os_strlen(value); + if (hlen & 1) + return NULL; + tlen = hlen / 2; + str = os_malloc(tlen + 1); + if (str == NULL) + return NULL; + if (hexstr2bin(value, str, tlen)) { + os_free(str); + return NULL; + } + str[tlen] = '\0'; + *len = tlen; + return (char *) str; + } +} + + +static int wpa_config_parse_str(const struct parse_data *data, + struct wpa_ssid *ssid, + int line, const char *value) +{ + size_t res_len, *dst_len; + char **dst, *tmp; + + if (os_strcmp(value, "NULL") == 0) { + wpa_printf(MSG_DEBUG, "Unset configuration string '%s'", + data->name); + tmp = NULL; + res_len = 0; + goto set; + } + + tmp = wpa_config_parse_string(value, &res_len); + if (tmp == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.", + line, data->name, + data->key_data ? "[KEY DATA REMOVED]" : value); + return -1; + } + + if (data->key_data) { + wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, + (u8 *) tmp, res_len); + } else { + wpa_hexdump_ascii(MSG_MSGDUMP, data->name, + (u8 *) tmp, res_len); + } + + if (data->param3 && res_len < (size_t) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " + "min_len=%ld)", line, data->name, + (unsigned long) res_len, (long) data->param3); + os_free(tmp); + return -1; + } + + if (data->param4 && res_len > (size_t) data->param4) { + wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " + "max_len=%ld)", line, data->name, + (unsigned long) res_len, (long) data->param4); + os_free(tmp); + return -1; + } + +set: + dst = (char **) (((u8 *) ssid) + (long) data->param1); + dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2); + os_free(*dst); + *dst = tmp; + if (data->param2) + *dst_len = res_len; + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static int is_hex(const u8 *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] < 32 || data[i] >= 127) + return 1; + } + return 0; +} + + +static char * wpa_config_write_string_ascii(const u8 *value, size_t len) +{ + char *buf; + + buf = os_malloc(len + 3); + if (buf == NULL) + return NULL; + buf[0] = '"'; + os_memcpy(buf + 1, value, len); + buf[len + 1] = '"'; + buf[len + 2] = '\0'; + + return buf; +} + + +static char * wpa_config_write_string_hex(const u8 *value, size_t len) +{ + char *buf; + + buf = os_zalloc(2 * len + 1); + if (buf == NULL) + return NULL; + wpa_snprintf_hex(buf, 2 * len + 1, value, len); + + return buf; +} + + +static char * wpa_config_write_string(const u8 *value, size_t len) +{ + if (value == NULL) + return NULL; + + if (is_hex(value, len)) + return wpa_config_write_string_hex(value, len); + else + return wpa_config_write_string_ascii(value, len); +} + + +static char * wpa_config_write_str(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + size_t len; + char **src; + + src = (char **) (((u8 *) ssid) + (long) data->param1); + if (*src == NULL) + return NULL; + + if (data->param2) + len = *((size_t *) (((u8 *) ssid) + (long) data->param2)); + else + len = os_strlen(*src); + + return wpa_config_write_string((const u8 *) *src, len); +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_int(const struct parse_data *data, + struct wpa_ssid *ssid, + int line, const char *value) +{ + int *dst; + + dst = (int *) (((u8 *) ssid) + (long) data->param1); + *dst = atoi(value); + wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); + + if (data->param3 && *dst < (long) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " + "min_value=%ld)", line, data->name, *dst, + (long) data->param3); + *dst = (long) data->param3; + return -1; + } + + if (data->param4 && *dst > (long) data->param4) { + wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " + "max_value=%ld)", line, data->name, *dst, + (long) data->param4); + *dst = (long) data->param4; + return -1; + } + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_int(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int *src, res; + char *value; + + src = (int *) (((u8 *) ssid) + (long) data->param1); + + value = os_malloc(20); + if (value == NULL) + return NULL; + res = os_snprintf(value, 20, "%d", *src); + 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_bssid(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 || + os_strcmp(value, "any") == 0) { + ssid->bssid_set = 0; + wpa_printf(MSG_MSGDUMP, "BSSID any"); + return 0; + } + if (hwaddr_aton(value, ssid->bssid)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.", + line, value); + return -1; + } + ssid->bssid_set = 1; + wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN); + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_bssid(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *value; + int res; + + if (!ssid->bssid_set) + return NULL; + + value = os_malloc(20); + if (value == NULL) + return NULL; + res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid)); + 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_psk(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + if (*value == '"') { +#ifndef CONFIG_NO_PBKDF2 + const char *pos; + size_t len; + + value++; + pos = os_strrchr(value, '"'); + if (pos) + len = pos - value; + else + len = os_strlen(value); + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase " + "length %lu (expected: 8..63) '%s'.", + line, (unsigned long) len, value); + return -1; + } + wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)", + (u8 *) value, len); + if (ssid->passphrase && os_strlen(ssid->passphrase) == len && + os_memcmp(ssid->passphrase, value, len) == 0) + return 0; + ssid->psk_set = 0; + os_free(ssid->passphrase); + ssid->passphrase = os_malloc(len + 1); + if (ssid->passphrase == NULL) + return -1; + os_memcpy(ssid->passphrase, value, len); + ssid->passphrase[len] = '\0'; + return 0; +#else /* CONFIG_NO_PBKDF2 */ + wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not " + "supported.", line); + return -1; +#endif /* CONFIG_NO_PBKDF2 */ + } + + if (hexstr2bin(value, ssid->psk, PMK_LEN) || + value[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", + line, value); + return -1; + } + + os_free(ssid->passphrase); + ssid->passphrase = NULL; + + ssid->psk_set = 1; + wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN); + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_psk(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + if (ssid->passphrase) + return wpa_config_write_string_ascii( + (const u8 *) ssid->passphrase, + os_strlen(ssid->passphrase)); + + if (ssid->psk_set) + return wpa_config_write_string_hex(ssid->psk, PMK_LEN); + + return NULL; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_proto(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "WPA") == 0) + val |= WPA_PROTO_WPA; + else if (os_strcmp(start, "RSN") == 0 || + os_strcmp(start, "WPA2") == 0) + val |= WPA_PROTO_RSN; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no proto values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val); + ssid->proto = val; + return errors ? -1 : 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_proto(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int first = 1, ret; + char *buf, *pos, *end; + + pos = buf = os_zalloc(10); + if (buf == NULL) + return NULL; + end = buf + 10; + + if (ssid->proto & WPA_PROTO_WPA) { + ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " "); + 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 ? "" : " "); + if (ret < 0 || ret >= end - pos) + return buf; + pos += ret; + first = 0; + } + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_key_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (os_strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; + else if (os_strcmp(start, "IEEE8021X") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else if (os_strcmp(start, "NONE") == 0) + val |= WPA_KEY_MGMT_NONE; + else if (os_strcmp(start, "WPA-NONE") == 0) + val |= WPA_KEY_MGMT_WPA_NONE; +#ifdef CONFIG_IEEE80211R + else if (os_strcmp(start, "FT-PSK") == 0) + val |= WPA_KEY_MGMT_FT_PSK; + else if (os_strcmp(start, "FT-EAP") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) + val |= WPA_KEY_MGMT_PSK_SHA256; + else if (os_strcmp(start, "WPA-EAP-SHA256") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SHA256; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPS + else if (os_strcmp(start, "WPS") == 0) + val |= WPA_KEY_MGMT_WPS; +#endif /* CONFIG_WPS */ + else { + wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no key_mgmt values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val); + ssid->key_mgmt = val; + return errors ? -1 : 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_key_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *buf, *pos, *end; + int ret; + + pos = buf = os_zalloc(50); + if (buf == NULL) + return NULL; + end = buf + 50; + + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + ret = os_snprintf(pos, end - pos, "%sWPA-PSK", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "%sWPA-EAP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + ret = os_snprintf(pos, end - pos, "%sIEEE8021X", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) { + ret = os_snprintf(pos, end - pos, "%sWPA-NONE", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + +#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_IEEE8021X) + pos += os_snprintf(pos, end - pos, "%sFT-EAP", + pos == buf ? "" : " "); +#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_IEEE8021X_SHA256) + pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256", + pos == buf ? "" : " "); +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_WPS + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) + pos += os_snprintf(pos, end - pos, "%sWPS", + pos == buf ? "" : " "); +#endif /* CONFIG_WPS */ + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_cipher(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (os_strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (os_strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (os_strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, start); + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_cipher(int cipher) +{ + char *buf, *pos, *end; + int ret; + + pos = buf = os_zalloc(50); + if (buf == NULL) + return NULL; + end = buf + 50; + + if (cipher & WPA_CIPHER_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (cipher & WPA_CIPHER_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (cipher & WPA_CIPHER_WEP104) { + ret = os_snprintf(pos, end - pos, "%sWEP104", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (cipher & WPA_CIPHER_WEP40) { + ret = os_snprintf(pos, end - pos, "%sWEP40", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (cipher & WPA_CIPHER_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_pairwise(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val; + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) { + wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher " + "(0x%x).", line, val); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val); + ssid->pairwise_cipher = val; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_pairwise(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_cipher(ssid->pairwise_cipher); +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_group(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val; + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | + WPA_CIPHER_WEP40)) { + wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " + "(0x%x).", line, val); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "group: 0x%x", val); + ssid->group_cipher = val; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_group(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_cipher(ssid->group_cipher); +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_auth_alg(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "OPEN") == 0) + val |= WPA_AUTH_ALG_OPEN; + else if (os_strcmp(start, "SHARED") == 0) + val |= WPA_AUTH_ALG_SHARED; + else if (os_strcmp(start, "LEAP") == 0) + val |= WPA_AUTH_ALG_LEAP; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no auth_alg values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val); + ssid->auth_alg = val; + return errors ? -1 : 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_auth_alg(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *buf, *pos, *end; + int ret; + + pos = buf = os_zalloc(30); + if (buf == NULL) + return NULL; + end = buf + 30; + + if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) { + ret = os_snprintf(pos, end - pos, "%sOPEN", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) { + ret = os_snprintf(pos, end - pos, "%sSHARED", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) { + ret = os_snprintf(pos, end - pos, "%sLEAP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int * wpa_config_parse_freqs(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int *freqs; + size_t used, len; + const char *pos; + + used = 0; + len = 10; + freqs = os_zalloc((len + 1) * sizeof(int)); + if (freqs == NULL) + return NULL; + + pos = value; + while (pos) { + while (*pos == ' ') + pos++; + if (used == len) { + int *n; + size_t i; + n = os_realloc(freqs, (len * 2 + 1) * sizeof(int)); + if (n == NULL) { + os_free(freqs); + return NULL; + } + for (i = len; i <= len * 2; i++) + n[i] = 0; + freqs = n; + len *= 2; + } + + freqs[used] = atoi(pos); + if (freqs[used] == 0) + break; + used++; + pos = os_strchr(pos + 1, ' '); + } + + return freqs; +} + + +static int wpa_config_parse_scan_freq(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int *freqs; + + freqs = wpa_config_parse_freqs(data, ssid, line, value); + if (freqs == NULL) + return -1; + os_free(ssid->scan_freq); + ssid->scan_freq = freqs; + + return 0; +} + + +static int wpa_config_parse_freq_list(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int *freqs; + + freqs = wpa_config_parse_freqs(data, ssid, line, value); + if (freqs == NULL) + return -1; + os_free(ssid->freq_list); + ssid->freq_list = freqs; + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_freqs(const struct parse_data *data, + const int *freqs) +{ + char *buf, *pos, *end; + int i, ret; + size_t count; + + if (freqs == NULL) + return NULL; + + count = 0; + for (i = 0; freqs[i]; i++) + count++; + + pos = buf = os_zalloc(10 * count + 1); + if (buf == NULL) + return NULL; + end = buf + 10 * count + 1; + + for (i = 0; freqs[i]; i++) { + ret = os_snprintf(pos, end - pos, "%s%u", + i == 0 ? "" : " ", freqs[i]); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + return buf; +} + + +static char * wpa_config_write_scan_freq(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_freqs(data, ssid->scan_freq); +} + + +static char * wpa_config_write_freq_list(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_freqs(data, ssid->freq_list); +} +#endif /* NO_CONFIG_WRITE */ + + +#ifdef IEEE8021X_EAPOL +static int wpa_config_parse_eap(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int last, errors = 0; + char *start, *end, *buf; + struct eap_method_type *methods = NULL, *tmp; + size_t num_methods = 0; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + tmp = methods; + methods = os_realloc(methods, + (num_methods + 1) * sizeof(*methods)); + if (methods == NULL) { + os_free(tmp); + os_free(buf); + return -1; + } + methods[num_methods].method = eap_peer_get_type( + start, &methods[num_methods].vendor); + if (methods[num_methods].vendor == EAP_VENDOR_IETF && + methods[num_methods].method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "Line %d: unknown EAP method " + "'%s'", line, start); + wpa_printf(MSG_ERROR, "You may need to add support for" + " this EAP method during wpa_supplicant\n" + "build time configuration.\n" + "See README for more information."); + errors++; + } else if (methods[num_methods].vendor == EAP_VENDOR_IETF && + methods[num_methods].method == EAP_TYPE_LEAP) + ssid->leap++; + else + ssid->non_leap++; + num_methods++; + if (last) + break; + start = end + 1; + } + os_free(buf); + + tmp = methods; + methods = os_realloc(methods, (num_methods + 1) * sizeof(*methods)); + if (methods == NULL) { + os_free(tmp); + return -1; + } + methods[num_methods].vendor = EAP_VENDOR_IETF; + methods[num_methods].method = EAP_TYPE_NONE; + num_methods++; + + wpa_hexdump(MSG_MSGDUMP, "eap methods", + (u8 *) methods, num_methods * sizeof(*methods)); + ssid->eap.eap_methods = methods; + return errors ? -1 : 0; +} + + +static char * wpa_config_write_eap(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int i, ret; + char *buf, *pos, *end; + const struct eap_method_type *eap_methods = ssid->eap.eap_methods; + const char *name; + + if (eap_methods == NULL) + return NULL; + + pos = buf = os_zalloc(100); + if (buf == NULL) + return NULL; + end = buf + 100; + + for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF || + eap_methods[i].method != EAP_TYPE_NONE; i++) { + name = eap_get_name(eap_methods[i].vendor, + eap_methods[i].method); + if (name) { + ret = os_snprintf(pos, end - pos, "%s%s", + pos == buf ? "" : " ", name); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + } + } + + end[-1] = '\0'; + + return buf; +} + + +static int wpa_config_parse_password(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + u8 *hash; + + if (os_strcmp(value, "NULL") == 0) { + wpa_printf(MSG_DEBUG, "Unset configuration string 'password'"); + os_free(ssid->eap.password); + ssid->eap.password = NULL; + ssid->eap.password_len = 0; + return 0; + } + + if (os_strncmp(value, "hash:", 5) != 0) { + char *tmp; + size_t res_len; + + tmp = wpa_config_parse_string(value, &res_len); + if (tmp == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to parse " + "password.", line); + return -1; + } + wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, + (u8 *) tmp, res_len); + + os_free(ssid->eap.password); + ssid->eap.password = (u8 *) tmp; + ssid->eap.password_len = res_len; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + + return 0; + } + + + /* NtPasswordHash: hash:<32 hex digits> */ + if (os_strlen(value + 5) != 2 * 16) { + wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length " + "(expected 32 hex digits)", line); + return -1; + } + + hash = os_malloc(16); + if (hash == NULL) + return -1; + + if (hexstr2bin(value + 5, hash, 16)) { + os_free(hash); + wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line); + return -1; + } + + wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16); + + os_free(ssid->eap.password); + ssid->eap.password = hash; + ssid->eap.password_len = 16; + ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + + return 0; +} + + +static char * wpa_config_write_password(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *buf; + + if (ssid->eap.password == NULL) + return NULL; + + if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) { + return wpa_config_write_string( + ssid->eap.password, ssid->eap.password_len); + } + + buf = os_malloc(5 + 32 + 1); + if (buf == NULL) + return NULL; + + os_memcpy(buf, "hash:", 5); + wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16); + + return buf; +} +#endif /* IEEE8021X_EAPOL */ + + +static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, + const char *value, int idx) +{ + char *buf, title[20]; + int res; + + buf = wpa_config_parse_string(value, len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.", + line, idx, value); + return -1; + } + if (*len > MAX_WEP_KEY_LEN) { + wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.", + line, idx, value); + os_free(buf); + return -1; + } + os_memcpy(key, buf, *len); + os_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); + return 0; +} + + +static int wpa_config_parse_wep_key0(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_wep_key(ssid->wep_key[0], + &ssid->wep_key_len[0], line, + value, 0); +} + + +static int wpa_config_parse_wep_key1(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_wep_key(ssid->wep_key[1], + &ssid->wep_key_len[1], line, + value, 1); +} + + +static int wpa_config_parse_wep_key2(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_wep_key(ssid->wep_key[2], + &ssid->wep_key_len[2], line, + value, 2); +} + + +static int wpa_config_parse_wep_key3(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_wep_key(ssid->wep_key[3], + &ssid->wep_key_len[3], line, + value, 3); +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx) +{ + if (ssid->wep_key_len[idx] == 0) + return NULL; + return wpa_config_write_string(ssid->wep_key[idx], + ssid->wep_key_len[idx]); +} + + +static char * wpa_config_write_wep_key0(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_wep_key(ssid, 0); +} + + +static char * wpa_config_write_wep_key1(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_wep_key(ssid, 1); +} + + +static char * wpa_config_write_wep_key2(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_wep_key(ssid, 2); +} + + +static char * wpa_config_write_wep_key3(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_wep_key(ssid, 3); +} +#endif /* NO_CONFIG_WRITE */ + + +/* Helper macros for network block parser */ + +#ifdef OFFSET +#undef OFFSET +#endif /* OFFSET */ +/* OFFSET: Get offset of a variable within the wpa_ssid structure */ +#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v) + +/* STR: Define a string variable for an ASCII string; f = field name */ +#ifdef NO_CONFIG_WRITE +#define _STR(f) #f, wpa_config_parse_str, OFFSET(f) +#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f) +#else /* NO_CONFIG_WRITE */ +#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f) +#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f) +#endif /* NO_CONFIG_WRITE */ +#define STR(f) _STR(f), NULL, NULL, NULL, 0 +#define STRe(f) _STRe(f), NULL, NULL, NULL, 0 +#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1 +#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1 + +/* STR_LEN: Define a string variable with a separate variable for storing the + * data length. Unlike STR(), this can be used to store arbitrary binary data + * (i.e., even nul termination character). */ +#define _STR_LEN(f) _STR(f), OFFSET(f ## _len) +#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len) +#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0 +#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0 +#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1 + +/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length + * explicitly specified. */ +#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max) +#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0 +#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1 + +#ifdef NO_CONFIG_WRITE +#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0 +#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0 +#else /* NO_CONFIG_WRITE */ +#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \ + OFFSET(f), (void *) 0 +#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \ + OFFSET(eap.f), (void *) 0 +#endif /* NO_CONFIG_WRITE */ + +/* INT: Define an integer variable */ +#define INT(f) _INT(f), NULL, NULL, 0 +#define INTe(f) _INTe(f), NULL, NULL, 0 + +/* INT_RANGE: Define an integer variable with allowed value range */ +#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0 + +/* FUNC: Define a configuration variable that uses a custom function for + * parsing and writing the value. */ +#ifdef NO_CONFIG_WRITE +#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL +#else /* NO_CONFIG_WRITE */ +#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \ + NULL, NULL, NULL, NULL +#endif /* NO_CONFIG_WRITE */ +#define FUNC(f) _FUNC(f), 0 +#define FUNC_KEY(f) _FUNC(f), 1 + +/* + * Table of network configuration variables. This table is used to parse each + * network configuration variable, e.g., each line in wpa_supplicant.conf file + * that is inside a network block. + * + * This table is generated using the helper macros defined above and with + * generous help from the C pre-processor. The field name is stored as a string + * into .name and for STR and INT types, the offset of the target buffer within + * struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar + * offset to the field containing the length of the configuration variable. + * .param3 and .param4 can be used to mark the allowed range (length for STR + * and value for INT). + * + * For each configuration line in wpa_supplicant.conf, the parser goes through + * this table and select the entry that matches with the field name. The parser + * function (.parser) is then called to parse the actual value of the field. + * + * This kind of mechanism makes it easy to add new configuration parameters, + * since only one line needs to be added into this table and into the + * struct wpa_ssid definition if the new variable is either a string or + * integer. More complex types will need to use their own parser and writer + * functions. + */ +static const struct parse_data ssid_fields[] = { + { STR_RANGE(ssid, 0, MAX_SSID_LEN) }, + { INT_RANGE(scan_ssid, 0, 1) }, + { FUNC(bssid) }, + { FUNC_KEY(psk) }, + { FUNC(proto) }, + { FUNC(key_mgmt) }, + { FUNC(pairwise) }, + { FUNC(group) }, + { FUNC(auth_alg) }, + { FUNC(scan_freq) }, + { FUNC(freq_list) }, +#ifdef IEEE8021X_EAPOL + { FUNC(eap) }, + { STR_LENe(identity) }, + { STR_LENe(anonymous_identity) }, + { FUNC_KEY(password) }, + { STRe(ca_cert) }, + { STRe(ca_path) }, + { STRe(client_cert) }, + { STRe(private_key) }, + { STR_KEYe(private_key_passwd) }, + { STRe(dh_file) }, + { STRe(subject_match) }, + { STRe(altsubject_match) }, + { STRe(ca_cert2) }, + { STRe(ca_path2) }, + { STRe(client_cert2) }, + { STRe(private_key2) }, + { STR_KEYe(private_key2_passwd) }, + { STRe(dh_file2) }, + { STRe(subject_match2) }, + { STRe(altsubject_match2) }, + { STRe(phase1) }, + { STRe(phase2) }, + { STRe(pcsc) }, + { STR_KEYe(pin) }, + { STRe(engine_id) }, + { STRe(key_id) }, + { STRe(cert_id) }, + { STRe(ca_cert_id) }, + { STR_KEYe(pin2) }, + { STRe(engine2_id) }, + { STRe(key2_id) }, + { STRe(cert2_id) }, + { STRe(ca_cert2_id) }, + { INTe(engine) }, + { INTe(engine2) }, + { INT(eapol_flags) }, +#endif /* IEEE8021X_EAPOL */ + { FUNC_KEY(wep_key0) }, + { FUNC_KEY(wep_key1) }, + { FUNC_KEY(wep_key2) }, + { FUNC_KEY(wep_key3) }, + { INT(wep_tx_keyidx) }, + { INT(priority) }, +#ifdef IEEE8021X_EAPOL + { INT(eap_workaround) }, + { STRe(pac_file) }, + { INTe(fragment_size) }, +#endif /* IEEE8021X_EAPOL */ + { INT_RANGE(mode, 0, 4) }, + { INT_RANGE(proactive_key_caching, 0, 1) }, + { INT_RANGE(disabled, 0, 2) }, + { STR(id_str) }, +#ifdef CONFIG_IEEE80211W + { INT_RANGE(ieee80211w, 0, 2) }, +#endif /* CONFIG_IEEE80211W */ + { INT_RANGE(peerkey, 0, 1) }, + { INT_RANGE(mixed_cell, 0, 1) }, + { INT_RANGE(frequency, 0, 10000) }, + { INT(wpa_ptk_rekey) }, + { STR(bgscan) }, +}; + +#undef OFFSET +#undef _STR +#undef STR +#undef STR_KEY +#undef _STR_LEN +#undef STR_LEN +#undef STR_LEN_KEY +#undef _STR_RANGE +#undef STR_RANGE +#undef STR_RANGE_KEY +#undef _INT +#undef INT +#undef INT_RANGE +#undef _FUNC +#undef FUNC +#undef FUNC_KEY +#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0])) + + +/** + * wpa_config_add_prio_network - Add a network to priority lists + * @config: Configuration data from wpa_config_read() + * @ssid: Pointer to the network configuration to be added to the list + * Returns: 0 on success, -1 on failure + * + * This function is used to add a network block to the priority list of + * networks. This must be called for each network when reading in the full + * configuration. In addition, this can be used indirectly when updating + * priorities by calling wpa_config_update_prio_list(). + */ +int wpa_config_add_prio_network(struct wpa_config *config, + struct wpa_ssid *ssid) +{ + int prio; + struct wpa_ssid *prev, **nlist; + + /* + * Add to an existing priority list if one is available for the + * configured priority level for this network. + */ + for (prio = 0; prio < config->num_prio; prio++) { + prev = config->pssid[prio]; + if (prev->priority == ssid->priority) { + while (prev->pnext) + prev = prev->pnext; + prev->pnext = ssid; + return 0; + } + } + + /* First network for this priority - add a new priority list */ + nlist = os_realloc(config->pssid, + (config->num_prio + 1) * sizeof(struct wpa_ssid *)); + if (nlist == NULL) + return -1; + + for (prio = 0; prio < config->num_prio; prio++) { + if (nlist[prio]->priority < ssid->priority) + break; + } + + os_memmove(&nlist[prio + 1], &nlist[prio], + (config->num_prio - prio) * sizeof(struct wpa_ssid *)); + + nlist[prio] = ssid; + config->num_prio++; + config->pssid = nlist; + + return 0; +} + + +/** + * wpa_config_update_prio_list - Update network priority list + * @config: Configuration data from wpa_config_read() + * Returns: 0 on success, -1 on failure + * + * This function is called to update the priority list of networks in the + * configuration when a network is being added or removed. This is also called + * if a priority for a network is changed. + */ +int wpa_config_update_prio_list(struct wpa_config *config) +{ + struct wpa_ssid *ssid; + int ret = 0; + + os_free(config->pssid); + config->pssid = NULL; + config->num_prio = 0; + + ssid = config->ssid; + while (ssid) { + ssid->pnext = NULL; + if (wpa_config_add_prio_network(config, ssid) < 0) + ret = -1; + ssid = ssid->next; + } + + return ret; +} + + +#ifdef IEEE8021X_EAPOL +static void eap_peer_config_free(struct eap_peer_config *eap) +{ + os_free(eap->eap_methods); + os_free(eap->identity); + os_free(eap->anonymous_identity); + os_free(eap->password); + 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); + os_free(eap->dh_file); + os_free(eap->subject_match); + os_free(eap->altsubject_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); + os_free(eap->dh_file2); + os_free(eap->subject_match2); + os_free(eap->altsubject_match2); + os_free(eap->phase1); + os_free(eap->phase2); + os_free(eap->pcsc); + os_free(eap->pin); + os_free(eap->engine_id); + os_free(eap->key_id); + os_free(eap->cert_id); + os_free(eap->ca_cert_id); + os_free(eap->key2_id); + os_free(eap->cert2_id); + os_free(eap->ca_cert2_id); + os_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); +} +#endif /* IEEE8021X_EAPOL */ + + +/** + * wpa_config_free_ssid - Free network/ssid configuration data + * @ssid: Configuration data for the network + * + * This function frees all resources allocated for the network configuration + * data. + */ +void wpa_config_free_ssid(struct wpa_ssid *ssid) +{ + os_free(ssid->ssid); + os_free(ssid->passphrase); +#ifdef IEEE8021X_EAPOL + eap_peer_config_free(&ssid->eap); +#endif /* IEEE8021X_EAPOL */ + os_free(ssid->id_str); + os_free(ssid->scan_freq); + os_free(ssid->freq_list); + os_free(ssid->bgscan); + os_free(ssid); +} + + +/** + * wpa_config_free - Free configuration data + * @config: Configuration data from wpa_config_read() + * + * This function frees all resources allocated for the configuration data by + * wpa_config_read(). + */ +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; + + ssid = config->ssid; + while (ssid) { + prev = ssid; + ssid = ssid->next; + wpa_config_free_ssid(prev); + } + +#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 */ + + os_free(config->ctrl_interface); + os_free(config->ctrl_interface_group); + os_free(config->opensc_engine_path); + os_free(config->pkcs11_engine_path); + os_free(config->pkcs11_module_path); + os_free(config->driver_param); + os_free(config->device_name); + os_free(config->manufacturer); + os_free(config->model_name); + os_free(config->model_number); + os_free(config->serial_number); + os_free(config->config_methods); + os_free(config->p2p_ssid_postfix); + os_free(config->pssid); + os_free(config); +} + + +/** + * wpa_config_foreach_network - Iterate over each configured network + * @config: Configuration data from wpa_config_read() + * @func: Callback function to process each network + * @arg: Opaque argument to pass to callback function + * + * Iterate over the set of configured networks calling the specified + * function for each item. We guard against callbacks removing the + * supplied network. + */ +void wpa_config_foreach_network(struct wpa_config *config, + void (*func)(void *, struct wpa_ssid *), + void *arg) +{ + struct wpa_ssid *ssid, *next; + + ssid = config->ssid; + while (ssid) { + next = ssid->next; + func(arg, ssid); + ssid = next; + } +} + + +/** + * wpa_config_get_network - Get configured network based on id + * @config: Configuration data from wpa_config_read() + * @id: Unique network id to search for + * Returns: Network configuration or %NULL if not found + */ +struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id) +{ + struct wpa_ssid *ssid; + + ssid = config->ssid; + while (ssid) { + if (id == ssid->id) + break; + ssid = ssid->next; + } + + return ssid; +} + + +/** + * wpa_config_add_network - Add a new network with empty configuration + * @config: Configuration data from wpa_config_read() + * Returns: The new network configuration or %NULL if operation failed + */ +struct wpa_ssid * wpa_config_add_network(struct wpa_config *config) +{ + int id; + struct wpa_ssid *ssid, *last = NULL; + + id = -1; + ssid = config->ssid; + while (ssid) { + if (ssid->id > id) + id = ssid->id; + last = ssid; + ssid = ssid->next; + } + id++; + + ssid = os_zalloc(sizeof(*ssid)); + if (ssid == NULL) + return NULL; + ssid->id = id; + if (last) + last->next = ssid; + else + config->ssid = ssid; + + wpa_config_update_prio_list(config); + + return ssid; +} + + +/** + * wpa_config_remove_network - Remove a configured network based on id + * @config: Configuration data from wpa_config_read() + * @id: Unique network id to search for + * Returns: 0 on success, or -1 if the network was not found + */ +int wpa_config_remove_network(struct wpa_config *config, int id) +{ + struct wpa_ssid *ssid, *prev = NULL; + + ssid = config->ssid; + while (ssid) { + if (id == ssid->id) + break; + prev = ssid; + ssid = ssid->next; + } + + if (ssid == NULL) + return -1; + + if (prev) + prev->next = ssid->next; + else + config->ssid = ssid->next; + + wpa_config_update_prio_list(config); + wpa_config_free_ssid(ssid); + return 0; +} + + +/** + * wpa_config_set_network_defaults - Set network default values + * @ssid: Pointer to network configuration data + */ +void wpa_config_set_network_defaults(struct wpa_ssid *ssid) +{ + ssid->proto = DEFAULT_PROTO; + ssid->pairwise_cipher = DEFAULT_PAIRWISE; + ssid->group_cipher = DEFAULT_GROUP; + ssid->key_mgmt = DEFAULT_KEY_MGMT; +#ifdef IEEE8021X_EAPOL + ssid->eapol_flags = DEFAULT_EAPOL_FLAGS; + ssid->eap_workaround = DEFAULT_EAP_WORKAROUND; + ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE; +#endif /* IEEE8021X_EAPOL */ +} + + +/** + * wpa_config_set - Set a variable in network configuration + * @ssid: Pointer to network configuration data + * @var: Variable name, e.g., "ssid" + * @value: Variable value + * @line: Line number in configuration file or 0 if not used + * Returns: 0 on success, -1 on failure + * + * This function can be used to set network configuration variables based on + * both the configuration file and management interface input. The value + * parameter must be in the same format as the text-based configuration file is + * using. For example, strings are using double quotation marks. + */ +int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value, + int line) +{ + size_t i; + int ret = 0; + + if (ssid == NULL || var == NULL || value == NULL) + return -1; + + for (i = 0; i < NUM_SSID_FIELDS; i++) { + const struct parse_data *field = &ssid_fields[i]; + if (os_strcmp(var, field->name) != 0) + continue; + + if (field->parser(field, ssid, line, value)) { + if (line) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse %s '%s'.", line, var, value); + } + ret = -1; + } + break; + } + if (i == NUM_SSID_FIELDS) { + if (line) { + wpa_printf(MSG_ERROR, "Line %d: unknown network field " + "'%s'.", line, var); + } + ret = -1; + } + + return ret; +} + + +/** + * wpa_config_get_all - Get all options from network configuration + * @ssid: Pointer to network configuration data + * @get_keys: Determines if keys/passwords will be included in returned list + * (if they may be exported) + * Returns: %NULL terminated list of all set keys and their values in the form + * of [key1, val1, key2, val2, ... , NULL] + * + * This function can be used to get list of all configured network properties. + * The caller is responsible for freeing the returned list and all its + * elements. + */ +char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys) +{ + const struct parse_data *field; + char *key, *value; + size_t i; + char **props; + int fields_num; + + get_keys = get_keys && ssid->export_keys; + + props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1)); + if (!props) + return NULL; + + fields_num = 0; + for (i = 0; i < NUM_SSID_FIELDS; i++) { + field = &ssid_fields[i]; + if (field->key_data && !get_keys) + continue; + value = field->writer(field, ssid); + if (value == NULL) + continue; + if (os_strlen(value) == 0) { + os_free(value); + continue; + } + + key = os_strdup(field->name); + if (key == NULL) { + os_free(value); + goto err; + } + + props[fields_num * 2] = key; + props[fields_num * 2 + 1] = value; + + fields_num++; + } + + return props; + +err: + value = *props; + while (value) + os_free(value++); + os_free(props); + return NULL; +} + + +#ifndef NO_CONFIG_WRITE +/** + * wpa_config_get - Get a variable in network configuration + * @ssid: Pointer to network configuration data + * @var: Variable name, e.g., "ssid" + * Returns: Value of the variable or %NULL on failure + * + * This function can be used to get network configuration variables. The + * returned value is a copy of the configuration variable in text format, i.e,. + * the same format that the text-based configuration file and wpa_config_set() + * are using for the value. The caller is responsible for freeing the returned + * value. + */ +char * wpa_config_get(struct wpa_ssid *ssid, const char *var) +{ + size_t i; + + if (ssid == NULL || var == NULL) + return NULL; + + for (i = 0; i < NUM_SSID_FIELDS; i++) { + const struct parse_data *field = &ssid_fields[i]; + if (os_strcmp(var, field->name) == 0) + return field->writer(field, ssid); + } + + return NULL; +} + + +/** + * wpa_config_get_no_key - Get a variable in network configuration (no keys) + * @ssid: Pointer to network configuration data + * @var: Variable name, e.g., "ssid" + * Returns: Value of the variable or %NULL on failure + * + * This function can be used to get network configuration variable like + * wpa_config_get(). The only difference is that this functions does not expose + * key/password material from the configuration. In case a key/password field + * is requested, the returned value is an empty string or %NULL if the variable + * is not set or "*" if the variable is set (regardless of its value). The + * returned value is a copy of the configuration variable in text format, i.e,. + * the same format that the text-based configuration file and wpa_config_set() + * are using for the value. The caller is responsible for freeing the returned + * value. + */ +char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var) +{ + size_t i; + + if (ssid == NULL || var == NULL) + return NULL; + + for (i = 0; i < NUM_SSID_FIELDS; i++) { + const struct parse_data *field = &ssid_fields[i]; + if (os_strcmp(var, field->name) == 0) { + char *res = field->writer(field, ssid); + if (field->key_data) { + if (res && res[0]) { + wpa_printf(MSG_DEBUG, "Do not allow " + "key_data field to be " + "exposed"); + os_free(res); + return os_strdup("*"); + } + + os_free(res); + return NULL; + } + return res; + } + } + + return NULL; +} +#endif /* NO_CONFIG_WRITE */ + + +/** + * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID + * @ssid: Pointer to network configuration data + * + * This function must be called to update WPA PSK when either SSID or the + * passphrase has changed for the network configuration. + */ +void wpa_config_update_psk(struct wpa_ssid *ssid) +{ +#ifndef CONFIG_NO_PBKDF2 + pbkdf2_sha1(ssid->passphrase, + (char *) ssid->ssid, ssid->ssid_len, 4096, + ssid->psk, PMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", + ssid->psk, PMK_LEN); + ssid->psk_set = 1; +#endif /* CONFIG_NO_PBKDF2 */ +} + + +#ifndef CONFIG_NO_CONFIG_BLOBS +/** + * wpa_config_get_blob - Get a named configuration blob + * @config: Configuration data from wpa_config_read() + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ +const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config, + const char *name) +{ + struct wpa_config_blob *blob = config->blobs; + + while (blob) { + if (os_strcmp(blob->name, name) == 0) + return blob; + blob = blob->next; + } + return NULL; +} + + +/** + * wpa_config_set_blob - Set or add a named configuration blob + * @config: Configuration data from wpa_config_read() + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an existing + * blob. + */ +void wpa_config_set_blob(struct wpa_config *config, + struct wpa_config_blob *blob) +{ + wpa_config_remove_blob(config, blob->name); + blob->next = config->blobs; + config->blobs = blob; +} + + +/** + * wpa_config_free_blob - Free blob data + * @blob: Pointer to blob to be freed + */ +void wpa_config_free_blob(struct wpa_config_blob *blob) +{ + if (blob) { + os_free(blob->name); + os_free(blob->data); + os_free(blob); + } +} + + +/** + * wpa_config_remove_blob - Remove a named configuration blob + * @config: Configuration data from wpa_config_read() + * @name: Name of the blob to remove + * Returns: 0 if blob was removed or -1 if blob was not found + */ +int wpa_config_remove_blob(struct wpa_config *config, const char *name) +{ + struct wpa_config_blob *pos = config->blobs, *prev = NULL; + + while (pos) { + if (os_strcmp(pos->name, name) == 0) { + if (prev) + prev->next = pos->next; + else + config->blobs = pos->next; + wpa_config_free_blob(pos); + return 0; + } + prev = pos; + pos = pos->next; + } + + return -1; +} +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +/** + * wpa_config_alloc_empty - Allocate an empty configuration + * @ctrl_interface: Control interface parameters, e.g., path to UNIX domain + * socket + * @driver_param: Driver parameters + * Returns: Pointer to allocated configuration data or %NULL on failure + */ +struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, + const char *driver_param) +{ + struct wpa_config *config; + + config = os_zalloc(sizeof(*config)); + if (config == NULL) + return NULL; + config->eapol_version = DEFAULT_EAPOL_VERSION; + config->ap_scan = DEFAULT_AP_SCAN; + config->fast_reauth = DEFAULT_FAST_REAUTH; + config->p2p_go_intent = DEFAULT_P2P_GO_INTENT; + config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS; + 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; + config->max_num_sta = DEFAULT_MAX_NUM_STA; + + if (ctrl_interface) + config->ctrl_interface = os_strdup(ctrl_interface); + if (driver_param) + config->driver_param = os_strdup(driver_param); + + return config; +} + + +#ifndef CONFIG_NO_STDOUT_DEBUG +/** + * wpa_config_debug_dump_networks - Debug dump of configured networks + * @config: Configuration data from wpa_config_read() + */ +void wpa_config_debug_dump_networks(struct wpa_config *config) +{ + int prio; + struct wpa_ssid *ssid; + + for (prio = 0; prio < config->num_prio; prio++) { + ssid = config->pssid[prio]; + wpa_printf(MSG_DEBUG, "Priority group %d", + ssid->priority); + while (ssid) { + wpa_printf(MSG_DEBUG, " id=%d ssid='%s'", + ssid->id, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + ssid = ssid->pnext; + } + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +struct global_parse_data { + char *name; + int (*parser)(const struct global_parse_data *data, + struct wpa_config *config, int line, const char *value); + void *param1, *param2, *param3; + unsigned int changed_flag; +}; + + +static int wpa_global_config_parse_int(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + int *dst; + dst = (int *) (((u8 *) config) + (long) data->param1); + *dst = atoi(pos); + wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst); + + if (data->param2 && *dst < (long) data->param2) { + wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " + "min_value=%ld)", line, data->name, *dst, + (long) data->param2); + *dst = (long) data->param2; + return -1; + } + + if (data->param3 && *dst > (long) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " + "max_value=%ld)", line, data->name, *dst, + (long) data->param3); + *dst = (long) data->param3; + return -1; + } + + return 0; +} + + +static int wpa_global_config_parse_str(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + size_t len; + char **dst, *tmp; + + len = os_strlen(pos); + if (data->param2 && len < (size_t) data->param2) { + wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " + "min_len=%ld)", line, data->name, + (unsigned long) len, (long) data->param2); + return -1; + } + + if (data->param3 && len > (size_t) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " + "max_len=%ld)", line, data->name, + (unsigned long) len, (long) data->param3); + return -1; + } + + tmp = os_strdup(pos); + if (tmp == NULL) + return -1; + + dst = (char **) (((u8 *) config) + (long) data->param1); + os_free(*dst); + *dst = tmp; + wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst); + + return 0; +} + + +static int wpa_config_process_country(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + if (!pos[0] || !pos[1]) { + wpa_printf(MSG_DEBUG, "Invalid country set"); + return -1; + } + config->country[0] = pos[0]; + config->country[1] = pos[1]; + wpa_printf(MSG_DEBUG, "country='%c%c'", + config->country[0], config->country[1]); + return 0; +} + + +static int wpa_config_process_load_dynamic_eap( + const struct global_parse_data *data, struct wpa_config *config, + int line, const char *so) +{ + int ret; + wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so); + ret = eap_peer_method_load(so); + if (ret == -2) { + wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not " + "reloading."); + } else if (ret) { + wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP " + "method '%s'.", line, so); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_WPS + +static int wpa_config_process_uuid(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + char buf[40]; + if (uuid_str2bin(pos, config->uuid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line); + return -1; + } + uuid_bin2str(config->uuid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "uuid=%s", buf); + return 0; +} + + +static int wpa_config_process_device_type( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + return wps_dev_type_str2bin(pos, config->device_type); +} + + +static int wpa_config_process_os_version(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + if (hexstr2bin(pos, config->os_version, 4)) { + wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line); + return -1; + } + wpa_printf(MSG_DEBUG, "os_version=%08x", + WPA_GET_BE32(config->os_version)); + return 0; +} + +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P +static int wpa_config_process_sec_device_type( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int idx; + + if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) { + wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type " + "items", line); + return -1; + } + + idx = config->num_sec_device_types; + + if (wps_dev_type_str2bin(pos, config->sec_device_type[idx])) + return -1; + + config->num_sec_device_types++; + return 0; +} +#endif /* CONFIG_P2P */ + + +#ifdef OFFSET +#undef OFFSET +#endif /* OFFSET */ +/* OFFSET: Get offset of a variable within the wpa_config structure */ +#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v) + +#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL +#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL +#define _INT(f) #f, wpa_global_config_parse_int, OFFSET(f) +#define INT(f) _INT(f), NULL, NULL +#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max +#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f) +#define STR(f) _STR(f), NULL, NULL +#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max + +static const struct global_parse_data global_fields[] = { +#ifdef CONFIG_CTRL_IFACE + { STR(ctrl_interface), 0 }, + { STR(ctrl_interface_group), 0 } /* deprecated */, +#endif /* CONFIG_CTRL_IFACE */ + { INT_RANGE(eapol_version, 1, 2), 0 }, + { INT(ap_scan), 0 }, + { INT(fast_reauth), 0 }, + { STR(opensc_engine_path), 0 }, + { STR(pkcs11_engine_path), 0 }, + { STR(pkcs11_module_path), 0 }, + { STR(driver_param), 0 }, + { INT(dot11RSNAConfigPMKLifetime), 0 }, + { INT(dot11RSNAConfigPMKReauthThreshold), 0 }, + { INT(dot11RSNAConfigSATimeout), 0 }, +#ifndef CONFIG_NO_CONFIG_WRITE + { INT(update_config), 0 }, +#endif /* CONFIG_NO_CONFIG_WRITE */ + { FUNC_NO_VAR(load_dynamic_eap), 0 }, +#ifdef CONFIG_WPS + { FUNC(uuid), CFG_CHANGED_UUID }, + { STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME }, + { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING }, + { STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING }, + { STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING }, + { STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING }, + { FUNC(device_type), CFG_CHANGED_DEVICE_TYPE }, + { FUNC(os_version), CFG_CHANGED_OS_VERSION }, + { STR(config_methods), CFG_CHANGED_CONFIG_METHODS }, + { INT_RANGE(wps_cred_processing, 0, 2), 0 }, +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + { 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_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 }, +#endif /* CONFIG_P2P */ + { FUNC(country), CFG_CHANGED_COUNTRY }, + { INT(bss_max_count), 0 }, + { INT(bss_expiration_age), 0 }, + { INT(bss_expiration_scan_count), 0 }, + { INT_RANGE(filter_ssids, 0, 1), 0 }, + { INT(max_num_sta), 0 }, + { INT_RANGE(disassoc_low_ack, 0, 1), 0 } +}; + +#undef FUNC +#undef _INT +#undef INT +#undef INT_RANGE +#undef _STR +#undef STR +#undef STR_RANGE +#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) + + +int wpa_config_process_global(struct wpa_config *config, char *pos, int line) +{ + size_t i; + int ret = 0; + + for (i = 0; i < NUM_GLOBAL_FIELDS; i++) { + const struct global_parse_data *field = &global_fields[i]; + size_t flen = os_strlen(field->name); + if (os_strncmp(pos, field->name, flen) != 0 || + pos[flen] != '=') + continue; + + if (field->parser(field, config, line, pos + flen + 1)) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse '%s'.", line, pos); + ret = -1; + } + config->changed_parameters |= field->changed_flag; + break; + } + if (i == NUM_GLOBAL_FIELDS) { + if (line < 0) + return -1; + wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", + line, pos); + ret = -1; + } + + return ret; +} diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h new file mode 100644 index 0000000..c370362 --- /dev/null +++ b/wpa_supplicant/config.h @@ -0,0 +1,502 @@ +/* + * WPA Supplicant / Configuration file structures + * Copyright (c) 2003-2005, 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. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#define DEFAULT_EAPOL_VERSION 1 +#ifdef CONFIG_NO_SCAN_PROCESSING +#define DEFAULT_AP_SCAN 2 +#else /* CONFIG_NO_SCAN_PROCESSING */ +#define DEFAULT_AP_SCAN 1 +#endif /* CONFIG_NO_SCAN_PROCESSING */ +#define DEFAULT_FAST_REAUTH 1 +#define DEFAULT_P2P_GO_INTENT 7 +#define DEFAULT_P2P_INTRA_BSS 1 +#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 + +#include "config_ssid.h" +#include "wps/wps.h" + + +#define CFG_CHANGED_DEVICE_NAME BIT(0) +#define CFG_CHANGED_CONFIG_METHODS BIT(1) +#define CFG_CHANGED_DEVICE_TYPE BIT(2) +#define CFG_CHANGED_OS_VERSION BIT(3) +#define CFG_CHANGED_UUID BIT(4) +#define CFG_CHANGED_COUNTRY BIT(5) +#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6) +#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7) +#define CFG_CHANGED_WPS_STRING BIT(8) +#define CFG_CHANGED_P2P_INTRA_BSS BIT(9) +#define CFG_CHANGED_VENDOR_EXTENSION BIT(10) + +/** + * struct wpa_config - wpa_supplicant configuration data + * + * This data structure is presents the per-interface (radio) configuration + * data. In many cases, there is only one struct wpa_config instance, but if + * more than one network interface is being controlled, one instance is used + * for each. + */ +struct wpa_config { + /** + * ssid - Head of the global network list + * + * This is the head for the list of all the configured networks. + */ + struct wpa_ssid *ssid; + + /** + * pssid - Per-priority network lists (in priority order) + */ + struct wpa_ssid **pssid; + + /** + * num_prio - Number of different priorities used in the pssid lists + * + * This indicates how many per-priority network lists are included in + * pssid. + */ + int num_prio; + + /** + * eapol_version - IEEE 802.1X/EAPOL version number + * + * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which + * defines EAPOL version 2. However, there are many APs that do not + * handle the new version number correctly (they seem to drop the + * frames completely). In order 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). + */ + int eapol_version; + + /** + * ap_scan - AP scanning/selection + * + * By default, wpa_supplicant requests driver to perform AP + * scanning and then uses the scan results to select a + * suitable AP. Another alternative is to allow the driver to + * take care of AP scanning and selection and use + * wpa_supplicant just to process EAPOL frames based on IEEE + * 802.11 association information from the driver. + * + * 1: wpa_supplicant initiates scanning and AP selection (default). + * + * 0: Driver takes care of scanning, AP selection, and IEEE 802.11 + * association parameters (e.g., WPA IE generation); this mode can + * also be used with non-WPA drivers when using IEEE 802.1X mode; + * do not try to associate with APs (i.e., external program needs + * to control association). This mode must also be used when using + * wired Ethernet drivers. + * + * 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, the network blocks in the configuration are tried + * one by one until the driver reports successful association; each + * network block should have explicit security policy (i.e., only one + * option in the lists) for key_mgmt, pairwise, group, proto variables. + */ + int ap_scan; + + /** + * ctrl_interface - Parameters for the control interface + * + * If this is specified, %wpa_supplicant will open a control interface + * that is available for external programs to manage %wpa_supplicant. + * The meaning of this string depends on which control interface + * mechanism is used. For all cases, the existance of this parameter + * in configuration is used to determine whether the control interface + * is enabled. + * + * For UNIX domain sockets (default on Linux and BSD): This is a + * directory that will be created for UNIX domain sockets for listening + * to requests from external programs (CLI/GUI, etc.) for status + * information and configuration. The socket file will be named based + * on the interface name, so multiple %wpa_supplicant processes can be + * run at the same time if more than one interface is used. + * /var/run/wpa_supplicant is the recommended directory for sockets and + * by default, wpa_cli will use it when trying to connect with + * %wpa_supplicant. + * + * Access control for the control interface can be configured + * by setting the directory to allow only members of a group + * to use sockets. This way, it is possible to run + * %wpa_supplicant as root (since it needs to change network + * configuration and open raw sockets) and still allow GUI/CLI + * components to be run as non-root users. However, since the + * control interface can be used to change the network + * configuration, this access needs to be protected in many + * cases. By default, %wpa_supplicant is configured to use gid + * 0 (root). If you want to allow non-root users to use the + * control interface, add a new group and change this value to + * match with that group. Add users that should have control + * interface access to this group. + * + * When configuring both the directory and group, use following format: + * DIR=/var/run/wpa_supplicant GROUP=wheel + * DIR=/var/run/wpa_supplicant GROUP=0 + * (group can be either group name or gid) + * + * For UDP connections (default on Windows): The value will be ignored. + * This variable is just used to select that the control interface is + * to be created. The value can be set to, e.g., udp + * (ctrl_interface=udp). + * + * For Windows Named Pipe: This value can be used to set the security + * descriptor for controlling access to the control interface. Security + * descriptor can be set using Security Descriptor String Format (see + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp). + * The descriptor string needs to be prefixed with SDDL=. For example, + * ctrl_interface=SDDL=D: would set an empty DACL (which will reject + * all connections). + */ + char *ctrl_interface; + + /** + * ctrl_interface_group - Control interface group (DEPRECATED) + * + * This variable is only used for backwards compatibility. Group for + * UNIX domain sockets should now be specified using GROUP=group in + * ctrl_interface variable. + */ + char *ctrl_interface_group; + + /** + * fast_reauth - EAP fast re-authentication (session resumption) + * + * By default, fast re-authentication is enabled for all EAP methods + * that support it. This variable can be used to disable fast + * re-authentication (by setting fast_reauth=0). Normally, there is no + * need to disable fast re-authentication. + */ + int fast_reauth; + + /** + * opensc_engine_path - Path to the OpenSSL engine for opensc + * + * This is an OpenSSL specific configuration option for loading OpenSC + * engine (engine_opensc.so); if %NULL, this engine is not loaded. + */ + char *opensc_engine_path; + + /** + * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11 + * + * This is an OpenSSL specific configuration option for loading PKCS#11 + * engine (engine_pkcs11.so); if %NULL, this engine is not loaded. + */ + char *pkcs11_engine_path; + + /** + * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module + * + * This is an OpenSSL specific configuration option for configuring + * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this + * module is not loaded. + */ + char *pkcs11_module_path; + + /** + * driver_param - Driver interface parameters + * + * This text string is passed to the selected driver interface with the + * optional struct wpa_driver_ops::set_param() handler. This can be + * used to configure driver specific options without having to add new + * driver interface functionality. + */ + char *driver_param; + + /** + * dot11RSNAConfigPMKLifetime - Maximum lifetime of a PMK + * + * dot11 MIB variable for the maximum lifetime of a PMK in the PMK + * cache (unit: seconds). + */ + unsigned int dot11RSNAConfigPMKLifetime; + + /** + * dot11RSNAConfigPMKReauthThreshold - PMK re-authentication threshold + * + * dot11 MIB variable for the percentage of the PMK lifetime + * that should expire before an IEEE 802.1X reauthentication occurs. + */ + unsigned int dot11RSNAConfigPMKReauthThreshold; + + /** + * dot11RSNAConfigSATimeout - Security association timeout + * + * dot11 MIB variable for the maximum time a security association + * shall take to set up (unit: seconds). + */ + unsigned int dot11RSNAConfigSATimeout; + + /** + * update_config - Is wpa_supplicant allowed to update configuration + * + * This variable control whether wpa_supplicant is allow to re-write + * its configuration with wpa_config_write(). If this is zero, + * configuration data is only changed in memory and the external data + * is not overriden. If this is non-zero, wpa_supplicant will update + * the configuration data (e.g., a file) whenever configuration is + * changed. This update may replace the old configuration which can + * remove comments from it in case of a text file configuration. + */ + int update_config; + + /** + * blobs - Configuration blobs + */ + struct wpa_config_blob *blobs; + + /** + * uuid - Universally Unique IDentifier (UUID; see RFC 4122) for WPS + */ + u8 uuid[16]; + + /** + * device_name - Device Name (WPS) + * User-friendly description of device; up to 32 octets encoded in + * UTF-8 + */ + char *device_name; + + /** + * manufacturer - Manufacturer (WPS) + * The manufacturer of the device (up to 64 ASCII characters) + */ + char *manufacturer; + + /** + * model_name - Model Name (WPS) + * Model of the device (up to 32 ASCII characters) + */ + char *model_name; + + /** + * model_number - Model Number (WPS) + * Additional device description (up to 32 ASCII characters) + */ + char *model_number; + + /** + * serial_number - Serial Number (WPS) + * Serial number of the device (up to 32 characters) + */ + char *serial_number; + + /** + * device_type - Primary Device Type (WPS) + */ + u8 device_type[WPS_DEV_TYPE_LEN]; + + /** + * config_methods - Config Methods + * + * This is a space-separated list of supported WPS configuration + * methods. For example, "label virtual_display virtual_push_button + * keypad". + * Available methods: usba ethernet label display ext_nfc_token + * int_nfc_token nfc_interface push_button keypad + * virtual_display physical_display + * virtual_push_button physical_push_button. + */ + char *config_methods; + + /** + * os_version - OS Version (WPS) + * 4-octet operating system version number + */ + u8 os_version[4]; + + /** + * country - Country code + * + * This is the ISO/IEC alpha2 country code for which we are operating + * in + */ + char country[2]; + + /** + * wps_cred_processing - Credential processing + * + * 0 = process received credentials internally + * 1 = do not process received credentials; just pass them over + * ctrl_iface to external program(s) + * 2 = process received credentials internally and pass them over + * ctrl_iface to external program(s) + */ + int wps_cred_processing; + +#define MAX_SEC_DEVICE_TYPES 5 + /** + * sec_device_types - Secondary Device Types (P2P) + */ + u8 sec_device_type[MAX_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN]; + int num_sec_device_types; + + int p2p_listen_reg_class; + int p2p_listen_channel; + int p2p_oper_reg_class; + int p2p_oper_channel; + int p2p_go_intent; + char *p2p_ssid_postfix; + int persistent_reconnect; + int p2p_intra_bss; + +#define MAX_WPS_VENDOR_EXT 10 + /** + * wps_vendor_ext - Vendor extension attributes in WPS + */ + struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT]; + + /** + * p2p_group_idle - Maximum idle time in seconds for P2P group + * + * This value controls how long a P2P group is maintained after there + * is no other members in the group. As a GO, this means no associated + * stations in the group. As a P2P client, this means no GO seen in + * scan results. The maximum idle time is specified in seconds with 0 + * indicating no time limit, i.e., the P2P group remains in active + * state indefinitely until explicitly removed. + */ + unsigned int p2p_group_idle; + + /** + * bss_max_count - Maximum number of BSS entries to keep in memory + */ + unsigned int bss_max_count; + + /** + * bss_expiration_age - BSS entry age after which it can be expired + * + * This value controls the time in seconds after which a BSS entry + * gets removed if it has not been updated or is not in use. + */ + unsigned int bss_expiration_age; + + /** + * bss_expiration_scan_count - Expire BSS after number of scans + * + * If the BSS entry has not been seen in this many scans, it will be + * removed. A value of 1 means that entry is removed after the first + * scan in which the BSSID is not seen. Larger values can be used + * to avoid BSS entries disappearing if they are not visible in + * every scan (e.g., low signal quality or interference). + */ + unsigned int bss_expiration_scan_count; + + /** + * filter_ssids - SSID-based scan result filtering + * + * 0 = do not filter scan results + * 1 = only include configured SSIDs in scan results/BSS table + */ + int filter_ssids; + + /** + * max_num_sta - Maximum number of STAs in an AP/P2P GO + */ + unsigned int max_num_sta; + + /** + * changed_parameters - Bitmap of changed parameters since last update + */ + unsigned int changed_parameters; + + /** + * disassoc_low_ack - Disassocicate stations with massive packet loss + */ + int disassoc_low_ack; +}; + + +/* Prototypes for common functions from config.c */ + +void wpa_config_free(struct wpa_config *ssid); +void wpa_config_free_ssid(struct wpa_ssid *ssid); +void wpa_config_foreach_network(struct wpa_config *config, + void (*func)(void *, struct wpa_ssid *), + void *arg); +struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id); +struct wpa_ssid * wpa_config_add_network(struct wpa_config *config); +int wpa_config_remove_network(struct wpa_config *config, int id); +void wpa_config_set_network_defaults(struct wpa_ssid *ssid); +int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value, + int line); +char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys); +char * wpa_config_get(struct wpa_ssid *ssid, const char *var); +char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var); +void wpa_config_update_psk(struct wpa_ssid *ssid); +int wpa_config_add_prio_network(struct wpa_config *config, + struct wpa_ssid *ssid); +int wpa_config_update_prio_list(struct wpa_config *config); +const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config, + const char *name); +void wpa_config_set_blob(struct wpa_config *config, + 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); + +struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, + const char *driver_param); +#ifndef CONFIG_NO_STDOUT_DEBUG +void wpa_config_debug_dump_networks(struct wpa_config *config); +#else /* CONFIG_NO_STDOUT_DEBUG */ +#define wpa_config_debug_dump_networks(c) do { } while (0) +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +/* Prototypes for common functions from config.c */ +int wpa_config_process_global(struct wpa_config *config, char *pos, int line); + + +/* Prototypes for backend specific functions from the selected config_*.c */ + +/** + * wpa_config_read - Read and parse configuration database + * @name: Name of the configuration (e.g., path and file name for the + * configuration file) + * Returns: Pointer to allocated configuration data or %NULL on failure + * + * This function reads configuration data, parses its contents, and allocates + * data structures needed for storing configuration information. The allocated + * data can be freed with wpa_config_free(). + * + * Each configuration backend needs to implement this function. + */ +struct wpa_config * wpa_config_read(const char *name); + +/** + * wpa_config_write - Write or update configuration data + * @name: Name of the configuration (e.g., path and file name for the + * configuration file) + * @config: Configuration data from wpa_config_read() + * Returns: 0 on success, -1 on failure + * + * This function write all configuration data into an external database (e.g., + * a text file) in a format that can be read with wpa_config_read(). This can + * be used to allow wpa_supplicant to update its configuration, e.g., when a + * new network is added or a password is changed. + * + * Each configuration backend needs to implement this function. + */ +int wpa_config_write(const char *name, struct wpa_config *config); + +#endif /* CONFIG_H */ diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c new file mode 100644 index 0000000..2d5fdd6 --- /dev/null +++ b/wpa_supplicant/config_file.c @@ -0,0 +1,755 @@ +/* + * WPA Supplicant / Configuration backend: text file + * Copyright (c) 2003-2008, 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 file implements a configuration backend for text files. All the + * configuration information is stored in a text file that uses a format + * described in the sample configuration file, wpa_supplicant.conf. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "base64.h" +#include "uuid.h" + + +/** + * wpa_config_get_line - Read the next configuration file line + * @s: Buffer for the line + * @size: The buffer length + * @stream: File stream to read from + * @line: Pointer to a variable storing the file line number + * @_pos: Buffer for the pointer to the beginning of data on the text line or + * %NULL if not needed (returned value used instead) + * Returns: Pointer to the beginning of data on the text line or %NULL if no + * more text lines are available. + * + * This function reads the next non-empty line from the configuration file and + * removes comments. The returned string is guaranteed to be null-terminated. + */ +static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line, + char **_pos) +{ + char *pos, *end, *sstart; + + while (fgets(s, size, stream)) { + (*line)++; + s[size - 1] = '\0'; + pos = s; + + /* Skip white space from the beginning of line. */ + while (*pos == ' ' || *pos == '\t' || *pos == '\r') + pos++; + + /* Skip comment lines and empty lines */ + if (*pos == '#' || *pos == '\n' || *pos == '\0') + continue; + + /* + * Remove # comments unless they are within a double quoted + * string. + */ + sstart = os_strchr(pos, '"'); + if (sstart) + sstart = os_strrchr(sstart + 1, '"'); + if (!sstart) + sstart = pos; + end = os_strchr(sstart, '#'); + if (end) + *end-- = '\0'; + else + end = pos + os_strlen(pos) - 1; + + /* Remove trailing white space. */ + while (end > pos && + (*end == '\n' || *end == ' ' || *end == '\t' || + *end == '\r')) + *end-- = '\0'; + + if (*pos == '\0') + continue; + + if (_pos) + *_pos = pos; + return pos; + } + + if (_pos) + *_pos = NULL; + return NULL; +} + + +static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) +{ + int errors = 0; + + if (ssid->passphrase) { + if (ssid->psk_set) { + wpa_printf(MSG_ERROR, "Line %d: both PSK and " + "passphrase configured.", line); + errors++; + } + wpa_config_update_psk(ssid); + } + + if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)) && + !ssid->psk_set) { + wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " + "management, but no PSK configured.", line); + errors++; + } + + if ((ssid->group_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { + /* Group cipher cannot be stronger than the pairwise cipher. */ + wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher" + " list since it was not allowed for pairwise " + "cipher", line); + ssid->group_cipher &= ~WPA_CIPHER_CCMP; + } + + return errors; +} + + +static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) +{ + struct wpa_ssid *ssid; + int errors = 0, end = 0; + char buf[256], *pos, *pos2; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", + *line); + ssid = os_zalloc(sizeof(*ssid)); + if (ssid == NULL) + return NULL; + ssid->id = id; + + wpa_config_set_network_defaults(ssid); + + while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { + if (os_strcmp(pos, "}") == 0) { + end = 1; + break; + } + + pos2 = os_strchr(pos, '='); + if (pos2 == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line " + "'%s'.", *line, pos); + errors++; + continue; + } + + *pos2++ = '\0'; + if (*pos2 == '"') { + if (os_strchr(pos2 + 1, '"') == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "quotation '%s'.", *line, pos2); + errors++; + continue; + } + } + + if (wpa_config_set(ssid, pos, pos2, *line) < 0) + errors++; + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: network block was not " + "terminated properly.", *line); + errors++; + } + + errors += wpa_config_validate_network(ssid, *line); + + if (errors) { + wpa_config_free_ssid(ssid); + ssid = NULL; + } + + return ssid; +} + + +#ifndef CONFIG_NO_CONFIG_BLOBS +static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line, + const char *name) +{ + struct wpa_config_blob *blob; + char buf[256], *pos; + unsigned char *encoded = NULL, *nencoded; + int end = 0; + size_t encoded_len = 0, len; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'", + *line, name); + + while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { + if (os_strcmp(pos, "}") == 0) { + end = 1; + break; + } + + len = os_strlen(pos); + nencoded = os_realloc(encoded, encoded_len + len); + if (nencoded == NULL) { + wpa_printf(MSG_ERROR, "Line %d: not enough memory for " + "blob", *line); + os_free(encoded); + return NULL; + } + encoded = nencoded; + os_memcpy(encoded + encoded_len, pos, len); + encoded_len += len; + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: blob was not terminated " + "properly", *line); + os_free(encoded); + return NULL; + } + + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) { + os_free(encoded); + return NULL; + } + blob->name = os_strdup(name); + blob->data = base64_decode(encoded, encoded_len, &blob->len); + os_free(encoded); + + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + return NULL; + } + + return blob; +} + + +static int wpa_config_process_blob(struct wpa_config *config, FILE *f, + int *line, char *bname) +{ + char *name_end; + struct wpa_config_blob *blob; + + name_end = os_strchr(bname, '='); + if (name_end == NULL) { + wpa_printf(MSG_ERROR, "Line %d: no blob name terminator", + *line); + return -1; + } + *name_end = '\0'; + + blob = wpa_config_read_blob(f, line, bname); + if (blob == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to read blob %s", + *line, bname); + return -1; + } + wpa_config_set_blob(config, blob); + return 0; +} +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +struct wpa_config * wpa_config_read(const char *name) +{ + FILE *f; + char buf[256], *pos; + int errors = 0, line = 0; + struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + struct wpa_config *config; + int id = 0; + + config = wpa_config_alloc_empty(NULL, NULL); + if (config == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); + f = fopen(name, "r"); + if (f == NULL) { + os_free(config); + return NULL; + } + + while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) { + if (os_strcmp(pos, "network={") == 0) { + ssid = wpa_config_read_network(f, &line, id++); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse network block.", line); + errors++; + continue; + } + if (head == NULL) { + head = tail = ssid; + } else { + tail->next = ssid; + tail = ssid; + } + if (wpa_config_add_prio_network(config, ssid)) { + wpa_printf(MSG_ERROR, "Line %d: failed to add " + "network block to priority list.", + line); + errors++; + continue; + } +#ifndef CONFIG_NO_CONFIG_BLOBS + } else if (os_strncmp(pos, "blob-base64-", 12) == 0) { + if (wpa_config_process_blob(config, f, &line, pos + 12) + < 0) { + errors++; + continue; + } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + } else if (wpa_config_process_global(config, pos, line) < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " + "line '%s'.", line, pos); + errors++; + continue; + } + } + + fclose(f); + + config->ssid = head; + wpa_config_debug_dump_networks(config); + +#ifndef WPA_IGNORE_CONFIG_ERRORS + if (errors) { + wpa_config_free(config); + config = NULL; + head = NULL; + } +#endif /* WPA_IGNORE_CONFIG_ERRORS */ + + return config; +} + + +#ifndef CONFIG_NO_CONFIG_WRITE + +static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, field); + if (value == NULL) + return; + fprintf(f, "\t%s=%s\n", field, value); + os_free(value); +} + + +static void write_int(FILE *f, const char *field, int value, int def) +{ + if (value == def) + return; + fprintf(f, "\t%s=%d\n", field, value); +} + + +static void write_bssid(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "bssid"); + if (value == NULL) + return; + fprintf(f, "\tbssid=%s\n", value); + os_free(value); +} + + +static void write_psk(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "psk"); + if (value == NULL) + return; + fprintf(f, "\tpsk=%s\n", value); + os_free(value); +} + + +static void write_proto(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->proto == DEFAULT_PROTO) + return; + + value = wpa_config_get(ssid, "proto"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tproto=%s\n", value); + os_free(value); +} + + +static void write_key_mgmt(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->key_mgmt == DEFAULT_KEY_MGMT) + return; + + value = wpa_config_get(ssid, "key_mgmt"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tkey_mgmt=%s\n", value); + os_free(value); +} + + +static void write_pairwise(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->pairwise_cipher == DEFAULT_PAIRWISE) + return; + + value = wpa_config_get(ssid, "pairwise"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tpairwise=%s\n", value); + os_free(value); +} + + +static void write_group(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->group_cipher == DEFAULT_GROUP) + return; + + value = wpa_config_get(ssid, "group"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tgroup=%s\n", value); + os_free(value); +} + + +static void write_auth_alg(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->auth_alg == 0) + return; + + value = wpa_config_get(ssid, "auth_alg"); + if (value == NULL) + return; + if (value[0]) + fprintf(f, "\tauth_alg=%s\n", value); + os_free(value); +} + + +#ifdef IEEE8021X_EAPOL +static void write_eap(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + value = wpa_config_get(ssid, "eap"); + if (value == NULL) + return; + + if (value[0]) + fprintf(f, "\teap=%s\n", value); + os_free(value); +} +#endif /* IEEE8021X_EAPOL */ + + +static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) +{ + char field[20], *value; + int res; + + res = os_snprintf(field, sizeof(field), "wep_key%d", idx); + if (res < 0 || (size_t) res >= sizeof(field)) + return; + value = wpa_config_get(ssid, field); + if (value) { + fprintf(f, "\t%s=%s\n", field, value); + os_free(value); + } +} + + +static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) +{ + int i; + +#define STR(t) write_str(f, #t, ssid) +#define INT(t) write_int(f, #t, ssid->t, 0) +#define INTe(t) write_int(f, #t, ssid->eap.t, 0) +#define INT_DEF(t, def) write_int(f, #t, ssid->t, def) +#define INT_DEFe(t, def) write_int(f, #t, ssid->eap.t, def) + + STR(ssid); + INT(scan_ssid); + write_bssid(f, ssid); + write_psk(f, ssid); + write_proto(f, ssid); + write_key_mgmt(f, ssid); + write_pairwise(f, ssid); + write_group(f, ssid); + write_auth_alg(f, ssid); +#ifdef IEEE8021X_EAPOL + write_eap(f, ssid); + STR(identity); + STR(anonymous_identity); + STR(password); + STR(ca_cert); + STR(ca_path); + STR(client_cert); + STR(private_key); + STR(private_key_passwd); + STR(dh_file); + STR(subject_match); + STR(altsubject_match); + STR(ca_cert2); + STR(ca_path2); + STR(client_cert2); + STR(private_key2); + STR(private_key2_passwd); + STR(dh_file2); + STR(subject_match2); + STR(altsubject_match2); + STR(phase1); + STR(phase2); + STR(pcsc); + STR(pin); + STR(engine_id); + STR(key_id); + STR(cert_id); + STR(ca_cert_id); + STR(key2_id); + STR(pin2); + STR(engine2_id); + STR(cert2_id); + STR(ca_cert2_id); + INTe(engine); + INTe(engine2); + INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); +#endif /* IEEE8021X_EAPOL */ + for (i = 0; i < 4; i++) + write_wep_key(f, i, ssid); + INT(wep_tx_keyidx); + INT(priority); +#ifdef IEEE8021X_EAPOL + INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); + STR(pac_file); + INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); +#endif /* IEEE8021X_EAPOL */ + INT(mode); + INT(proactive_key_caching); + INT(disabled); + INT(peerkey); +#ifdef CONFIG_IEEE80211W + INT(ieee80211w); +#endif /* CONFIG_IEEE80211W */ + STR(id_str); + +#undef STR +#undef INT +#undef INT_DEF +} + + +#ifndef CONFIG_NO_CONFIG_BLOBS +static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob) +{ + unsigned char *encoded; + + encoded = base64_encode(blob->data, blob->len, NULL); + if (encoded == NULL) + return -1; + + fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded); + os_free(encoded); + return 0; +} +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +static void wpa_config_write_global(FILE *f, struct wpa_config *config) +{ +#ifdef CONFIG_CTRL_IFACE + if (config->ctrl_interface) + fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface); + if (config->ctrl_interface_group) + fprintf(f, "ctrl_interface_group=%s\n", + config->ctrl_interface_group); +#endif /* CONFIG_CTRL_IFACE */ + if (config->eapol_version != DEFAULT_EAPOL_VERSION) + fprintf(f, "eapol_version=%d\n", config->eapol_version); + if (config->ap_scan != DEFAULT_AP_SCAN) + fprintf(f, "ap_scan=%d\n", config->ap_scan); + if (config->fast_reauth != DEFAULT_FAST_REAUTH) + fprintf(f, "fast_reauth=%d\n", config->fast_reauth); + if (config->opensc_engine_path) + fprintf(f, "opensc_engine_path=%s\n", + config->opensc_engine_path); + if (config->pkcs11_engine_path) + fprintf(f, "pkcs11_engine_path=%s\n", + config->pkcs11_engine_path); + if (config->pkcs11_module_path) + fprintf(f, "pkcs11_module_path=%s\n", + config->pkcs11_module_path); + if (config->driver_param) + fprintf(f, "driver_param=%s\n", config->driver_param); + if (config->dot11RSNAConfigPMKLifetime) + fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n", + config->dot11RSNAConfigPMKLifetime); + if (config->dot11RSNAConfigPMKReauthThreshold) + fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n", + config->dot11RSNAConfigPMKReauthThreshold); + if (config->dot11RSNAConfigSATimeout) + fprintf(f, "dot11RSNAConfigSATimeout=%d\n", + config->dot11RSNAConfigSATimeout); + if (config->update_config) + fprintf(f, "update_config=%d\n", config->update_config); +#ifdef CONFIG_WPS + if (!is_nil_uuid(config->uuid)) { + char buf[40]; + uuid_bin2str(config->uuid, buf, sizeof(buf)); + fprintf(f, "uuid=%s\n", buf); + } + if (config->device_name) + fprintf(f, "device_name=%s\n", config->device_name); + if (config->manufacturer) + fprintf(f, "manufacturer=%s\n", config->manufacturer); + if (config->model_name) + fprintf(f, "model_name=%s\n", config->model_name); + if (config->model_number) + fprintf(f, "model_number=%s\n", config->model_number); + if (config->serial_number) + fprintf(f, "serial_number=%s\n", config->serial_number); + { + char _buf[WPS_DEV_TYPE_BUFSIZE], *buf; + buf = wps_dev_type_bin2str(config->device_type, + _buf, sizeof(_buf)); + fprintf(f, "device_type=%s\n", buf); + } + if (WPA_GET_BE32(config->os_version)) + fprintf(f, "os_version=%08x\n", + WPA_GET_BE32(config->os_version)); + if (config->config_methods) + fprintf(f, "config_methods=%s\n", config->config_methods); + if (config->wps_cred_processing) + fprintf(f, "wps_cred_processing=%d\n", + config->wps_cred_processing); +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (config->p2p_listen_reg_class) + fprintf(f, "p2p_listen_reg_class=%u\n", + config->p2p_listen_reg_class); + if (config->p2p_listen_channel) + fprintf(f, "p2p_listen_channel=%u\n", + config->p2p_listen_channel); + if (config->p2p_oper_reg_class) + fprintf(f, "p2p_oper_reg_class=%u\n", + config->p2p_oper_reg_class); + if (config->p2p_oper_channel) + fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel); + if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT) + fprintf(f, "p2p_go_intent=%u\n", config->p2p_go_intent); + if (config->p2p_ssid_postfix) + fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix); + if (config->persistent_reconnect) + fprintf(f, "persistent_reconnect=%u\n", + config->persistent_reconnect); + if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS) + 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); +#endif /* CONFIG_P2P */ + if (config->country[0] && config->country[1]) { + fprintf(f, "country=%c%c\n", + config->country[0], config->country[1]); + } + if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT) + fprintf(f, "bss_max_count=%u\n", config->bss_max_count); + if (config->bss_expiration_age != DEFAULT_BSS_EXPIRATION_AGE) + fprintf(f, "bss_expiration_age=%u\n", + config->bss_expiration_age); + if (config->bss_expiration_scan_count != + DEFAULT_BSS_EXPIRATION_SCAN_COUNT) + fprintf(f, "bss_expiration_scan_count=%u\n", + config->bss_expiration_scan_count); + if (config->filter_ssids) + fprintf(f, "filter_ssids=%d\n", config->filter_ssids); + if (config->max_num_sta != DEFAULT_MAX_NUM_STA) + fprintf(f, "max_num_sta=%u\n", config->max_num_sta); + if (config->disassoc_low_ack) + fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack); +} + +#endif /* CONFIG_NO_CONFIG_WRITE */ + + +int wpa_config_write(const char *name, struct wpa_config *config) +{ +#ifndef CONFIG_NO_CONFIG_WRITE + FILE *f; + struct wpa_ssid *ssid; +#ifndef CONFIG_NO_CONFIG_BLOBS + struct wpa_config_blob *blob; +#endif /* CONFIG_NO_CONFIG_BLOBS */ + int ret = 0; + + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + + f = fopen(name, "w"); + if (f == NULL) { + wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name); + return -1; + } + + wpa_config_write_global(f, config); + + for (ssid = config->ssid; ssid; ssid = ssid->next) { + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary) + continue; /* do not save temporary networks */ + fprintf(f, "\nnetwork={\n"); + wpa_config_write_network(f, ssid); + fprintf(f, "}\n"); + } + +#ifndef CONFIG_NO_CONFIG_BLOBS + for (blob = config->blobs; blob; blob = blob->next) { + ret = wpa_config_write_blob(f, blob); + if (ret) + break; + } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + fclose(f); + + wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", + name, ret ? "un" : ""); + return ret; +#else /* CONFIG_NO_CONFIG_WRITE */ + return -1; +#endif /* CONFIG_NO_CONFIG_WRITE */ +} diff --git a/wpa_supplicant/config_none.c b/wpa_supplicant/config_none.c new file mode 100644 index 0000000..2e9ccc0 --- /dev/null +++ b/wpa_supplicant/config_none.c @@ -0,0 +1,57 @@ +/* + * WPA Supplicant / Configuration backend: empty starting point + * Copyright (c) 2003-2005, 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 file implements dummy example of a configuration backend. None of the + * functions are actually implemented so this can be used as a simple + * compilation test or a starting point for a new configuration backend. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "base64.h" + + +struct wpa_config * wpa_config_read(const char *name) +{ + struct wpa_config *config; + + config = wpa_config_alloc_empty(NULL, NULL); + if (config == NULL) + return NULL; + /* TODO: fill in configuration data */ + return config; +} + + +int wpa_config_write(const char *name, struct wpa_config *config) +{ + struct wpa_ssid *ssid; + struct wpa_config_blob *blob; + + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + + /* TODO: write global config parameters */ + + + for (ssid = config->ssid; ssid; ssid = ssid->next) { + /* TODO: write networks */ + } + + for (blob = config->blobs; blob; blob = blob->next) { + /* TODO: write blobs */ + } + + return 0; +} diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h new file mode 100644 index 0000000..8419f43 --- /dev/null +++ b/wpa_supplicant/config_ssid.h @@ -0,0 +1,413 @@ +/* + * WPA Supplicant / Network configuration structures + * Copyright (c) 2003-2008, 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. + */ + +#ifndef CONFIG_SSID_H +#define CONFIG_SSID_H + +#include "common/defs.h" +#include "eap_peer/eap_config.h" + +#define MAX_SSID_LEN 32 + + +#define DEFAULT_EAP_WORKAROUND ((unsigned int) -1) +#define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \ + EAPOL_FLAG_REQUIRE_KEY_BROADCAST) +#define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN) +#define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X) +#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP) +#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | \ + WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) +#define DEFAULT_FRAGMENT_SIZE 1398 + +/** + * struct wpa_ssid - Network configuration data + * + * This structure includes all the configuration variables for a network. This + * data is included in the per-interface configuration data as an element of + * the network list, struct wpa_config::ssid. Each network block in the + * configuration is mapped to a struct wpa_ssid instance. + */ +struct wpa_ssid { + /** + * next - Next network in global list + * + * This pointer can be used to iterate over all networks. The head of + * this list is stored in the ssid field of struct wpa_config. + */ + struct wpa_ssid *next; + + /** + * pnext - Next network in per-priority list + * + * This pointer can be used to iterate over all networks in the same + * priority class. The heads of these list are stored in the pssid + * fields of struct wpa_config. + */ + struct wpa_ssid *pnext; + + /** + * id - Unique id for the network + * + * This identifier is used as a unique identifier for each network + * block when using the control interface. Each network is allocated an + * id when it is being created, either when reading the configuration + * file or when a new network is added through the control interface. + */ + int id; + + /** + * priority - Priority group + * + * By default, all networks will get same priority group (0). If some + * of the networks are more desirable, this field can be used to change + * the order in which wpa_supplicant goes through the networks when + * selecting a BSS. The priority groups will be iterated in decreasing + * priority (i.e., the larger the priority value, the sooner the + * network is matched against the scan results). Within each priority + * group, networks will be selected based on security policy, signal + * strength, etc. + * + * Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are + * not using this priority to select the order for scanning. Instead, + * they try the networks in the order that used in the configuration + * file. + */ + int priority; + + /** + * ssid - Service set identifier (network name) + * + * This is the SSID for the network. For wireless interfaces, this is + * used to select which network will be used. If set to %NULL (or + * ssid_len=0), any SSID can be used. For wired interfaces, this must + * be set to %NULL. Note: SSID may contain any characters, even nul + * (ASCII 0) and as such, this should not be assumed to be a nul + * terminated string. ssid_len defines how many characters are valid + * and the ssid field is not guaranteed to be nul terminated. + */ + u8 *ssid; + + /** + * ssid_len - Length of the SSID + */ + size_t ssid_len; + + /** + * bssid - BSSID + * + * If set, this network block is used only when associating with the AP + * using the configured BSSID + * + * If this is a persistent P2P group (disabled == 2), this is the GO + * Device Address. + */ + u8 bssid[ETH_ALEN]; + + /** + * bssid_set - Whether BSSID is configured for this network + */ + int bssid_set; + + /** + * psk - WPA pre-shared key (256 bits) + */ + u8 psk[32]; + + /** + * psk_set - Whether PSK field is configured + */ + int psk_set; + + /** + * passphrase - WPA ASCII passphrase + * + * If this is set, psk will be generated using the SSID and passphrase + * configured for the network. ASCII passphrase must be between 8 and + * 63 characters (inclusive). + */ + char *passphrase; + + /** + * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_* + */ + int pairwise_cipher; + + /** + * group_cipher - Bitfield of allowed group ciphers, WPA_CIPHER_* + */ + int group_cipher; + + /** + * key_mgmt - Bitfield of allowed key management protocols + * + * WPA_KEY_MGMT_* + */ + int key_mgmt; + + /** + * proto - Bitfield of allowed protocols, WPA_PROTO_* + */ + int proto; + + /** + * auth_alg - Bitfield of allowed authentication algorithms + * + * WPA_AUTH_ALG_* + */ + int auth_alg; + + /** + * scan_ssid - Scan this SSID with Probe Requests + * + * scan_ssid can be used to scan for APs using hidden SSIDs. + * Note: Many drivers do not support this. ap_mode=2 can be used with + * such drivers to use hidden SSIDs. + */ + int scan_ssid; + +#ifdef IEEE8021X_EAPOL +#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0) +#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1) + /** + * eapol_flags - Bit field of IEEE 802.1X/EAPOL options (EAPOL_FLAG_*) + */ + int eapol_flags; + + /** + * eap - EAP peer configuration for this network + */ + struct eap_peer_config eap; +#endif /* IEEE8021X_EAPOL */ + +#define NUM_WEP_KEYS 4 +#define MAX_WEP_KEY_LEN 16 + /** + * wep_key - WEP keys + */ + u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN]; + + /** + * wep_key_len - WEP key lengths + */ + size_t wep_key_len[NUM_WEP_KEYS]; + + /** + * wep_tx_keyidx - Default key index for TX frames using WEP + */ + int wep_tx_keyidx; + + /** + * proactive_key_caching - Enable proactive key caching + * + * This field can be used to enable proactive key caching which is also + * known as opportunistic PMKSA caching for WPA2. This is disabled (0) + * by default. Enable by setting this to 1. + * + * Proactive key caching is used to make supplicant assume that the APs + * are using the same PMK and generate PMKSA cache entries without + * doing RSN pre-authentication. This requires support from the AP side + * and is normally used with wireless switches that co-locate the + * authenticator. + */ + int proactive_key_caching; + + /** + * mixed_cell - Whether mixed cells are allowed + * + * This option can be used to configure whether so called mixed cells, + * i.e., networks that use both plaintext and encryption in the same + * SSID, are allowed. This is disabled (0) by default. Enable by + * setting this to 1. + */ + int mixed_cell; + +#ifdef IEEE8021X_EAPOL + + /** + * leap - Number of EAP methods using LEAP + * + * This field should be set to 1 if LEAP is enabled. This is used to + * select IEEE 802.11 authentication algorithm. + */ + int leap; + + /** + * non_leap - Number of EAP methods not using LEAP + * + * This field should be set to >0 if any EAP method other than LEAP is + * enabled. This is used to select IEEE 802.11 authentication + * algorithm. + */ + int non_leap; + + /** + * eap_workaround - EAP workarounds enabled + * + * wpa_supplicant supports number of "EAP workarounds" to work around + * interoperability issues with incorrectly behaving authentication + * servers. This is recommended to be enabled by default because some + * of the issues are present in large number of authentication servers. + * + * Strict EAP conformance mode can be configured by disabling + * workarounds with eap_workaround = 0. + */ + unsigned int eap_workaround; + +#endif /* IEEE8021X_EAPOL */ + + /** + * mode - IEEE 802.11 operation mode (Infrastucture/IBSS) + * + * 0 = infrastructure (Managed) mode, i.e., associate with an AP. + * + * 1 = IBSS (ad-hoc, peer-to-peer) + * + * 2 = AP (access point) + * + * 3 = P2P Group Owner (can be set in the configuration file) + * + * 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). + */ + enum wpas_mode { + WPAS_MODE_INFRA = 0, + WPAS_MODE_IBSS = 1, + WPAS_MODE_AP = 2, + WPAS_MODE_P2P_GO = 3, + WPAS_MODE_P2P_GROUP_FORMATION = 4, + } mode; + + /** + * disabled - Whether this network is currently disabled + * + * 0 = this network can be used (default). + * 1 = this network block is disabled (can be enabled through + * ctrl_iface, e.g., with wpa_cli or wpa_gui). + * 2 = this network block includes parameters for a persistent P2P + * group (can be used with P2P ctrl_iface commands) + */ + int disabled; + + /** + * peerkey - Whether PeerKey handshake for direct links is allowed + * + * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are + * enabled. + * + * 0 = disabled (default) + * 1 = enabled + */ + int peerkey; + + /** + * id_str - Network identifier string for external scripts + * + * This value is passed to external ctrl_iface monitors in + * WPA_EVENT_CONNECTED event and wpa_cli sets this as WPA_ID_STR + * environment variable for action scripts. + */ + char *id_str; + +#ifdef CONFIG_IEEE80211W + /** + * ieee80211w - Whether management frame protection is enabled + * + * This value is used to configure policy for management frame + * protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required. + */ + enum mfp_options ieee80211w; +#endif /* CONFIG_IEEE80211W */ + + /** + * frequency - Channel frequency in megahertz (MHz) for IBSS + * + * This value is used to configure the initial channel for IBSS (adhoc) + * networks, e.g., 2412 = IEEE 802.11b/g channel 1. It is ignored in + * the infrastructure mode. In addition, this value is only used by the + * station that creates the IBSS. If an IBSS network with the + * configured SSID is already present, the frequency of the network + * will be used instead of this configured value. + */ + int frequency; + + /** + * wpa_ptk_rekey - Maximum lifetime for PTK in seconds + * + * This value can be used to enforce rekeying of PTK to mitigate some + * attacks against TKIP deficiencies. + */ + int wpa_ptk_rekey; + + /** + * scan_freq - Array of frequencies to scan or %NULL for all + * + * This is an optional zero-terminated array of frequencies in + * megahertz (MHz) to include in scan requests when searching for this + * network. This can be used to speed up scanning when the network is + * known to not use all possible channels. + */ + int *scan_freq; + + /** + * 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) in following format: + * <bgscan module name>:<module parameters> + */ + char *bgscan; + + /** + * freq_list - Array of allowed frequencies or %NULL for all + * + * This is an optional zero-terminated array of frequencies in + * megahertz (MHz) to allow for selecting the BSS. If set, scan results + * that do not match any of the specified frequencies are not + * considered when selecting a BSS. + */ + int *freq_list; + + /** + * p2p_group - Network generated as a P2P group (used internally) + */ + int p2p_group; + + /** + * p2p_persistent_group - Whether this is a persistent group + */ + int p2p_persistent_group; + + /** + * temporary - Whether this network is temporary and not to be saved + */ + int temporary; + + /** + * export_keys - Whether keys may be exported + * + * This attribute will be set when keys are determined through + * WPS or similar so that they may be exported. + */ + int export_keys; +}; + +#endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c new file mode 100644 index 0000000..ea3a2ac --- /dev/null +++ b/wpa_supplicant/config_winreg.c @@ -0,0 +1,1025 @@ +/* + * WPA Supplicant / Configuration backend: Windows registry + * Copyright (c) 2003-2008, 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 file implements a configuration backend for Windows registry. All the + * configuration information is stored in the registry and the format for + * network configuration fields is same as described in the sample + * configuration file, wpa_supplicant.conf. + * + * Configuration data is in + * \a HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant\\configs + * key. Each configuration profile has its own key under this. In terms of text + * files, each profile would map to a separate text file with possibly multiple + * networks. Under each profile, there is a networks key that lists all + * networks as a subkey. Each network has set of values in the same way as + * network block in the configuration file. In addition, blobs subkey has + * possible blobs as values. + * + * Example network configuration block: + * \verbatim +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000 + ssid="example" + key_mgmt=WPA-PSK +\endverbatim + */ + +#include "includes.h" + +#include "common.h" +#include "uuid.h" +#include "config.h" + +#ifndef WPA_KEY_ROOT +#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE +#endif +#ifndef WPA_KEY_PREFIX +#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant") +#endif + +#ifdef UNICODE +#define TSTR "%S" +#else /* UNICODE */ +#define TSTR "%s" +#endif /* UNICODE */ + + +static int wpa_config_read_blobs(struct wpa_config *config, HKEY hk) +{ + struct wpa_config_blob *blob; + int errors = 0; + HKEY bhk; + LONG ret; + DWORD i; + + ret = RegOpenKeyEx(hk, TEXT("blobs"), 0, KEY_QUERY_VALUE, &bhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config " + "blobs key"); + return 0; /* assume no blobs */ + } + + for (i = 0; ; i++) { +#define TNAMELEN 255 + TCHAR name[TNAMELEN]; + char data[4096]; + DWORD namelen, datalen, type; + + namelen = TNAMELEN; + datalen = sizeof(data); + ret = RegEnumValue(bhk, i, name, &namelen, NULL, &type, + (LPBYTE) data, &datalen); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumValue failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= TNAMELEN) + namelen = TNAMELEN - 1; + name[namelen] = TEXT('\0'); + wpa_unicode2ascii_inplace(name); + + if (datalen >= sizeof(data)) + datalen = sizeof(data) - 1; + + wpa_printf(MSG_MSGDUMP, "blob %d: field='%s' len %d", + (int) i, name, (int) datalen); + + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) { + errors++; + break; + } + blob->name = os_strdup((char *) name); + blob->data = os_malloc(datalen); + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + errors++; + break; + } + os_memcpy(blob->data, data, datalen); + blob->len = datalen; + + wpa_config_set_blob(config, blob); + } + + RegCloseKey(bhk); + + return errors ? -1 : 0; +} + + +static int wpa_config_read_reg_dword(HKEY hk, const TCHAR *name, int *_val) +{ + DWORD val, buflen; + LONG ret; + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + wpa_printf(MSG_DEBUG, TSTR "=%d", name, (int) val); + *_val = val; + return 0; + } + + return -1; +} + + +static char * wpa_config_read_reg_string(HKEY hk, const TCHAR *name) +{ + DWORD buflen; + LONG ret; + TCHAR *val; + + buflen = 0; + ret = RegQueryValueEx(hk, name, NULL, NULL, NULL, &buflen); + if (ret != ERROR_SUCCESS) + return NULL; + val = os_malloc(buflen); + if (val == NULL) + return NULL; + + ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) val, &buflen); + if (ret != ERROR_SUCCESS) { + os_free(val); + return NULL; + } + + wpa_unicode2ascii_inplace(val); + wpa_printf(MSG_DEBUG, TSTR "=%s", name, (char *) val); + return (char *) val; +} + + +#ifdef CONFIG_WPS +static int wpa_config_read_global_uuid(struct wpa_config *config, HKEY hk) +{ + char *str; + int ret = 0; + + str = wpa_config_read_reg_string(hk, TEXT("uuid")); + if (str == NULL) + return 0; + + if (uuid_str2bin(str, config->uuid)) + ret = -1; + + os_free(str); + + return ret; +} + + +static int wpa_config_read_global_os_version(struct wpa_config *config, + HKEY hk) +{ + char *str; + int ret = 0; + + str = wpa_config_read_reg_string(hk, TEXT("os_version")); + if (str == NULL) + return 0; + + if (hexstr2bin(str, config->os_version, 4)) + ret = -1; + + os_free(str); + + return ret; +} +#endif /* CONFIG_WPS */ + + +static int wpa_config_read_global(struct wpa_config *config, HKEY hk) +{ + int errors = 0; + + wpa_config_read_reg_dword(hk, TEXT("ap_scan"), &config->ap_scan); + wpa_config_read_reg_dword(hk, TEXT("fast_reauth"), + &config->fast_reauth); + wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"), + (int *) &config->dot11RSNAConfigPMKLifetime); + wpa_config_read_reg_dword(hk, + TEXT("dot11RSNAConfigPMKReauthThreshold"), + (int *) + &config->dot11RSNAConfigPMKReauthThreshold); + wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"), + (int *) &config->dot11RSNAConfigSATimeout); + wpa_config_read_reg_dword(hk, TEXT("update_config"), + &config->update_config); + + if (wpa_config_read_reg_dword(hk, TEXT("eapol_version"), + &config->eapol_version) == 0) { + if (config->eapol_version < 1 || + config->eapol_version > 2) { + wpa_printf(MSG_ERROR, "Invalid EAPOL version (%d)", + config->eapol_version); + errors++; + } + } + + config->ctrl_interface = wpa_config_read_reg_string( + hk, TEXT("ctrl_interface")); + +#ifdef CONFIG_WPS + if (wpa_config_read_global_uuid(config, hk)) + errors++; + config->device_name = wpa_config_read_reg_string( + hk, TEXT("device_name")); + config->manufacturer = wpa_config_read_reg_string( + hk, TEXT("manufacturer")); + config->model_name = wpa_config_read_reg_string( + hk, TEXT("model_name")); + config->serial_number = wpa_config_read_reg_string( + hk, TEXT("serial_number")); + { + char *t = wpa_config_read_reg_string( + hk, TEXT("device_type")); + if (t && wps_dev_type_str2bin(t, config->device_type)) + errors++; + os_free(t); + } + config->config_methods = wpa_config_read_reg_string( + hk, TEXT("config_methods")); + if (wpa_config_read_global_os_version(config, hk)) + errors++; + wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"), + &config->wps_cred_processing); +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + config->p2p_ssid_postfix = wpa_config_read_reg_string( + hk, TEXT("p2p_ssid_postfix")); + wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"), + (int *) &config->p2p_group_idle); +#endif /* CONFIG_P2P */ + + wpa_config_read_reg_dword(hk, TEXT("bss_max_count"), + (int *) &config->bss_max_count); + wpa_config_read_reg_dword(hk, TEXT("filter_ssids"), + &config->filter_ssids); + wpa_config_read_reg_dword(hk, TEXT("max_num_sta"), + (int *) &config->max_num_sta); + wpa_config_read_reg_dword(hk, TEXT("disassoc_low_ack"), + (int *) &config->disassoc_low_ack); + + return errors ? -1 : 0; +} + + +static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw, + int id) +{ + HKEY nhk; + LONG ret; + DWORD i; + struct wpa_ssid *ssid; + int errors = 0; + + ret = RegOpenKeyEx(hk, netw, 0, KEY_QUERY_VALUE, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config " + "network '" TSTR "'", netw); + return NULL; + } + + wpa_printf(MSG_MSGDUMP, "Start of a new network '" TSTR "'", netw); + ssid = os_zalloc(sizeof(*ssid)); + if (ssid == NULL) { + RegCloseKey(nhk); + return NULL; + } + ssid->id = id; + + wpa_config_set_network_defaults(ssid); + + for (i = 0; ; i++) { + TCHAR name[255], data[1024]; + DWORD namelen, datalen, type; + + namelen = 255; + datalen = sizeof(data); + ret = RegEnumValue(nhk, i, name, &namelen, NULL, &type, + (LPBYTE) data, &datalen); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "RegEnumValue failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = TEXT('\0'); + + if (datalen >= 1024) + datalen = 1024 - 1; + data[datalen] = TEXT('\0'); + + wpa_unicode2ascii_inplace(name); + wpa_unicode2ascii_inplace(data); + if (wpa_config_set(ssid, (char *) name, (char *) data, 0) < 0) + errors++; + } + + RegCloseKey(nhk); + + if (ssid->passphrase) { + if (ssid->psk_set) { + wpa_printf(MSG_ERROR, "Both PSK and passphrase " + "configured for network '" TSTR "'.", netw); + errors++; + } + wpa_config_update_psk(ssid); + } + + if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)) && + !ssid->psk_set) { + wpa_printf(MSG_ERROR, "WPA-PSK accepted for key management, " + "but no PSK configured for network '" TSTR "'.", + netw); + errors++; + } + + if ((ssid->group_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { + /* Group cipher cannot be stronger than the pairwise cipher. */ + wpa_printf(MSG_DEBUG, "Removed CCMP from group cipher " + "list since it was not allowed for pairwise " + "cipher for network '" TSTR "'.", netw); + ssid->group_cipher &= ~WPA_CIPHER_CCMP; + } + + if (errors) { + wpa_config_free_ssid(ssid); + ssid = NULL; + } + + return ssid; +} + + +static int wpa_config_read_networks(struct wpa_config *config, HKEY hk) +{ + HKEY nhk; + struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + int errors = 0; + LONG ret; + DWORD i; + + ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_ENUMERATE_SUB_KEYS, + &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant networks " + "registry key"); + return -1; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + + ssid = wpa_config_read_network(nhk, name, i); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "Failed to parse network " + "profile '%s'.", name); + errors++; + continue; + } + if (head == NULL) { + head = tail = ssid; + } else { + tail->next = ssid; + tail = ssid; + } + if (wpa_config_add_prio_network(config, ssid)) { + wpa_printf(MSG_ERROR, "Failed to add network profile " + "'%s' to priority list.", name); + errors++; + continue; + } + } + + RegCloseKey(nhk); + + config->ssid = head; + + return errors ? -1 : 0; +} + + +struct wpa_config * wpa_config_read(const char *name) +{ + TCHAR buf[256]; + int errors = 0; + struct wpa_config *config; + HKEY hk; + LONG ret; + + config = wpa_config_alloc_empty(NULL, NULL); + if (config == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "Reading configuration profile '%s'", name); + +#ifdef UNICODE + _snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name); +#else /* UNICODE */ + os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name); +#endif /* UNICODE */ + + ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant " + "configuration registry HKLM\\" TSTR, buf); + os_free(config); + return NULL; + } + + if (wpa_config_read_global(config, hk)) + errors++; + + if (wpa_config_read_networks(config, hk)) + errors++; + + if (wpa_config_read_blobs(config, hk)) + errors++; + + wpa_config_debug_dump_networks(config); + + RegCloseKey(hk); + + if (errors) { + wpa_config_free(config); + config = NULL; + } + + return config; +} + + +static int wpa_config_write_reg_dword(HKEY hk, const TCHAR *name, int val, + int def) +{ + LONG ret; + DWORD _val = val; + + if (val == def) { + RegDeleteValue(hk, name); + return 0; + } + + ret = RegSetValueEx(hk, name, 0, REG_DWORD, (LPBYTE) &_val, + sizeof(_val)); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set %s=%d: error %d", + name, val, (int) GetLastError()); + return -1; + } + + return 0; +} + + +static int wpa_config_write_reg_string(HKEY hk, const char *name, + const char *val) +{ + LONG ret; + TCHAR *_name, *_val; + + _name = wpa_strdup_tchar(name); + if (_name == NULL) + return -1; + + if (val == NULL) { + RegDeleteValue(hk, _name); + os_free(_name); + return 0; + } + + _val = wpa_strdup_tchar(val); + if (_val == NULL) { + os_free(_name); + return -1; + } + ret = RegSetValueEx(hk, _name, 0, REG_SZ, (BYTE *) _val, + (os_strlen(val) + 1) * sizeof(TCHAR)); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set %s='%s': " + "error %d", name, val, (int) GetLastError()); + os_free(_name); + os_free(_val); + return -1; + } + + os_free(_name); + os_free(_val); + return 0; +} + + +static int wpa_config_write_global(struct wpa_config *config, HKEY hk) +{ +#ifdef CONFIG_CTRL_IFACE + wpa_config_write_reg_string(hk, "ctrl_interface", + config->ctrl_interface); +#endif /* CONFIG_CTRL_IFACE */ + + wpa_config_write_reg_dword(hk, TEXT("eapol_version"), + config->eapol_version, + DEFAULT_EAPOL_VERSION); + wpa_config_write_reg_dword(hk, TEXT("ap_scan"), config->ap_scan, + DEFAULT_AP_SCAN); + wpa_config_write_reg_dword(hk, TEXT("fast_reauth"), + config->fast_reauth, DEFAULT_FAST_REAUTH); + wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"), + config->dot11RSNAConfigPMKLifetime, 0); + wpa_config_write_reg_dword(hk, + TEXT("dot11RSNAConfigPMKReauthThreshold"), + config->dot11RSNAConfigPMKReauthThreshold, + 0); + wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"), + config->dot11RSNAConfigSATimeout, 0); + wpa_config_write_reg_dword(hk, TEXT("update_config"), + config->update_config, + 0); +#ifdef CONFIG_WPS + if (!is_nil_uuid(config->uuid)) { + char buf[40]; + uuid_bin2str(config->uuid, buf, sizeof(buf)); + wpa_config_write_reg_string(hk, "uuid", buf); + } + wpa_config_write_reg_string(hk, "device_name", config->device_name); + wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer); + wpa_config_write_reg_string(hk, "model_name", config->model_name); + wpa_config_write_reg_string(hk, "model_number", config->model_number); + wpa_config_write_reg_string(hk, "serial_number", + config->serial_number); + { + char _buf[WPS_DEV_TYPE_BUFSIZE], *buf; + buf = wps_dev_type_bin2str(config->device_type, + _buf, sizeof(_buf)); + wpa_config_write_reg_string(hk, "device_type", buf); + } + wpa_config_write_reg_string(hk, "config_methods", + config->config_methods); + if (WPA_GET_BE32(config->os_version)) { + char vbuf[10]; + os_snprintf(vbuf, sizeof(vbuf), "%08x", + WPA_GET_BE32(config->os_version)); + wpa_config_write_reg_string(hk, "os_version", vbuf); + } + wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"), + config->wps_cred_processing, 0); +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + wpa_config_write_reg_string(hk, "p2p_ssid_postfix", + config->p2p_ssid_postfix); + wpa_config_write_reg_dword(hk, TEXT("p2p_group_idle"), + config->p2p_group_idle, 0); +#endif /* CONFIG_P2P */ + + wpa_config_write_reg_dword(hk, TEXT("bss_max_count"), + config->bss_max_count, + DEFAULT_BSS_MAX_COUNT); + wpa_config_write_reg_dword(hk, TEXT("filter_ssids"), + config->filter_ssids, 0); + wpa_config_write_reg_dword(hk, TEXT("max_num_sta"), + config->max_num_sta, DEFAULT_MAX_NUM_STA); + wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"), + config->disassoc_low_ack, 0); + + return 0; +} + + +static int wpa_config_delete_subkeys(HKEY hk, const TCHAR *key) +{ + HKEY nhk; + int i, errors = 0; + LONG ret; + + ret = RegOpenKeyEx(hk, key, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not open key '" TSTR + "' for subkey deletion: error 0x%x (%d)", key, + (unsigned int) ret, (int) GetLastError()); + return 0; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = TEXT('\0'); + + ret = RegDeleteKey(nhk, name); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegDeleteKey failed: 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + errors++; + } + } + + RegCloseKey(nhk); + + return errors ? -1 : 0; +} + + +static void write_str(HKEY hk, const char *field, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, field); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, field, value); + os_free(value); +} + + +static void write_int(HKEY hk, const char *field, int value, int def) +{ + char val[20]; + if (value == def) + return; + os_snprintf(val, sizeof(val), "%d", value); + wpa_config_write_reg_string(hk, field, val); +} + + +static void write_bssid(HKEY hk, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "bssid"); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, "bssid", value); + os_free(value); +} + + +static void write_psk(HKEY hk, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "psk"); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, "psk", value); + os_free(value); +} + + +static void write_proto(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->proto == DEFAULT_PROTO) + return; + + value = wpa_config_get(ssid, "proto"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "proto", value); + os_free(value); +} + + +static void write_key_mgmt(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->key_mgmt == DEFAULT_KEY_MGMT) + return; + + value = wpa_config_get(ssid, "key_mgmt"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "key_mgmt", value); + os_free(value); +} + + +static void write_pairwise(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->pairwise_cipher == DEFAULT_PAIRWISE) + return; + + value = wpa_config_get(ssid, "pairwise"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "pairwise", value); + os_free(value); +} + + +static void write_group(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->group_cipher == DEFAULT_GROUP) + return; + + value = wpa_config_get(ssid, "group"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "group", value); + os_free(value); +} + + +static void write_auth_alg(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->auth_alg == 0) + return; + + value = wpa_config_get(ssid, "auth_alg"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "auth_alg", value); + os_free(value); +} + + +#ifdef IEEE8021X_EAPOL +static void write_eap(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + value = wpa_config_get(ssid, "eap"); + if (value == NULL) + return; + + if (value[0]) + wpa_config_write_reg_string(hk, "eap", value); + os_free(value); +} +#endif /* IEEE8021X_EAPOL */ + + +static void write_wep_key(HKEY hk, int idx, struct wpa_ssid *ssid) +{ + char field[20], *value; + + os_snprintf(field, sizeof(field), "wep_key%d", idx); + value = wpa_config_get(ssid, field); + if (value) { + wpa_config_write_reg_string(hk, field, value); + os_free(value); + } +} + + +static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) +{ + int i, errors = 0; + HKEY nhk, netw; + LONG ret; + TCHAR name[5]; + + ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_CREATE_SUB_KEY, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not open networks key " + "for subkey addition: error 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + return 0; + } + +#ifdef UNICODE + wsprintf(name, L"%04d", id); +#else /* UNICODE */ + os_snprintf(name, sizeof(name), "%04d", id); +#endif /* UNICODE */ + ret = RegCreateKeyEx(nhk, name, 0, NULL, 0, KEY_WRITE, NULL, &netw, + NULL); + RegCloseKey(nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not add network key '%s':" + " error 0x%x (%d)", + name, (unsigned int) ret, (int) GetLastError()); + return -1; + } + +#define STR(t) write_str(netw, #t, ssid) +#define INT(t) write_int(netw, #t, ssid->t, 0) +#define INTe(t) write_int(netw, #t, ssid->eap.t, 0) +#define INT_DEF(t, def) write_int(netw, #t, ssid->t, def) +#define INT_DEFe(t, def) write_int(netw, #t, ssid->eap.t, def) + + STR(ssid); + INT(scan_ssid); + write_bssid(netw, ssid); + write_psk(netw, ssid); + write_proto(netw, ssid); + write_key_mgmt(netw, ssid); + write_pairwise(netw, ssid); + write_group(netw, ssid); + write_auth_alg(netw, ssid); +#ifdef IEEE8021X_EAPOL + write_eap(netw, ssid); + STR(identity); + STR(anonymous_identity); + STR(password); + STR(ca_cert); + STR(ca_path); + STR(client_cert); + STR(private_key); + STR(private_key_passwd); + STR(dh_file); + STR(subject_match); + STR(altsubject_match); + STR(ca_cert2); + STR(ca_path2); + STR(client_cert2); + STR(private_key2); + STR(private_key2_passwd); + STR(dh_file2); + STR(subject_match2); + STR(altsubject_match2); + STR(phase1); + STR(phase2); + STR(pcsc); + STR(pin); + STR(engine_id); + STR(key_id); + STR(cert_id); + STR(ca_cert_id); + STR(key2_id); + STR(pin2); + STR(engine2_id); + STR(cert2_id); + STR(ca_cert2_id); + INTe(engine); + INTe(engine2); + INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); +#endif /* IEEE8021X_EAPOL */ + for (i = 0; i < 4; i++) + write_wep_key(netw, i, ssid); + INT(wep_tx_keyidx); + INT(priority); +#ifdef IEEE8021X_EAPOL + INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); + STR(pac_file); + INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); +#endif /* IEEE8021X_EAPOL */ + INT(mode); + INT(proactive_key_caching); + INT(disabled); + INT(peerkey); +#ifdef CONFIG_IEEE80211W + INT(ieee80211w); +#endif /* CONFIG_IEEE80211W */ + STR(id_str); + +#undef STR +#undef INT +#undef INT_DEF + + RegCloseKey(netw); + + return errors ? -1 : 0; +} + + +static int wpa_config_write_blob(HKEY hk, struct wpa_config_blob *blob) +{ + HKEY bhk; + LONG ret; + TCHAR *name; + + ret = RegCreateKeyEx(hk, TEXT("blobs"), 0, NULL, 0, KEY_WRITE, NULL, + &bhk, NULL); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not add blobs key: " + "error 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + return -1; + } + + name = wpa_strdup_tchar(blob->name); + ret = RegSetValueEx(bhk, name, 0, REG_BINARY, blob->data, + blob->len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set blob %s': " + "error 0x%x (%d)", blob->name, (unsigned int) ret, + (int) GetLastError()); + RegCloseKey(bhk); + os_free(name); + return -1; + } + os_free(name); + + RegCloseKey(bhk); + + return 0; +} + + +int wpa_config_write(const char *name, struct wpa_config *config) +{ + TCHAR buf[256]; + HKEY hk; + LONG ret; + int errors = 0; + struct wpa_ssid *ssid; + struct wpa_config_blob *blob; + int id; + + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + +#ifdef UNICODE + _snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name); +#else /* UNICODE */ + os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name); +#endif /* UNICODE */ + + ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_SET_VALUE | DELETE, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant " + "configuration registry %s: error %d", buf, + (int) GetLastError()); + return -1; + } + + if (wpa_config_write_global(config, hk)) { + wpa_printf(MSG_ERROR, "Failed to write global configuration " + "data"); + errors++; + } + + wpa_config_delete_subkeys(hk, TEXT("networks")); + for (ssid = config->ssid, id = 0; ssid; ssid = ssid->next, id++) { + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) + continue; /* do not save temporary WPS networks */ + if (wpa_config_write_network(hk, ssid, id)) + errors++; + } + + RegDeleteKey(hk, TEXT("blobs")); + for (blob = config->blobs; blob; blob = blob->next) { + if (wpa_config_write_blob(hk, blob)) + errors++; + } + + RegCloseKey(hk); + + wpa_printf(MSG_DEBUG, "Configuration '%s' written %ssuccessfully", + name, errors ? "un" : ""); + return errors ? -1 : 0; +} diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c new file mode 100644 index 0000000..8604d16 --- /dev/null +++ b/wpa_supplicant/ctrl_iface.c @@ -0,0 +1,3530 @@ +/* + * WPA Supplicant / Control interface (shared code for all backends) + * Copyright (c) 2004-2010, 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/version.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "eap_peer/eap.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/preauth.h" +#include "rsn_supp/pmksa_cache.h" +#include "l2_packet/l2_packet.h" +#include "wps/wps.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "wps_supplicant.h" +#include "ibss_rsn.h" +#include "ap.h" +#include "p2p_supplicant.h" +#include "p2p/p2p.h" +#include "notify.h" +#include "bss.h" +#include "scan.h" +#include "ctrl_iface.h" + +extern struct wpa_driver_ops *wpa_drivers[]; + +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 wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *value; + int ret = 0; + + value = os_strchr(cmd, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value); + if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + atoi(value), -1, -1, -1); + } else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, atoi(value), -1, -1); + } else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, -1, atoi(value), -1); + } else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, -1, -1, atoi(value)); + } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, + atoi(value))) + ret = -1; + } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") == + 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, + atoi(value))) + ret = -1; + } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value))) + ret = -1; + } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) { + wpa_s->wps_fragment_size = atoi(value); +#ifdef CONFIG_WPS_TESTING + } else if (os_strcasecmp(cmd, "wps_version_number") == 0) { + long int val; + val = strtol(value, NULL, 0); + if (val < 0 || val > 0xff) { + ret = -1; + wpa_printf(MSG_DEBUG, "WPS: Invalid " + "wps_version_number %ld", val); + } else { + wps_version_number = val; + wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS " + "version %u.%u", + (wps_version_number & 0xf0) >> 4, + wps_version_number & 0x0f); + } + } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) { + wps_testing_dummy_cred = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", + wps_testing_dummy_cred); +#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_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); + if (disabled) { + if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0) + ret = -1; + } else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0) + ret = -1; + wpa_tdls_enable(wpa_s->wpa, !disabled); +#endif /* CONFIG_TDLS */ + } else { + value[-1] = '='; + ret = wpa_config_process_global(wpa_s->conf, cmd, -1); + if (ret == 0) + wpa_supplicant_update_config(wpa_s); + } + + return ret; +} + + +static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, size_t buflen) +{ + int res; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd); + + if (os_strcmp(cmd, "version") == 0) { + res = os_snprintf(buf, buflen, "%s", VERSION_STR); + if (res < 0 || (unsigned int) res >= buflen) + return -1; + return res; + } + + return -1; +} + + +#ifdef IEEE8021X_EAPOL +static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, + char *addr) +{ + u8 bssid[ETH_ALEN]; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (hwaddr_aton(addr, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address " + "'%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid)); + rsn_preauth_deinit(wpa_s->wpa); + if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL)) + return -1; + + return 0; +} +#endif /* IEEE8021X_EAPOL */ + + +#ifdef CONFIG_PEERKEY +/* MLME-STKSTART.request(peer) */ +static int wpa_supplicant_ctrl_iface_stkstart( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR, + MAC2STR(peer)); + + return wpa_sm_stkstart(wpa_s->wpa, peer); +} +#endif /* CONFIG_PEERKEY */ + + +#ifdef CONFIG_TDLS + +static int wpa_supplicant_ctrl_iface_tdls_discover( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR, + MAC2STR(peer)); + + return wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer); +} + + +static int wpa_supplicant_ctrl_iface_tdls_setup( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + int ret; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR, + MAC2STR(peer)); + + ret = wpa_tdls_reneg(wpa_s->wpa, peer); + if (ret) + ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); + return ret; +} + + +static int wpa_supplicant_ctrl_iface_tdls_teardown( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR, + MAC2STR(peer)); + + return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); +} + +#endif /* CONFIG_TDLS */ + + +#ifdef CONFIG_IEEE80211R +static int wpa_supplicant_ctrl_iface_ft_ds( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 target_ap[ETH_ALEN]; + struct wpa_bss *bss; + const u8 *mdie; + + if (hwaddr_aton(addr, target_ap)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap)); + + bss = wpa_bss_get_bssid(wpa_s, target_ap); + if (bss) + mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); + else + mdie = NULL; + + return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie); +} +#endif /* CONFIG_IEEE80211R */ + + +#ifdef CONFIG_WPS +static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 bssid[ETH_ALEN], *_bssid = bssid; + u8 p2p_dev_addr[ETH_ALEN], *_p2p_dev_addr = NULL; + + if (cmd == NULL || os_strcmp(cmd, "any") == 0) { + _bssid = NULL; +#ifdef CONFIG_P2P + } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { + if (hwaddr_aton(cmd + 13, p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid " + "P2P Device Address '%s'", + cmd + 13); + return -1; + } + _p2p_dev_addr = p2p_dev_addr; +#endif /* CONFIG_P2P */ + } else if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", + cmd); + return -1; + } + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr); +#endif /* CONFIG_AP */ + + return wpas_wps_start_pbc(wpa_s, _bssid, 0); +} + + +static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + u8 bssid[ETH_ALEN], *_bssid = bssid; + char *pin; + int ret; + + pin = os_strchr(cmd, ' '); + if (pin) + *pin++ = '\0'; + + if (os_strcmp(cmd, "any") == 0) + _bssid = NULL; + else if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'", + cmd); + return -1; + } + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin, + buf, buflen); +#endif /* CONFIG_AP */ + + if (pin) { + ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0, + DEV_PW_DEFAULT); + if (ret < 0) + return -1; + ret = os_snprintf(buf, buflen, "%s", pin); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; + } + + ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT); + if (ret < 0) + return -1; + + /* Return the generated PIN */ + ret = os_snprintf(buf, buflen, "%08d", ret); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; +} + + +static int wpa_supplicant_ctrl_iface_wps_check_pin( + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) +{ + char pin[9]; + size_t len; + char *pos; + int ret; + + wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN", + (u8 *) cmd, os_strlen(cmd)); + for (pos = cmd, len = 0; *pos != '\0'; pos++) { + if (*pos < '0' || *pos > '9') + continue; + pin[len++] = *pos; + if (len == 9) { + wpa_printf(MSG_DEBUG, "WPS: Too long PIN"); + return -1; + } + } + if (len != 4 && len != 8) { + wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len); + return -1; + } + pin[len] = '\0'; + + if (len == 8) { + unsigned int pin_val; + pin_val = atoi(pin); + if (!wps_pin_valid(pin_val)) { + wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit"); + ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n"); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; + } + } + + ret = os_snprintf(buf, buflen, "%s", pin); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + + return ret; +} + + +#ifdef CONFIG_WPS_OOB +static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *path, *method, *name; + + path = os_strchr(cmd, ' '); + if (path == NULL) + return -1; + *path++ = '\0'; + + method = os_strchr(path, ' '); + if (method == NULL) + return -1; + *method++ = '\0'; + + name = os_strchr(method, ' '); + if (name != NULL) + *name++ = '\0'; + + return wpas_wps_start_oob(wpa_s, cmd, path, method, name); +} +#endif /* CONFIG_WPS_OOB */ + + +static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 bssid[ETH_ALEN]; + char *pin; + char *new_ssid; + char *new_auth; + char *new_encr; + char *new_key; + struct wps_new_ap_settings ap; + + pin = os_strchr(cmd, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + + if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", + cmd); + return -1; + } + + new_ssid = os_strchr(pin, ' '); + if (new_ssid == NULL) + return wpas_wps_start_reg(wpa_s, bssid, pin, NULL); + *new_ssid++ = '\0'; + + new_auth = os_strchr(new_ssid, ' '); + if (new_auth == NULL) + return -1; + *new_auth++ = '\0'; + + new_encr = os_strchr(new_auth, ' '); + if (new_encr == NULL) + return -1; + *new_encr++ = '\0'; + + new_key = os_strchr(new_encr, ' '); + if (new_key == NULL) + return -1; + *new_key++ = '\0'; + + os_memset(&ap, 0, sizeof(ap)); + ap.ssid_hex = new_ssid; + ap.auth = new_auth; + ap.encr = new_encr; + ap.key_hex = new_key; + return wpas_wps_start_reg(wpa_s, bssid, pin, &ap); +} + + +#ifdef CONFIG_AP +static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + int timeout = 300; + char *pos; + const char *pin_txt; + + if (!wpa_s->ap_iface) + return -1; + + pos = os_strchr(cmd, ' '); + if (pos) + *pos++ = '\0'; + + if (os_strcmp(cmd, "disable") == 0) { + wpas_wps_ap_pin_disable(wpa_s); + return os_snprintf(buf, buflen, "OK\n"); + } + + if (os_strcmp(cmd, "random") == 0) { + if (pos) + timeout = atoi(pos); + pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(cmd, "get") == 0) { + pin_txt = wpas_wps_ap_pin_get(wpa_s); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(cmd, "set") == 0) { + char *pin; + if (pos == NULL) + return -1; + pin = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + timeout = atoi(pos); + } + if (os_strlen(pin) > buflen) + return -1; + if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0) + return -1; + return os_snprintf(buf, buflen, "%s", pin); + } + + return -1; +} +#endif /* CONFIG_AP */ + + +#ifdef CONFIG_WPS_ER +static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *uuid = cmd, *pin, *pos; + u8 addr_buf[ETH_ALEN], *addr = NULL; + pin = os_strchr(uuid, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + pos = os_strchr(pin, ' '); + if (pos) { + *pos++ = '\0'; + if (hwaddr_aton(pos, addr_buf) == 0) + addr = addr_buf; + } + return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin); +} + + +static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *uuid = cmd, *pin; + pin = os_strchr(uuid, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + return wpas_wps_er_learn(wpa_s, uuid, pin); +} + + +static int wpa_supplicant_ctrl_iface_wps_er_set_config( + struct wpa_supplicant *wpa_s, char *cmd) +{ + char *uuid = cmd, *id; + id = os_strchr(uuid, ' '); + if (id == NULL) + return -1; + *id++ = '\0'; + return wpas_wps_er_set_config(wpa_s, uuid, atoi(id)); +} + + +static int wpa_supplicant_ctrl_iface_wps_er_config( + struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pin; + char *new_ssid; + char *new_auth; + char *new_encr; + char *new_key; + struct wps_new_ap_settings ap; + + pin = os_strchr(cmd, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + + new_ssid = os_strchr(pin, ' '); + if (new_ssid == NULL) + return -1; + *new_ssid++ = '\0'; + + new_auth = os_strchr(new_ssid, ' '); + if (new_auth == NULL) + return -1; + *new_auth++ = '\0'; + + new_encr = os_strchr(new_auth, ' '); + if (new_encr == NULL) + return -1; + *new_encr++ = '\0'; + + new_key = os_strchr(new_encr, ' '); + if (new_key == NULL) + return -1; + *new_key++ = '\0'; + + os_memset(&ap, 0, sizeof(ap)); + ap.ssid_hex = new_ssid; + ap.auth = new_auth; + ap.encr = new_encr; + ap.key_hex = new_key; + return wpas_wps_er_config(wpa_s, cmd, pin, &ap); +} +#endif /* CONFIG_WPS_ER */ + +#endif /* CONFIG_WPS */ + + +#ifdef CONFIG_IBSS_RSN +static int wpa_supplicant_ctrl_iface_ibss_rsn( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR, + MAC2STR(peer)); + + return ibss_rsn_start(wpa_s->ibss_rsn, peer); +} +#endif /* CONFIG_IBSS_RSN */ + + +static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, + char *rsp) +{ +#ifdef IEEE8021X_EAPOL + char *pos, *id_pos; + int id; + struct wpa_ssid *ssid; + struct eap_peer_config *eap; + + pos = os_strchr(rsp, '-'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id_pos = pos; + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id = atoi(id_pos); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", + (u8 *) pos, os_strlen(pos)); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "to update", id); + return -1; + } + eap = &ssid->eap; + + if (os_strcmp(rsp, "IDENTITY") == 0) { + os_free(eap->identity); + eap->identity = (u8 *) os_strdup(pos); + eap->identity_len = os_strlen(pos); + eap->pending_req_identity = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (os_strcmp(rsp, "PASSWORD") == 0) { + os_free(eap->password); + eap->password = (u8 *) os_strdup(pos); + eap->password_len = os_strlen(pos); + eap->pending_req_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) { + os_free(eap->new_password); + eap->new_password = (u8 *) os_strdup(pos); + eap->new_password_len = os_strlen(pos); + eap->pending_req_new_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (os_strcmp(rsp, "PIN") == 0) { + os_free(eap->pin); + eap->pin = os_strdup(pos); + eap->pending_req_pin = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (os_strcmp(rsp, "OTP") == 0) { + os_free(eap->otp); + eap->otp = (u8 *) os_strdup(pos); + eap->otp_len = os_strlen(pos); + os_free(eap->pending_req_otp); + eap->pending_req_otp = NULL; + eap->pending_req_otp_len = 0; + } else if (os_strcmp(rsp, "PASSPHRASE") == 0) { + os_free(eap->private_key_passwd); + eap->private_key_passwd = (u8 *) os_strdup(pos); + eap->pending_req_passphrase = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp); + return -1; + } + + return 0; +#else /* IEEE8021X_EAPOL */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); + return -1; +#endif /* IEEE8021X_EAPOL */ +} + + +static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, + const char *params, + char *buf, size_t buflen) +{ + char *pos, *end, tmp[30]; + int res, verbose, ret; + + verbose = os_strcmp(params, "-VERBOSE") == 0; + pos = buf; + end = buf + buflen; + if (wpa_s->wpa_state >= WPA_ASSOCIATED) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", + MAC2STR(wpa_s->bssid)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + if (ssid) { + u8 *_ssid = ssid->ssid; + size_t ssid_len = ssid->ssid_len; + u8 ssid_buf[MAX_SSID_LEN]; + if (ssid_len == 0) { + int _res = wpa_drv_get_ssid(wpa_s, ssid_buf); + if (_res < 0) + ssid_len = 0; + else + ssid_len = _res; + _ssid = ssid_buf; + } + ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n", + wpa_ssid_txt(_ssid, ssid_len), + ssid->id); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (ssid->id_str) { + ret = os_snprintf(pos, end - pos, + "id_str=%s\n", + ssid->id_str); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + switch (ssid->mode) { + case WPAS_MODE_INFRA: + ret = os_snprintf(pos, end - pos, + "mode=station\n"); + break; + case WPAS_MODE_IBSS: + ret = os_snprintf(pos, end - pos, + "mode=IBSS\n"); + break; + case WPAS_MODE_AP: + ret = os_snprintf(pos, end - pos, + "mode=AP\n"); + break; + case WPAS_MODE_P2P_GO: + ret = os_snprintf(pos, end - pos, + "mode=P2P GO\n"); + break; + case WPAS_MODE_P2P_GROUP_FORMATION: + ret = os_snprintf(pos, end - pos, + "mode=P2P GO - group " + "formation\n"); + break; + default: + ret = 0; + break; + } + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos, + end - pos, + verbose); + } else +#endif /* CONFIG_AP */ + pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose); + } + ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", + wpa_supplicant_state_txt(wpa_s->wpa_state)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (wpa_s->l2 && + l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) { + ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) { + ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR + "\n", MAC2STR(wpa_s->global->p2p_dev_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_P2P */ + + ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n", + MAC2STR(wpa_s->own_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, + verbose); + if (res >= 0) + pos += res; + } + + res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose); + if (res >= 0) + pos += res; + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *pos; + int id; + struct wpa_ssid *ssid; + u8 bssid[ETH_ALEN]; + + /* cmd: "<network id> <BSSID>" */ + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos); + if (hwaddr_aton(pos, bssid)) { + wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos); + return -1; + } + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "to update", id); + return -1; + } + + os_memcpy(ssid->bssid, bssid, ETH_ALEN); + ssid->bssid_set = !is_zero_ether_addr(bssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_list_networks( + struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + char *pos, *end; + struct wpa_ssid *ssid; + int ret; + + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, + "network id / ssid / bssid / flags\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ssid = wpa_s->conf->ssid; + while (ssid) { + ret = os_snprintf(pos, end - pos, "%d\t%s", + ssid->id, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + if (ssid->bssid_set) { + ret = os_snprintf(pos, end - pos, "\t" MACSTR, + MAC2STR(ssid->bssid)); + } else { + ret = os_snprintf(pos, end - pos, "\tany"); + } + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + ret = os_snprintf(pos, end - pos, "\t%s%s%s", + ssid == wpa_s->current_ssid ? + "[CURRENT]" : "", + ssid->disabled ? "[DISABLED]" : "", + ssid->disabled == 2 ? "[P2P-PERSISTENT]" : + ""); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ssid = ssid->next; + } + + return pos - buf; +} + + +static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) +{ + int first = 1, ret; + ret = os_snprintf(pos, end - pos, "-"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + if (cipher & WPA_CIPHER_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (cipher & WPA_CIPHER_WEP40) { + ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (cipher & WPA_CIPHER_WEP104) { + ret = os_snprintf(pos, end - pos, "%sWEP104", + first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (cipher & WPA_CIPHER_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + if (cipher & WPA_CIPHER_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } + return pos; +} + + +static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, + const u8 *ie, size_t ie_len) +{ + struct wpa_ie_data data; + int first, ret; + + ret = os_snprintf(pos, end - pos, "[%s-", proto); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + + if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) { + ret = os_snprintf(pos, end - pos, "?]"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + return pos; + } + + first = 1; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "%sEAP", first ? "" : "+"); + 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 ? "" : "+"); + 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 ? "" : "+"); + 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 ? "" : "+"); + 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 ? "" : "+"); + 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 ? "" : "+"); + 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 ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } +#endif /* CONFIG_IEEE80211W */ + + pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); + + if (data.capabilities & WPA_CAPABILITY_PREAUTH) { + ret = os_snprintf(pos, end - pos, "-preauth"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "]"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + + return pos; +} + + +#ifdef CONFIG_WPS +static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s, + char *pos, char *end, + struct wpabuf *wps_ie) +{ + int ret; + const char *txt; + + if (wps_ie == NULL) + 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 + txt = "[WPS]"; + + ret = os_snprintf(pos, end - pos, "%s", txt); + if (ret >= 0 && ret < end - pos) + pos += ret; + wpabuf_free(wps_ie); + return pos; +} +#endif /* CONFIG_WPS */ + + +static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s, + char *pos, char *end, + const struct wpa_bss *bss) +{ +#ifdef CONFIG_WPS + struct wpabuf *wps_ie; + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie); +#else /* CONFIG_WPS */ + return pos; +#endif /* CONFIG_WPS */ +} + + +/* Format one result on one text line into a buffer. */ +static int wpa_supplicant_ctrl_iface_scan_result( + struct wpa_supplicant *wpa_s, + const struct wpa_bss *bss, char *buf, size_t buflen) +{ + char *pos, *end; + int ret; + const u8 *ie, *ie2, *p2p; + + p2p = wpa_bss_get_vendor_ie(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) + return 0; /* Do not show P2P listen discovery results here */ + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", + MAC2STR(bss->bssid), bss->freq, bss->level); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (ie) + pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); + ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (ie2) + pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); + if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { + ret = os_snprintf(pos, end - pos, "[WEP]"); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + 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]"); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\t%s", + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_scan_results( + struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + char *pos, *end; + struct wpa_bss *bss; + int ret; + + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / " + "flags / ssid\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { + ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos, + end - pos); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_select_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + /* cmd: "<network id>" or "any" */ + if (os_strcmp(cmd, "any") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any"); + ssid = NULL; + } else { + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " + "network id=%d", id); + return -1; + } + if (ssid->disabled == 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " + "SELECT_NETWORK with persistent P2P group"); + return -1; + } + } + + wpa_supplicant_select_network(wpa_s, ssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_enable_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + /* cmd: "<network id>" or "all" */ + if (os_strcmp(cmd, "all") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all"); + ssid = NULL; + } else { + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " + "network id=%d", id); + return -1; + } + if (ssid->disabled == 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " + "ENABLE_NETWORK with persistent P2P group"); + return -1; + } + } + wpa_supplicant_enable_network(wpa_s, ssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_disable_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + /* cmd: "<network id>" or "all" */ + if (os_strcmp(cmd, "all") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all"); + ssid = NULL; + } else { + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " + "network id=%d", id); + return -1; + } + if (ssid->disabled == 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " + "DISABLE_NETWORK with persistent P2P " + "group"); + return -1; + } + } + wpa_supplicant_disable_network(wpa_s, ssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_add_network( + struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + struct wpa_ssid *ssid; + int ret; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK"); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + + wpas_notify_network_added(wpa_s, ssid); + + ssid->disabled = 1; + wpa_config_set_network_defaults(ssid); + + ret = os_snprintf(buf, buflen, "%d\n", ssid->id); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; +} + + +static int wpa_supplicant_ctrl_iface_remove_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + /* cmd: "<network id>" or "all" */ + if (os_strcmp(cmd, "all") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all"); + ssid = wpa_s->conf->ssid; + while (ssid) { + struct wpa_ssid *remove_ssid = ssid; + id = ssid->id; + ssid = ssid->next; + wpas_notify_network_removed(wpa_s, remove_ssid); + wpa_config_remove_network(wpa_s->conf, id); + } + if (wpa_s->current_ssid) { + eapol_sm_invalidate_cached_session(wpa_s->eapol); + wpa_supplicant_disassociate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + return 0; + } + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || + wpa_config_remove_network(wpa_s->conf, id) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " + "id=%d", id); + return -1; + } + + if (ssid == wpa_s->current_ssid) { + /* + * Invalidate the EAP session cache if the current network is + * removed. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_set_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + char *name, *value; + + /* cmd: "<network id> <variable name> <value>" */ + name = os_strchr(cmd, ' '); + if (name == NULL) + return -1; + *name++ = '\0'; + + value = os_strchr(name, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'", + id, name); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", + (u8 *) value, os_strlen(value)); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " + "id=%d", id); + 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 (wpa_s->current_ssid == ssid) { + /* + * Invalidate the EAP session cache if anything in the current + * 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_get_network( + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) +{ + int id; + size_t res; + struct wpa_ssid *ssid; + char *name, *value; + + /* cmd: "<network id> <variable name>" */ + name = os_strchr(cmd, ' '); + if (name == NULL || buflen == 0) + return -1; + *name++ = '\0'; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", + id, name); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " + "id=%d", id); + return -1; + } + + value = wpa_config_get_no_key(ssid, name); + if (value == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " + "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) +{ + int ret; + + if (!wpa_s->conf->update_config) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed " + "to update configuration (update_config=0)"); + return -1; + } + + ret = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (ret) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to " + "update configuration"); + } else { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration" + " updated"); + } + + return ret; +} +#endif /* CONFIG_NO_CONFIG_WRITE */ + + +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; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "CCMP TKIP NONE", buflen); + if (len >= buflen) + return -1; + 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_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; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_group(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret, first = 1; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen); + if (len >= buflen) + return -1; + 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_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; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE " + "NONE", buflen); + if (len >= buflen) + return -1; + return len; + } + + ret = os_snprintf(pos, end - pos, "NONE IEEE8021X"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { + ret = os_snprintf(pos, end - pos, " WPA-EAP"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + ret = os_snprintf(pos, end - pos, " WPA-PSK"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + ret = os_snprintf(pos, end - pos, " WPA-NONE"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_proto(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret, first = 1; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "RSN WPA", buflen); + if (len >= buflen) + return -1; + return len; + } + + 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 ? "" : " "); + 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 ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_auth_alg(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret, first = 1; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen); + if (len >= buflen) + return -1; + return len; + } + + if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) { + ret = os_snprintf(pos, end - pos, "%sOPEN", first ? "" : " "); + 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 ? "" : " "); + 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 ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_get_capability( + struct wpa_supplicant *wpa_s, const char *_field, char *buf, + size_t buflen) +{ + struct wpa_driver_capa capa; + int res; + char *strict; + char field[30]; + size_t len; + + /* Determine whether or not strict checking was requested */ + len = os_strlcpy(field, _field, sizeof(field)); + if (len >= sizeof(field)) + return -1; + strict = os_strchr(field, ' '); + if (strict != NULL) { + *strict++ = '\0'; + if (os_strcmp(strict, "strict") != 0) + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s", + field, strict ? strict : ""); + + if (os_strcmp(field, "eap") == 0) { + return eap_get_names(buf, buflen); + } + + res = wpa_drv_get_capa(wpa_s, &capa); + + if (os_strcmp(field, "pairwise") == 0) + return ctrl_iface_get_capability_pairwise(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "group") == 0) + return ctrl_iface_get_capability_group(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "key_mgmt") == 0) + return ctrl_iface_get_capability_key_mgmt(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "proto") == 0) + return ctrl_iface_get_capability_proto(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "auth_alg") == 0) + return ctrl_iface_get_capability_auth_alg(res, strict, &capa, + buf, buflen); + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", + field); + + return -1; +} + + +static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, + const char *cmd, char *buf, + size_t buflen) +{ + u8 bssid[ETH_ALEN]; + size_t i; + struct wpa_bss *bss; + int ret; + char *pos, *end; + const u8 *ie, *ie2; + + if (os_strcmp(cmd, "FIRST") == 0) + bss = dl_list_first(&wpa_s->bss, struct wpa_bss, list); + else if (os_strncmp(cmd, "ID-", 3) == 0) { + i = atoi(cmd + 3); + bss = wpa_bss_get_id(wpa_s, i); + } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { + i = atoi(cmd + 5); + bss = wpa_bss_get_id(wpa_s, i); + if (bss) { + struct dl_list *next = bss->list_id.next; + if (next == &wpa_s->bss_id) + bss = NULL; + else + bss = dl_list_entry(next, struct wpa_bss, + list_id); + } + } else if (hwaddr_aton(cmd, bssid) == 0) + bss = wpa_bss_get_bssid(wpa_s, bssid); + else { + struct wpa_bss *tmp; + i = atoi(cmd); + bss = NULL; + dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id) + { + if (i-- == 0) { + bss = tmp; + break; + } + } + } + + if (bss == NULL) + return 0; + + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, + "id=%u\n" + "bssid=" MACSTR "\n" + "freq=%d\n" + "beacon_int=%d\n" + "capabilities=0x%04x\n" + "qual=%d\n" + "noise=%d\n" + "level=%d\n" + "tsf=%016llu\n" + "ie=", + bss->id, + MAC2STR(bss->bssid), bss->freq, bss->beacon_int, + bss->caps, bss->qual, bss->noise, bss->level, + (unsigned long long) bss->tsf); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ie = (const u8 *) (bss + 1); + for (i = 0; i < bss->ie_len; i++) { + ret = os_snprintf(pos, end - pos, "%02x", *ie++); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) { + ret = os_snprintf(pos, end - pos, "[P2P]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "flags="); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (ie) + pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); + ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (ie2) + pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); + if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { + ret = os_snprintf(pos, end - pos, "[WEP]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_IBSS) { + ret = os_snprintf(pos, end - pos, "[IBSS]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_ESS) { + ret = os_snprintf(pos, end - pos, "[ESS]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "ssid=%s\n", + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + +#ifdef CONFIG_WPS + ie = (const u8 *) (bss + 1); + ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P + ie = (const u8 *) (bss + 1); + ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; +#endif /* CONFIG_P2P */ + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_ap_scan( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int ap_scan = atoi(cmd); + return wpa_supplicant_set_ap_scan(wpa_s, ap_scan); +} + + +static int wpa_supplicant_ctrl_iface_scan_interval( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int scan_int = atoi(cmd); + if (scan_int < 0) + return -1; + wpa_s->scan_interval = scan_int; + return 0; +} + + +static int wpa_supplicant_ctrl_iface_bss_expire_age( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int expire_age = atoi(cmd); + return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age); +} + + +static int wpa_supplicant_ctrl_iface_bss_expire_count( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int expire_count = atoi(cmd); + return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count); +} + + +static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication"); + /* 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 */ + + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL, + 0); + /* MLME-SETPROTECTION.request(None) */ + wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid, + MLME_SETPROTECTION_PROTECT_TYPE_NONE, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + wpa_sm_drop_sa(wpa_s->wpa); +} + + +static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, + char *addr) +{ + u8 bssid[ETH_ALEN]; + struct wpa_bss *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (hwaddr_aton(addr, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid)); + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (!bss) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found " + "from BSS table"); + return -1; + } + + /* + * TODO: Find best network configuration block from configuration to + * allow roaming to other networks + */ + + if (!ssid) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network " + "configuration known for the target AP"); + return -1; + } + + wpa_s->reassociate = 1; + wpa_supplicant_connect(wpa_s, bss, ssid); + + return 0; +} + + +#ifdef CONFIG_P2P +static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) +{ + unsigned int timeout = atoi(cmd); + enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; + + if (os_strstr(cmd, "type=social")) + type = P2P_FIND_ONLY_SOCIAL; + else if (os_strstr(cmd, "type=progressive")) + type = P2P_FIND_PROGRESSIVE; + + return wpas_p2p_find(wpa_s, timeout, type, 0, NULL); +} + + +static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + char *pos, *pos2; + char *pin = NULL; + enum p2p_wps_method wps_method; + int new_pin; + int ret; + int persistent_group; + int join; + int auth; + int go_intent = -1; + int freq = 0; + + /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad] [persistent] + * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] */ + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + persistent_group = os_strstr(pos, " persistent") != NULL; + join = os_strstr(pos, " join") != NULL; + auth = os_strstr(pos, " auth") != NULL; + + pos2 = os_strstr(pos, " go_intent="); + if (pos2) { + pos2 += 11; + go_intent = atoi(pos2); + if (go_intent < 0 || go_intent > 15) + return -1; + } + + pos2 = os_strstr(pos, " freq="); + if (pos2) { + pos2 += 6; + freq = atoi(pos2); + if (freq <= 0) + return -1; + } + + if (os_strncmp(pos, "pin", 3) == 0) { + /* Request random PIN (to be displayed) and enable the PIN */ + wps_method = WPS_PIN_DISPLAY; + } else if (os_strncmp(pos, "pbc", 3) == 0) { + wps_method = WPS_PBC; + } else { + pin = pos; + pos = os_strchr(pin, ' '); + wps_method = WPS_PIN_KEYPAD; + if (pos) { + *pos++ = '\0'; + if (os_strncmp(pos, "label", 5) == 0) + wps_method = WPS_PIN_LABEL; + else if (os_strncmp(pos, "display", 7) == 0) + wps_method = WPS_PIN_DISPLAY; + } + } + + new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, + persistent_group, join, auth, go_intent, + freq); + if (new_pin == -2) { + os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); + return 25; + } + if (new_pin == -3) { + os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25); + return 25; + } + if (new_pin < 0) + return -1; + if (wps_method == WPS_PIN_DISPLAY && pin == NULL) { + ret = os_snprintf(buf, buflen, "%08d", new_pin); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; + } + + os_memcpy(buf, "OK\n", 3); + return 3; +} + + +static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd) +{ + unsigned int timeout = atoi(cmd); + return wpas_p2p_listen(wpa_s, timeout); +} + + +static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + char *pos; + + /* <addr> <config method> */ + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + return wpas_p2p_prov_disc(wpa_s, addr, pos); +} + + +static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || + ssid->passphrase == NULL) + return -1; + + os_strlcpy(buf, ssid->passphrase, buflen); + return os_strlen(buf); +} + + +static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + u64 ref; + int res; + u8 dst_buf[ETH_ALEN], *dst; + struct wpabuf *tlvs; + char *pos; + size_t len; + + if (hwaddr_aton(cmd, dst_buf)) + return -1; + dst = dst_buf; + if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && + dst[3] == 0 && dst[4] == 0 && dst[5] == 0) + dst = NULL; + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + if (os_strncmp(pos, "upnp ", 5) == 0) { + u8 version; + pos += 5; + if (hexstr2bin(pos, &version, 1) < 0) + return -1; + pos += 2; + if (*pos != ' ') + return -1; + pos++; + ref = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, dst, + version, pos); + } else { + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + tlvs = wpabuf_alloc(len); + if (tlvs == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) { + wpabuf_free(tlvs); + return -1; + } + + ref = (unsigned long) wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + } + res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref); + if (res < 0 || (unsigned) res >= buflen) + return -1; + return res; +} + + +static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s, + char *cmd) +{ + long long unsigned val; + u64 req; + if (sscanf(cmd, "%llx", &val) != 1) + return -1; + req = val; + return wpas_p2p_sd_cancel_request(wpa_s, (void *) (unsigned long) req); +} + + +static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd) +{ + int freq; + u8 dst[ETH_ALEN]; + u8 dialog_token; + struct wpabuf *resp_tlvs; + char *pos, *pos2; + size_t len; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + freq = atoi(cmd); + if (freq == 0) + return -1; + + if (hwaddr_aton(pos, dst)) + return -1; + pos += 17; + if (*pos != ' ') + return -1; + pos++; + + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + return -1; + *pos2++ = '\0'; + dialog_token = atoi(pos); + + len = os_strlen(pos2); + if (len & 1) + return -1; + len /= 2; + resp_tlvs = wpabuf_alloc(len); + if (resp_tlvs == NULL) + return -1; + if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) { + wpabuf_free(resp_tlvs); + return -1; + } + + wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs); + wpabuf_free(resp_tlvs); + return 0; +} + + +static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s, + char *cmd) +{ + wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd); + return 0; +} + + +static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *pos; + size_t len; + struct wpabuf *query, *resp; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + len = os_strlen(cmd); + if (len & 1) + return -1; + len /= 2; + query = wpabuf_alloc(len); + if (query == NULL) + return -1; + if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) { + wpabuf_free(query); + return -1; + } + + len = os_strlen(pos); + if (len & 1) { + wpabuf_free(query); + return -1; + } + len /= 2; + resp = wpabuf_alloc(len); + if (resp == NULL) { + wpabuf_free(query); + return -1; + } + if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) { + wpabuf_free(query); + wpabuf_free(resp); + return -1; + } + + if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) { + wpabuf_free(query); + wpabuf_free(resp); + return -1; + } + return 0; +} + + +static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + u8 version; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (hexstr2bin(cmd, &version, 1) < 0) + return -1; + + return wpas_p2p_service_add_upnp(wpa_s, version, pos); +} + + +static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "bonjour") == 0) + return p2p_ctrl_service_add_bonjour(wpa_s, pos); + if (os_strcmp(cmd, "upnp") == 0) + return p2p_ctrl_service_add_upnp(wpa_s, pos); + wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); + return -1; +} + + +static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s, + char *cmd) +{ + size_t len; + struct wpabuf *query; + int ret; + + len = os_strlen(cmd); + if (len & 1) + return -1; + len /= 2; + query = wpabuf_alloc(len); + if (query == NULL) + return -1; + if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) { + wpabuf_free(query); + return -1; + } + + ret = wpas_p2p_service_del_bonjour(wpa_s, query); + wpabuf_free(query); + return ret; +} + + +static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + u8 version; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (hexstr2bin(cmd, &version, 1) < 0) + return -1; + + return wpas_p2p_service_del_upnp(wpa_s, version, pos); +} + + +static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "bonjour") == 0) + return p2p_ctrl_service_del_bonjour(wpa_s, pos); + if (os_strcmp(cmd, "upnp") == 0) + return p2p_ctrl_service_del_upnp(wpa_s, pos); + wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); + return -1; +} + + +static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + + /* <addr> */ + + if (hwaddr_aton(cmd, addr)) + return -1; + + return wpas_p2p_reject(wpa_s, addr); +} + + +static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + int id; + struct wpa_ssid *ssid; + u8 peer[ETH_ALEN]; + + id = atoi(cmd); + pos = os_strstr(cmd, " peer="); + if (pos) { + pos += 6; + if (hwaddr_aton(pos, peer)) + return -1; + } + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->disabled != 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "for persistent P2P group", + id); + return -1; + } + + return wpas_p2p_invite(wpa_s, pos ? peer : NULL, ssid, NULL); +} + + +static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL; + + pos = os_strstr(cmd, " peer="); + if (!pos) + return -1; + + *pos = '\0'; + pos += 6; + if (hwaddr_aton(pos, peer)) { + wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos); + return -1; + } + + pos = os_strstr(pos, " go_dev_addr="); + if (pos) { + pos += 13; + if (hwaddr_aton(pos, go_dev_addr)) { + wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", + pos); + return -1; + } + go_dev = go_dev_addr; + } + + return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev); +} + + +static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) +{ + if (os_strncmp(cmd, "persistent=", 11) == 0) + return p2p_ctrl_invite_persistent(wpa_s, cmd + 11); + if (os_strncmp(cmd, "group=", 6) == 0) + return p2p_ctrl_invite_group(wpa_s, cmd + 6); + + return -1; +} + + +static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, + char *cmd, int freq) +{ + int id; + struct wpa_ssid *ssid; + + id = atoi(cmd); + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->disabled != 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "for persistent P2P group", + id); + return -1; + } + + return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq); +} + + +static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) +{ + int freq = 0; + char *pos; + + pos = os_strstr(cmd, "freq="); + if (pos) + freq = atoi(pos + 5); + + if (os_strncmp(cmd, "persistent=", 11) == 0) + return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq); + if (os_strcmp(cmd, "persistent") == 0 || + os_strncmp(cmd, "persistent ", 11) == 0) + return wpas_p2p_group_add(wpa_s, 1, freq); + if (os_strncmp(cmd, "freq=", 5) == 0) + return wpas_p2p_group_add(wpa_s, 0, freq); + + wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'", + cmd); + return -1; +} + + +static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN], *addr_ptr; + int next; + + if (!wpa_s->global->p2p) + return -1; + + if (os_strcmp(cmd, "FIRST") == 0) { + addr_ptr = NULL; + next = 0; + } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { + if (hwaddr_aton(cmd + 5, addr) < 0) + return -1; + addr_ptr = addr; + next = 1; + } else { + if (hwaddr_aton(cmd, addr) < 0) + return -1; + addr_ptr = addr; + next = 0; + } + + return p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next, + buf, buflen); +} + + +static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *param; + + if (wpa_s->global->p2p == NULL) + return -1; + + param = os_strchr(cmd, ' '); + if (param == NULL) + return -1; + *param++ = '\0'; + + if (os_strcmp(cmd, "discoverability") == 0) { + p2p_set_client_discoverability(wpa_s->global->p2p, + atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "managed") == 0) { + p2p_set_managed_oper(wpa_s->global->p2p, atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "listen_channel") == 0) { + return p2p_set_listen_channel(wpa_s->global->p2p, 81, + atoi(param)); + } + + if (os_strcmp(cmd, "ssid_postfix") == 0) { + return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param, + os_strlen(param)); + } + + if (os_strcmp(cmd, "noa") == 0) { + char *pos; + int count, start, duration; + /* GO NoA parameters: count,start_offset(ms),duration(ms) */ + count = atoi(param); + pos = os_strchr(param, ','); + if (pos == NULL) + return -1; + pos++; + start = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + duration = atoi(pos); + if (count < 0 || count > 255 || start < 0 || duration < 0) + return -1; + if (count == 0 && duration > 0) + return -1; + wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d " + "start=%d duration=%d", count, start, duration); + return wpas_p2p_set_noa(wpa_s, count, start, duration); + } + + if (os_strcmp(cmd, "ps") == 0) + return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1); + + if (os_strcmp(cmd, "oppps") == 0) + return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1); + + if (os_strcmp(cmd, "ctwindow") == 0) + return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param)); + + if (os_strcmp(cmd, "disabled") == 0) { + wpa_s->global->p2p_disabled = atoi(param); + wpa_printf(MSG_DEBUG, "P2P functionality %s", + wpa_s->global->p2p_disabled ? + "disabled" : "enabled"); + if (wpa_s->global->p2p_disabled) { + wpas_p2p_stop_find(wpa_s); + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + p2p_flush(wpa_s->global->p2p); + } + return 0; + } + + if (os_strcmp(cmd, "force_long_sd") == 0) { + wpa_s->force_long_sd = atoi(param); + return 0; + } + + if (os_strcmp(cmd, "peer_filter") == 0) { + u8 addr[ETH_ALEN]; + if (hwaddr_aton(param, addr)) + return -1; + p2p_set_peer_filter(wpa_s->global->p2p, addr); + return 0; + } + + if (os_strcmp(cmd, "cross_connect") == 0) + return wpas_p2p_set_cross_connect(wpa_s, atoi(param)); + + if (os_strcmp(cmd, "go_apsd") == 0) { + if (os_strcmp(param, "disable") == 0) + wpa_s->set_ap_uapsd = 0; + else { + wpa_s->set_ap_uapsd = 1; + wpa_s->ap_uapsd = atoi(param); + } + return 0; + } + + if (os_strcmp(cmd, "client_apsd") == 0) { + if (os_strcmp(param, "disable") == 0) + wpa_s->set_sta_uapsd = 0; + else { + int be, bk, vi, vo; + char *pos; + /* format: BE,BK,VI,VO;max SP Length */ + be = atoi(param); + pos = os_strchr(param, ','); + if (pos == NULL) + return -1; + pos++; + bk = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vi = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vo = atoi(pos); + /* ignore max SP Length for now */ + + wpa_s->set_sta_uapsd = 1; + wpa_s->sta_uapsd = 0; + if (be) + wpa_s->sta_uapsd |= BIT(0); + if (bk) + wpa_s->sta_uapsd |= BIT(1); + if (vi) + wpa_s->sta_uapsd |= BIT(2); + if (vo) + wpa_s->sta_uapsd |= BIT(3); + } + return 0; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", + cmd); + + return -1; +} + + +static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos, *pos2; + unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0; + + if (cmd[0]) { + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + dur1 = atoi(cmd); + + pos2 = os_strchr(pos, ' '); + if (pos2) + *pos2++ = '\0'; + int1 = atoi(pos); + } else + pos2 = NULL; + + if (pos2) { + pos = os_strchr(pos2, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + dur2 = atoi(pos2); + int2 = atoi(pos); + } + + return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2); +} + + +static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + unsigned int period = 0, interval = 0; + + if (cmd[0]) { + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + period = atoi(cmd); + interval = atoi(pos); + } + + return wpas_p2p_ext_listen(wpa_s, period, interval); +} + +#endif /* CONFIG_P2P */ + + +static int wpa_supplicant_ctrl_iface_sta_autoconnect( + struct wpa_supplicant *wpa_s, char *cmd) +{ + wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0; + return 0; +} + + +static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct wpa_signal_info si; + int ret; + + ret = wpa_drv_signal_poll(wpa_s, &si); + if (ret) + return -1; + + ret = os_snprintf(buf, buflen, "RSSI=%d\nLINKSPEED=%d\n" + "NOISE=%d\nFREQUENCY=%u\n", + si.current_signal, si.current_txrate / 1000, + si.current_noise, si.frequency); + if (ret < 0 || (unsigned int) ret > buflen) + return -1; + return ret; +} + + +char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, + char *buf, size_t *resp_len) +{ + 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) { + 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)); + } + + reply = os_malloc(reply_size); + if (reply == NULL) { + *resp_len = 1; + return NULL; + } + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NOTE ", 5) == 0) { + wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); + } else if (os_strcmp(buf, "MIB") == 0) { + reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); + if (reply_len >= 0) { + int res; + res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + } else if (os_strncmp(buf, "STATUS", 6) == 0) { + reply_len = wpa_supplicant_ctrl_iface_status( + wpa_s, buf + 6, reply, reply_size); + } else if (os_strcmp(buf, "PMKSA") == 0) { + reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply, + reply_size); + } else if (os_strncmp(buf, "SET ", 4) == 0) { + if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) + reply_len = -1; + } else if (os_strncmp(buf, "GET ", 4) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4, + reply, reply_size); + } else if (os_strcmp(buf, "LOGON") == 0) { + eapol_sm_notify_logoff(wpa_s->eapol, FALSE); + } else if (os_strcmp(buf, "LOGOFF") == 0) { + eapol_sm_notify_logoff(wpa_s->eapol, TRUE); + } else if (os_strcmp(buf, "REASSOCIATE") == 0) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + reply_len = -1; + else { + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + } else if (os_strcmp(buf, "RECONNECT") == 0) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + reply_len = -1; + else if (wpa_s->disconnected) { + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +#ifdef IEEE8021X_EAPOL + } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) + reply_len = -1; +#endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(buf, "STKSTART ", 9) == 0) { + if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9)) + reply_len = -1; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211R + } else if (os_strncmp(buf, "FT_DS ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6)) + reply_len = -1; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPS + } else if (os_strcmp(buf, "WPS_PBC") == 0) { + int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL); + if (res == -2) { + os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); + reply_len = 17; + } else if (res) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) { + int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8); + if (res == -2) { + os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); + reply_len = 17; + } else if (res) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8, + reply, + reply_size); + } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_check_pin( + wpa_s, buf + 14, reply, reply_size); + } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { + if (wpas_wps_cancel(wpa_s)) + reply_len = -1; +#ifdef CONFIG_WPS_OOB + } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8)) + reply_len = -1; +#endif /* CONFIG_WPS_OOB */ + } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) + reply_len = -1; +#ifdef CONFIG_AP + } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin( + wpa_s, buf + 11, reply, reply_size); +#endif /* CONFIG_AP */ +#ifdef CONFIG_WPS_ER + } else if (os_strcmp(buf, "WPS_ER_START") == 0) { + if (wpas_wps_er_start(wpa_s, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) { + if (wpas_wps_er_start(wpa_s, buf + 13)) + reply_len = -1; + } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) { + if (wpas_wps_er_stop(wpa_s)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) { + if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) { + int ret = wpas_wps_er_pbc(wpa_s, buf + 11); + if (ret == -2) { + os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); + reply_len = 17; + } else if (ret == -3) { + os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18); + reply_len = 18; + } else if (ret == -4) { + os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20); + reply_len = 20; + } else if (ret) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) { + if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) { + if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s, + buf + 18)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14)) + reply_len = -1; +#endif /* CONFIG_WPS_ER */ +#endif /* CONFIG_WPS */ +#ifdef CONFIG_IBSS_RSN + } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) { + if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9)) + reply_len = -1; +#endif /* CONFIG_IBSS_RSN */ +#ifdef CONFIG_P2P + } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { + if (p2p_ctrl_find(wpa_s, buf + 9)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_FIND") == 0) { + if (p2p_ctrl_find(wpa_s, "")) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) { + wpas_p2p_stop_find(wpa_s); + } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) { + reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply, + reply_size); + } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) { + if (p2p_ctrl_listen(wpa_s, buf + 11)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_LISTEN") == 0) { + if (p2p_ctrl_listen(wpa_s, "")) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) { + 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)) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { + if (p2p_ctrl_group_add(wpa_s, buf + 14)) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) { + if (p2p_ctrl_prov_disc(wpa_s, buf + 14)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) { + reply_len = p2p_get_passphrase(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) { + reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply, + reply_size); + } else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) { + if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) { + if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) { + wpas_p2p_sd_service_update(wpa_s); + } else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) { + if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) { + wpas_p2p_service_flush(wpa_s); + } else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) { + if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) { + if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) { + if (p2p_ctrl_reject(wpa_s, buf + 11) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) { + if (p2p_ctrl_invite(wpa_s, buf + 11) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) { + reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply, + reply_size); + } else if (os_strncmp(buf, "P2P_SET ", 8) == 0) { + if (p2p_ctrl_set(wpa_s, buf + 8) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_FLUSH") == 0) { + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + wpa_s->force_long_sd = 0; + if (wpa_s->global->p2p) + p2p_flush(wpa_s->global->p2p); + } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) { + if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_CANCEL") == 0) { + if (wpas_p2p_cancel(wpa_s)) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) { + if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) { + if (p2p_ctrl_presence_req(wpa_s, "") < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) { + if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) { + if (p2p_ctrl_ext_listen(wpa_s, "") < 0) + reply_len = -1; +#endif /* CONFIG_P2P */ + } 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 if (os_strcmp(buf, "RECONFIGURE") == 0) { + if (wpa_supplicant_reload_configuration(wpa_s)) + reply_len = -1; + } else if (os_strcmp(buf, "TERMINATE") == 0) { + wpa_supplicant_terminate_proc(wpa_s->global); + } else if (os_strncmp(buf, "BSSID ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6)) + reply_len = -1; + } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) { + reply_len = wpa_supplicant_ctrl_iface_list_networks( + wpa_s, reply, reply_size); + } else if (os_strcmp(buf, "DISCONNECT") == 0) { + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } else if (os_strcmp(buf, "SCAN") == 0) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + reply_len = -1; + else { + if (!wpa_s->scanning && + ((wpa_s->wpa_state <= WPA_SCANNING) || + (wpa_s->wpa_state == WPA_COMPLETED))) { + wpa_s->scan_req = 2; + 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_RESULTS") == 0) { + reply_len = wpa_supplicant_ctrl_iface_scan_results( + wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) { + if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16)) + reply_len = -1; + } else if (os_strcmp(buf, "ADD_NETWORK") == 0) { + reply_len = wpa_supplicant_ctrl_iface_add_network( + wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) { + if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12)) + reply_len = -1; + } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get_network( + wpa_s, buf + 12, 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)) + reply_len = -1; +#endif /* CONFIG_NO_CONFIG_WRITE */ + } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get_capability( + wpa_s, buf + 15, reply, reply_size); + } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14)) + reply_len = -1; + } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { + reply_len = wpa_supplicant_global_iface_list( + wpa_s->global, reply, reply_size); + } else if (os_strcmp(buf, "INTERFACES") == 0) { + reply_len = wpa_supplicant_global_iface_interfaces( + wpa_s->global, reply, reply_size); + } else if (os_strncmp(buf, "BSS ", 4) == 0) { + reply_len = wpa_supplicant_ctrl_iface_bss( + wpa_s, buf + 4, reply, reply_size); +#ifdef CONFIG_AP + } else if (os_strcmp(buf, "STA-FIRST") == 0) { + reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "STA ", 4) == 0) { + reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply, + reply_size); + } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { + reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply, + reply_size); +#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); + } else if (os_strcmp(buf, "DROP_SA") == 0) { + wpa_supplicant_ctrl_iface_drop_sa(wpa_s); + } else if (os_strncmp(buf, "ROAM ", 5) == 0) { + if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5)) + reply_len = -1; + } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) { + if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) { + if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s, + buf + 17)) + reply_len = -1; +#ifdef CONFIG_TDLS + } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14)) + reply_len = -1; + } else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11)) + reply_len = -1; + } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14)) + reply_len = -1; +#endif /* CONFIG_TDLS */ + } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) { + reply_len = wpa_supplicant_signal_poll(wpa_s, reply, + reply_size); + } else { + os_memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + if (ctrl_rsp) + eapol_sm_notify_ctrl_response(wpa_s->eapol); + + *resp_len = reply_len; + return reply; +} + + +static int wpa_supplicant_global_iface_add(struct wpa_global *global, + char *cmd) +{ + struct wpa_interface iface; + char *pos; + + /* + * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param> + * TAB<bridge_ifname> + */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); + + os_memset(&iface, 0, sizeof(iface)); + + do { + iface.ifname = pos = cmd; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.ifname[0] == '\0') + return -1; + if (pos == NULL) + break; + + iface.confname = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.confname[0] == '\0') + iface.confname = NULL; + if (pos == NULL) + break; + + iface.driver = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.driver[0] == '\0') + iface.driver = NULL; + if (pos == NULL) + break; + + iface.ctrl_interface = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.ctrl_interface[0] == '\0') + iface.ctrl_interface = NULL; + if (pos == NULL) + break; + + iface.driver_param = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.driver_param[0] == '\0') + iface.driver_param = NULL; + if (pos == NULL) + break; + + iface.bridge_ifname = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.bridge_ifname[0] == '\0') + iface.bridge_ifname = NULL; + if (pos == NULL) + break; + } while (0); + + if (wpa_supplicant_get_iface(global, iface.ifname)) + return -1; + + return wpa_supplicant_add_iface(global, &iface) ? 0 : -1; +} + + +static int wpa_supplicant_global_iface_remove(struct wpa_global *global, + char *cmd) +{ + struct wpa_supplicant *wpa_s; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd); + + wpa_s = wpa_supplicant_get_iface(global, cmd); + if (wpa_s == NULL) + return -1; + return wpa_supplicant_remove_iface(global, wpa_s); +} + + +static void wpa_free_iface_info(struct wpa_interface_info *iface) +{ + struct wpa_interface_info *prev; + + while (iface) { + prev = iface; + iface = iface->next; + + os_free(prev->ifname); + os_free(prev->desc); + os_free(prev); + } +} + + +static int wpa_supplicant_global_iface_list(struct wpa_global *global, + char *buf, int len) +{ + int i, res; + struct wpa_interface_info *iface = NULL, *last = NULL, *tmp; + char *pos, *end; + + for (i = 0; wpa_drivers[i]; i++) { + struct wpa_driver_ops *drv = wpa_drivers[i]; + if (drv->get_interfaces == NULL) + continue; + tmp = drv->get_interfaces(global->drv_priv[i]); + if (tmp == NULL) + continue; + + if (last == NULL) + iface = last = tmp; + else + last->next = tmp; + while (last->next) + last = last->next; + } + + pos = buf; + end = buf + len; + for (tmp = iface; tmp; tmp = tmp->next) { + res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n", + tmp->drv_name, tmp->ifname, + tmp->desc ? tmp->desc : ""); + if (res < 0 || res >= end - pos) { + *pos = '\0'; + break; + } + pos += res; + } + + wpa_free_iface_info(iface); + + return pos - buf; +} + + +static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, + char *buf, int len) +{ + int res; + char *pos, *end; + struct wpa_supplicant *wpa_s; + + wpa_s = global->ifaces; + pos = buf; + end = buf + len; + + while (wpa_s) { + res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); + if (res < 0 || res >= end - pos) { + *pos = '\0'; + break; + } + pos += res; + wpa_s = wpa_s->next; + } + return pos - buf; +} + + +char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, + char *buf, size_t *resp_len) +{ + char *reply; + const int reply_size = 2048; + int reply_len; + + wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface", + (const u8 *) buf, os_strlen(buf)); + + reply = os_malloc(reply_size); + if (reply == NULL) { + *resp_len = 1; + return NULL; + } + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) { + if (wpa_supplicant_global_iface_add(global, buf + 14)) + reply_len = -1; + } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) { + if (wpa_supplicant_global_iface_remove(global, buf + 17)) + reply_len = -1; + } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { + reply_len = wpa_supplicant_global_iface_list( + global, reply, reply_size); + } else if (os_strcmp(buf, "INTERFACES") == 0) { + reply_len = wpa_supplicant_global_iface_interfaces( + global, reply, reply_size); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + wpa_supplicant_terminate_proc(global); + } else if (os_strcmp(buf, "SUSPEND") == 0) { + wpas_notify_suspend(global); + } else if (os_strcmp(buf, "RESUME") == 0) { + wpas_notify_resume(global); + } else { + os_memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + *resp_len = reply_len; + return reply; +} diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h new file mode 100644 index 0000000..051d99a --- /dev/null +++ b/wpa_supplicant/ctrl_iface.h @@ -0,0 +1,159 @@ +/* + * WPA Supplicant / UNIX domain socket -based control interface + * Copyright (c) 2004-2005, 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. + */ + +#ifndef CTRL_IFACE_H +#define CTRL_IFACE_H + +#ifdef CONFIG_CTRL_IFACE + +/* Shared functions from ctrl_iface.c; to be called by ctrl_iface backends */ + +/** + * wpa_supplicant_ctrl_iface_process - Process ctrl_iface command + * @wpa_s: Pointer to wpa_supplicant data + * @buf: Received command buffer (nul terminated string) + * @resp_len: Variable to be set to the response length + * Returns: Response (*resp_len bytes) or %NULL on failure + * + * Control interface backends call this function when receiving a message that + * they do not process internally, i.e., anything else than ATTACH, DETACH, + * and LEVEL. The return response value is then sent to the external program + * that sent the command. Caller is responsible for freeing the buffer after + * this. If %NULL is returned, *resp_len can be set to two special values: + * 1 = send "FAIL\n" response, 2 = send "OK\n" response. If *resp_len has any + * other value, no response is sent. + */ +char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, + char *buf, size_t *resp_len); + +/** + * wpa_supplicant_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 + * Returns: Response (*resp_len bytes) or %NULL on failure + * + * Control interface backends call this function when receiving a message from + * the global ctrl_iface connection. The return response value is then sent to + * the external program that sent the command. Caller is responsible for + * freeing the buffer after this. If %NULL is returned, *resp_len can be set to + * two special values: 1 = send "FAIL\n" response, 2 = send "OK\n" response. If + * *resp_len has any other value, no response is sent. + */ +char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, + char *buf, size_t *resp_len); + + +/* Functions that each ctrl_iface backend must implement */ + +/** + * wpa_supplicant_ctrl_iface_init - Initialize control interface + * @wpa_s: Pointer to wpa_supplicant data + * Returns: Pointer to private data on success, %NULL on failure + * + * Initialize the control interface and start receiving commands from external + * programs. + * + * Required to be implemented in each control interface backend. + */ +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s); + +/** + * wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface + * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() + * + * Deinitialize the control interface that was initialized with + * wpa_supplicant_ctrl_iface_init(). + * + * Required to be implemented in each control interface backend. + */ +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv); + +/** + * wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor + * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() + * + * Wait until the first message from an external program using the control + * interface is received. This function can be used to delay normal startup + * processing to allow control interface programs to attach with + * %wpa_supplicant before normal operations are started. + * + * Required to be implemented in each control interface backend. + */ +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv); + +/** + * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: Pointer to private data on success, %NULL on failure + * + * Initialize the global control interface and start receiving commands from + * external programs. + * + * Required to be implemented in each control interface backend. + */ +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global); + +/** + * wpa_supplicant_global_ctrl_iface_deinit - Deinitialize global ctrl interface + * @priv: Pointer to private data from wpa_supplicant_global_ctrl_iface_init() + * + * Deinitialize the global control interface that was initialized with + * wpa_supplicant_global_ctrl_iface_init(). + * + * Required to be implemented in each control interface backend. + */ +void wpa_supplicant_global_ctrl_iface_deinit( + struct ctrl_iface_global_priv *priv); + +#else /* CONFIG_CTRL_IFACE */ + +static inline struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + return (void *) -1; +} + +static inline void +wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ +} + +static inline void +wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, int level, + char *buf, size_t len) +{ +} + +static inline void +wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ +} + +static inline struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + return (void *) 1; +} + +static inline void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ +} + +#endif /* CONFIG_CTRL_IFACE */ + +#endif /* CTRL_IFACE_H */ diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c new file mode 100644 index 0000000..5f7e24d --- /dev/null +++ b/wpa_supplicant/ctrl_iface_named_pipe.c @@ -0,0 +1,835 @@ +/* + * WPA Supplicant / Windows Named Pipe -based control interface + * Copyright (c) 2004-2006, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "common/wpa_ctrl.h" + +#ifdef __MINGW32_VERSION +/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here + */ +#define SDDL_REVISION_1 1 +BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA( + LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG); +BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW( + LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG); +#ifdef UNICODE +#define ConvertStringSecurityDescriptorToSecurityDescriptor \ +ConvertStringSecurityDescriptorToSecurityDescriptorW +#else +#define ConvertStringSecurityDescriptorToSecurityDescriptor \ +ConvertStringSecurityDescriptorToSecurityDescriptorA +#endif +#else /* __MINGW32_VERSION */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif +#include <sddl.h> +#endif /* __MINGW32_VERSION */ + +#ifndef WPA_SUPPLICANT_NAMED_PIPE +#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" +#endif +#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) + +/* Per-interface ctrl_iface */ + +#define REQUEST_BUFSIZE 256 +#define REPLY_BUFSIZE 4096 + +struct ctrl_iface_priv; + +/** + * struct wpa_ctrl_dst - Internal data structure of control interface clients + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. This data is private to + * ctrl_iface_named_pipe.c and should not be touched directly from other files. + */ +struct wpa_ctrl_dst { + /* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */ + OVERLAPPED overlap; + struct wpa_ctrl_dst *next, *prev; + struct ctrl_iface_priv *priv; + HANDLE pipe; + int attached; + int debug_level; + int errors; + char req_buf[REQUEST_BUFSIZE]; + char *rsp_buf; + int used; +}; + + +struct ctrl_iface_priv { + struct wpa_supplicant *wpa_s; + struct wpa_ctrl_dst *ctrl_dst; + SECURITY_ATTRIBUTES attr; + int sec_attr_set; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len); + +static void ctrl_close_pipe(struct wpa_ctrl_dst *dst); +static void wpa_supplicant_ctrl_iface_receive(void *, void *); +static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap); + +struct wpa_global_dst; +static void global_close_pipe(struct wpa_global_dst *dst); +static void wpa_supplicant_global_iface_receive(void *eloop_data, + void *user_ctx); +static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap); + + +static int ctrl_broken_pipe(HANDLE pipe, int used) +{ + DWORD err; + + if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL)) + return 0; + + err = GetLastError(); + if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used)) + return 1; + return 0; +} + + +static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst, *next; + + dst = priv->ctrl_dst; + + while (dst) { + next = dst->next; + if (ctrl_broken_pipe(dst->pipe, dst->used)) { + wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p", + dst); + ctrl_close_pipe(dst); + } + dst = next; + } +} + + +static int ctrl_open_pipe(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst; + DWORD err; + TCHAR name[256]; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst); + + dst->priv = priv; + dst->debug_level = MSG_INFO; + dst->pipe = INVALID_HANDLE_VALUE; + + dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (dst->overlap.hEvent == NULL) { + wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d", + (int) GetLastError()); + goto fail; + } + + eloop_register_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent), + wpa_supplicant_ctrl_iface_receive, dst, NULL); + +#ifdef UNICODE + _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), + priv->wpa_s->ifname); +#else /* UNICODE */ + os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", + priv->wpa_s->ifname); +#endif /* UNICODE */ + + /* TODO: add support for configuring access list for the pipe */ + dst->pipe = CreateNamedPipe(name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT, + 15, REPLY_BUFSIZE, REQUEST_BUFSIZE, + 1000, + priv->sec_attr_set ? &priv->attr : NULL); + if (dst->pipe == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d", + (int) GetLastError()); + goto fail; + } + + if (ConnectNamedPipe(dst->pipe, &dst->overlap)) { + wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d", + (int) GetLastError()); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + err = GetLastError(); + switch (err) { + case ERROR_IO_PENDING: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in " + "progress"); + break; + case ERROR_PIPE_CONNECTED: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already " + "connected"); + if (SetEvent(dst->overlap.hEvent)) + break; + /* fall through */ + default: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d", + (int) err); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + dst->next = priv->ctrl_dst; + if (dst->next) + dst->next->prev = dst; + priv->ctrl_dst = dst; + + return 0; + +fail: + ctrl_close_pipe(dst); + return -1; +} + + +static void ctrl_close_pipe(struct wpa_ctrl_dst *dst) +{ + wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst); + + if (dst->overlap.hEvent) { + eloop_unregister_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent)); + CloseHandle(dst->overlap.hEvent); + } + + if (dst->pipe != INVALID_HANDLE_VALUE) { + /* + * Could use FlushFileBuffers() here to guarantee that all data + * gets delivered to the client, but that can block, so let's + * not do this for now. + * FlushFileBuffers(dst->pipe); + */ + CloseHandle(dst->pipe); + } + + if (dst->prev) + dst->prev->next = dst->next; + else + dst->priv->ctrl_dst = dst->next; + if (dst->next) + dst->next->prev = dst->prev; + + os_free(dst->rsp_buf); + os_free(dst); +} + + +static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p " + "err=%d bytes=%d", dst, (int) err, (int) bytes); + if (err) { + ctrl_close_pipe(dst); + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + + if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf), + &dst->overlap, ctrl_iface_read_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d", + (int) GetLastError()); + ctrl_close_pipe(dst); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst); +} + + +static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len) +{ + struct wpa_supplicant *wpa_s = dst->priv->wpa_s; + char *reply = NULL, *send_buf; + size_t reply_len = 0, send_len; + int new_attached = 0; + char *buf = dst->req_buf; + + dst->used = 1; + if (len >= REQUEST_BUFSIZE) + len = REQUEST_BUFSIZE - 1; + buf[len] = '\0'; + + if (os_strcmp(buf, "ATTACH") == 0) { + dst->attached = 1; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached"); + new_attached = 1; + reply_len = 2; + } else if (os_strcmp(buf, "DETACH") == 0) { + dst->attached = 0; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached"); + reply_len = 2; + } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6); + dst->debug_level = atoi(buf + 6); + reply_len = 2; + } else { + reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, + &reply_len); + } + + if (reply) { + send_buf = reply; + send_len = reply_len; + } else if (reply_len == 2) { + send_buf = "OK\n"; + send_len = 3; + } else { + send_buf = "FAIL\n"; + send_len = 5; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = os_malloc(send_len); + if (dst->rsp_buf == NULL) { + ctrl_close_pipe(dst); + os_free(reply); + return; + } + os_memcpy(dst->rsp_buf, send_buf, send_len); + os_free(reply); + + if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, + ctrl_iface_write_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d", + (int) GetLastError()); + ctrl_close_pipe(dst); + } else { + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", + dst); + } + + if (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); +} + + +static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d " + "bytes=%d", dst, (int) err, (int) bytes); + if (err == 0 && bytes > 0) + wpa_supplicant_ctrl_iface_rx(dst, bytes); +} + + +static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx) +{ + struct wpa_ctrl_dst *dst = eloop_data; + struct ctrl_iface_priv *priv = dst->priv; + DWORD bytes; + + wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive"); + ResetEvent(dst->overlap.hEvent); + + if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) { + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d", + (int) GetLastError()); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client " + "connected"); + + /* Open a new named pipe for the next client. */ + ctrl_open_pipe(priv); + + /* Use write completion function to start reading a command */ + ctrl_iface_write_completed(0, 0, &dst->overlap); + + ctrl_flush_broken_pipes(priv); +} + + +static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params) +{ + const char *sddl = NULL; + TCHAR *t_sddl; + + if (os_strncmp(params, "SDDL=", 5) == 0) + sddl = params + 5; + if (!sddl) { + sddl = os_strstr(params, " SDDL="); + if (sddl) + sddl += 6; + } + + if (!sddl) + return 0; + + wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl); + os_memset(&priv->attr, 0, sizeof(priv->attr)); + priv->attr.nLength = sizeof(priv->attr); + priv->attr.bInheritHandle = FALSE; + t_sddl = wpa_strdup_tchar(sddl); + if (t_sddl == NULL) + return -1; + if (!ConvertStringSecurityDescriptorToSecurityDescriptor( + t_sddl, SDDL_REVISION_1, + (PSECURITY_DESCRIPTOR *) (void *) + &priv->attr.lpSecurityDescriptor, + NULL)) { + os_free(t_sddl); + wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to " + "security descriptor: %d", + sddl, (int) GetLastError()); + return -1; + } + os_free(t_sddl); + + priv->sec_attr_set = 1; + + return 0; +} + + +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); +} + + +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; + priv->wpa_s = wpa_s; + + if (wpa_s->conf->ctrl_interface == NULL) + return priv; + + if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) { + os_free(priv); + return NULL; + } + + if (ctrl_open_pipe(priv) < 0) { + os_free(priv); + return NULL; + } + + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + return priv; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ + while (priv->ctrl_dst) + ctrl_close_pipe(priv->ctrl_dst); + if (priv->sec_attr_set) + LocalFree(priv->attr.lpSecurityDescriptor); + os_free(priv); +} + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx; + char *sbuf; + int llen; + DWORD written; + + dst = priv->ctrl_dst; + if (dst == NULL) + return; + + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + + llen = os_strlen(levelstr); + sbuf = os_malloc(llen + len); + if (sbuf == NULL) + return; + + os_memcpy(sbuf, levelstr, llen); + os_memcpy(sbuf + llen, buf, len); + + idx = 0; + while (dst) { + next = dst->next; + if (dst->attached && level >= dst->debug_level) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p", + dst); + if (!WriteFile(dst->pipe, sbuf, llen + len, &written, + NULL)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst " + "%p failed: %d", + dst, (int) GetLastError()); + dst->errors++; + if (dst->errors > 10) + ctrl_close_pipe(dst); + } else + dst->errors = 0; + } + idx++; + dst = next; + } + os_free(sbuf); +} + + +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ + wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", + priv->wpa_s->ifname); + if (priv->ctrl_dst == NULL) + return; + WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE); +} + + +/* Global ctrl_iface */ + +struct ctrl_iface_global_priv; + +struct wpa_global_dst { + /* Note: OVERLAPPED must be the first member of struct wpa_global_dst + */ + OVERLAPPED overlap; + struct wpa_global_dst *next, *prev; + struct ctrl_iface_global_priv *priv; + HANDLE pipe; + char req_buf[REQUEST_BUFSIZE]; + char *rsp_buf; + int used; +}; + +struct ctrl_iface_global_priv { + struct wpa_global *global; + struct wpa_global_dst *ctrl_dst; +}; + + +static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv) +{ + struct wpa_global_dst *dst, *next; + + dst = priv->ctrl_dst; + + while (dst) { + next = dst->next; + if (ctrl_broken_pipe(dst->pipe, dst->used)) { + wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p", + dst); + global_close_pipe(dst); + } + dst = next; + } +} + + +static int global_open_pipe(struct ctrl_iface_global_priv *priv) +{ + struct wpa_global_dst *dst; + DWORD err; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst); + + dst->priv = priv; + dst->pipe = INVALID_HANDLE_VALUE; + + dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (dst->overlap.hEvent == NULL) { + wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d", + (int) GetLastError()); + goto fail; + } + + eloop_register_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent), + wpa_supplicant_global_iface_receive, dst, NULL); + + /* TODO: add support for configuring access list for the pipe */ + dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT, + 10, REPLY_BUFSIZE, REQUEST_BUFSIZE, + 1000, NULL); + if (dst->pipe == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d", + (int) GetLastError()); + goto fail; + } + + if (ConnectNamedPipe(dst->pipe, &dst->overlap)) { + wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d", + (int) GetLastError()); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + err = GetLastError(); + switch (err) { + case ERROR_IO_PENDING: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in " + "progress"); + break; + case ERROR_PIPE_CONNECTED: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already " + "connected"); + if (SetEvent(dst->overlap.hEvent)) + break; + /* fall through */ + default: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d", + (int) err); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + dst->next = priv->ctrl_dst; + if (dst->next) + dst->next->prev = dst; + priv->ctrl_dst = dst; + + return 0; + +fail: + global_close_pipe(dst); + return -1; +} + + +static void global_close_pipe(struct wpa_global_dst *dst) +{ + wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst); + + if (dst->overlap.hEvent) { + eloop_unregister_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent)); + CloseHandle(dst->overlap.hEvent); + } + + if (dst->pipe != INVALID_HANDLE_VALUE) { + /* + * Could use FlushFileBuffers() here to guarantee that all data + * gets delivered to the client, but that can block, so let's + * not do this for now. + * FlushFileBuffers(dst->pipe); + */ + CloseHandle(dst->pipe); + } + + if (dst->prev) + dst->prev->next = dst->next; + else + dst->priv->ctrl_dst = dst->next; + if (dst->next) + dst->next->prev = dst->prev; + + os_free(dst->rsp_buf); + os_free(dst); +} + + +static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p " + "err=%d bytes=%d", dst, (int) err, (int) bytes); + if (err) { + global_close_pipe(dst); + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + + if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf), + &dst->overlap, global_iface_read_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d", + (int) GetLastError()); + global_close_pipe(dst); + /* FIX: if this was the pipe waiting for new global + * connections, at this point there are no open global pipes.. + * Should try to open a new pipe.. */ + return; + } + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst); +} + + +static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst, + size_t len) +{ + struct wpa_global *global = dst->priv->global; + char *reply = NULL, *send_buf; + size_t reply_len = 0, send_len; + char *buf = dst->req_buf; + + dst->used = 1; + if (len >= REQUEST_BUFSIZE) + len = REQUEST_BUFSIZE - 1; + buf[len] = '\0'; + + reply = wpa_supplicant_global_ctrl_iface_process(global, buf, + &reply_len); + if (reply) { + send_buf = reply; + send_len = reply_len; + } else if (reply_len) { + send_buf = "FAIL\n"; + send_len = 5; + } else { + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = os_malloc(send_len); + if (dst->rsp_buf == NULL) { + global_close_pipe(dst); + os_free(reply); + return; + } + os_memcpy(dst->rsp_buf, send_buf, send_len); + os_free(reply); + + if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, + global_iface_write_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d", + (int) GetLastError()); + global_close_pipe(dst); + } else { + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", + dst); + } +} + + +static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d " + "bytes=%d", dst, (int) err, (int) bytes); + if (err == 0 && bytes > 0) + wpa_supplicant_global_iface_rx(dst, bytes); +} + + +static void wpa_supplicant_global_iface_receive(void *eloop_data, + void *user_ctx) +{ + struct wpa_global_dst *dst = eloop_data; + struct ctrl_iface_global_priv *priv = dst->priv; + DWORD bytes; + + wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive"); + ResetEvent(dst->overlap.hEvent); + + if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) { + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d", + (int) GetLastError()); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client " + "connected"); + + /* Open a new named pipe for the next client. */ + if (global_open_pipe(priv) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed"); + return; + } + + /* Use write completion function to start reading a command */ + global_iface_write_completed(0, 0, &dst->overlap); + + global_flush_broken_pipes(priv); +} + + +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; + priv->global = global; + + if (global_open_pipe(priv) < 0) { + os_free(priv); + return NULL; + } + + return priv; +} + + +void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ + while (priv->ctrl_dst) + global_close_pipe(priv->ctrl_dst); + os_free(priv); +} diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c new file mode 100644 index 0000000..110ca4f --- /dev/null +++ b/wpa_supplicant/ctrl_iface_udp.c @@ -0,0 +1,561 @@ +/* + * WPA Supplicant / UDP socket -based control interface + * Copyright (c) 2004-2005, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "common/wpa_ctrl.h" + + +#define COOKIE_LEN 8 + +/* Per-interface ctrl_iface */ + +/** + * struct wpa_ctrl_dst - Internal data structure of control interface monitors + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. This data is private to + * ctrl_iface_udp.c and should not be touched directly from other files. + */ +struct wpa_ctrl_dst { + struct wpa_ctrl_dst *next; + struct sockaddr_in addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + + +struct ctrl_iface_priv { + struct wpa_supplicant *wpa_s; + int sock; + struct wpa_ctrl_dst *ctrl_dst; + u8 cookie[COOKIE_LEN]; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len); + + +static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, + struct sockaddr_in *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in)); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dst->next = priv->ctrl_dst; + priv->ctrl_dst = dst; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", + inet_ntoa(from->sin_addr), ntohs(from->sin_port)); + return 0; +} + + +static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, + struct sockaddr_in *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst, *prev = NULL; + + dst = priv->ctrl_dst; + while (dst) { + if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && + from->sin_port == dst->addr.sin_port) { + if (prev == NULL) + priv->ctrl_dst = dst->next; + else + prev->next = dst->next; + os_free(dst); + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " + "%s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + return 0; + } + prev = dst; + dst = dst->next; + } + return -1; +} + + +static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, + struct sockaddr_in *from, + socklen_t fromlen, + char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dst = priv->ctrl_dst; + while (dst) { + 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)); + dst->debug_level = atoi(level); + return 0; + } + dst = dst->next; + } + + return -1; +} + + +static char * +wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv, + size_t *reply_len) +{ + char *reply; + reply = os_malloc(7 + 2 * COOKIE_LEN + 1); + if (reply == NULL) { + *reply_len = 1; + return NULL; + } + + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + priv->cookie, COOKIE_LEN); + + *reply_len = 7 + 2 * COOKIE_LEN; + return reply; +} + + +static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct ctrl_iface_priv *priv = sock_ctx; + char buf[256], *pos; + int res; + struct sockaddr_in from; + socklen_t fromlen = sizeof(from); + char *reply = NULL; + size_t reply_len = 0; + int new_attached = 0; + u8 cookie[COOKIE_LEN]; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { + /* + * The OS networking stack is expected to drop this kind of + * frames since the socket is bound to only localhost address. + * Just in case, drop the frame if it is coming from any other + * address. + */ + wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " + "source %s", inet_ntoa(from.sin_addr)); + return; + } + buf[res] = '\0'; + + if (os_strcmp(buf, "GET_COOKIE") == 0) { + reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len); + goto done; + } + + /* + * Require that the client includes a prefix with the 'cookie' value + * fetched with GET_COOKIE command. This is used to verify that the + * client has access to a bidirectional link over UDP in order to + * avoid attacks using forged localhost IP address even if the OS does + * not block such frames from remote destinations. + */ + if (os_strncmp(buf, "COOKIE=", 7) != 0) { + wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " + "drop request"); + return; + } + + if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " + "request - drop request"); + return; + } + + if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " + "drop request"); + return; + } + + pos = buf + 7 + 2 * COOKIE_LEN; + while (*pos == ' ') + pos++; + + if (os_strcmp(pos, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) + reply_len = 1; + else { + new_attached = 1; + reply_len = 2; + } + } else if (os_strcmp(pos, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) + reply_len = 1; + else + reply_len = 2; + } else if (os_strncmp(pos, "LEVEL ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, + pos + 6)) + reply_len = 1; + else + reply_len = 2; + } else { + reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos, + &reply_len); + } + + done: + 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 (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); +} + + +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); +} + + +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + struct sockaddr_in addr; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->wpa_s = wpa_s; + priv->sock = -1; + os_get_random(priv->cookie, COOKIE_LEN); + + if (wpa_s->conf->ctrl_interface == NULL) + return priv; + + priv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (priv->sock < 0) { + perror("socket(PF_INET)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl((127 << 24) | 1); + addr.sin_port = htons(WPA_CTRL_IFACE_PORT); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(AF_INET)"); + goto fail; + } + + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, + wpa_s, priv); + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + return priv; + +fail: + if (priv->sock >= 0) + close(priv->sock); + os_free(priv); + return NULL; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst, *prev; + + if (priv->sock > -1) { + eloop_unregister_read_sock(priv->sock); + if (priv->ctrl_dst) { + /* + * Wait a second before closing the control socket if + * there are any attached monitors in order to allow + * them to receive any pending messages. + */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " + "monitors to receive messages"); + os_sleep(1, 0); + } + close(priv->sock); + priv->sock = -1; + } + + dst = priv->ctrl_dst; + while (dst) { + prev = dst; + dst = dst->next; + os_free(prev); + } + os_free(priv); +} + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx; + char *sbuf; + int llen; + + dst = priv->ctrl_dst; + if (priv->sock < 0 || dst == NULL) + return; + + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + + llen = os_strlen(levelstr); + sbuf = os_malloc(llen + len); + if (sbuf == NULL) + return; + + os_memcpy(sbuf, levelstr, llen); + os_memcpy(sbuf + llen, buf, len); + + idx = 0; + while (dst) { + next = dst->next; + if (level >= dst->debug_level) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", + inet_ntoa(dst->addr.sin_addr), + ntohs(dst->addr.sin_port)); + if (sendto(priv->sock, sbuf, llen + len, 0, + (struct sockaddr *) &dst->addr, + sizeof(dst->addr)) < 0) { + perror("sendto(CTRL_IFACE monitor)"); + dst->errors++; + if (dst->errors > 10) { + wpa_supplicant_ctrl_iface_detach( + priv, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + dst = next; + } + os_free(sbuf); +} + + +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ + wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", + priv->wpa_s->ifname); + eloop_wait_for_read_sock(priv->sock); +} + + +/* Global ctrl_iface */ + +struct ctrl_iface_global_priv { + int sock; + u8 cookie[COOKIE_LEN]; +}; + + +static char * +wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv, + size_t *reply_len) +{ + char *reply; + reply = os_malloc(7 + 2 * COOKIE_LEN + 1); + if (reply == NULL) { + *reply_len = 1; + return NULL; + } + + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + priv->cookie, COOKIE_LEN); + + *reply_len = 7 + 2 * COOKIE_LEN; + return reply; +} + + +static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_global *global = eloop_ctx; + struct ctrl_iface_global_priv *priv = sock_ctx; + char buf[256], *pos; + int res; + struct sockaddr_in from; + socklen_t fromlen = sizeof(from); + char *reply; + size_t reply_len; + u8 cookie[COOKIE_LEN]; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { + /* + * The OS networking stack is expected to drop this kind of + * frames since the socket is bound to only localhost address. + * Just in case, drop the frame if it is coming from any other + * address. + */ + wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " + "source %s", inet_ntoa(from.sin_addr)); + return; + } + buf[res] = '\0'; + + if (os_strcmp(buf, "GET_COOKIE") == 0) { + reply = wpa_supplicant_global_get_cookie(priv, &reply_len); + goto done; + } + + if (os_strncmp(buf, "COOKIE=", 7) != 0) { + wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " + "drop request"); + return; + } + + if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " + "request - drop request"); + return; + } + + if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " + "drop request"); + return; + } + + pos = buf + 7 + 2 * COOKIE_LEN; + while (*pos == ' ') + pos++; + + reply = wpa_supplicant_global_ctrl_iface_process(global, pos, + &reply_len); + + done: + if (reply) { + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen); + os_free(reply); + } else if (reply_len) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + } +} + + +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *priv; + struct sockaddr_in addr; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->sock = -1; + os_get_random(priv->cookie, COOKIE_LEN); + + if (global->params.ctrl_interface == NULL) + return priv; + + wpa_printf(MSG_DEBUG, "Global control interface '%s'", + global->params.ctrl_interface); + + priv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (priv->sock < 0) { + perror("socket(PF_INET)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl((127 << 24) | 1); + addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(AF_INET)"); + goto fail; + } + + eloop_register_read_sock(priv->sock, + wpa_supplicant_global_ctrl_iface_receive, + global, priv); + + return priv; + +fail: + if (priv->sock >= 0) + close(priv->sock); + os_free(priv); + return NULL; +} + + +void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ + if (priv->sock >= 0) { + eloop_unregister_read_sock(priv->sock); + close(priv->sock); + } + os_free(priv); +} diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c new file mode 100644 index 0000000..306a222 --- /dev/null +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -0,0 +1,734 @@ +/* + * WPA Supplicant / UNIX domain socket -based control interface + * Copyright (c) 2004-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. + */ + +#include "includes.h" +#include <sys/un.h> +#include <sys/stat.h> +#include <grp.h> +#include <stddef.h> +#ifdef ANDROID +#include <cutils/sockets.h> +#endif /* ANDROID */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" + +/* Per-interface ctrl_iface */ + +/** + * struct wpa_ctrl_dst - Internal data structure of control interface monitors + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. This data is private to + * ctrl_iface_unix.c and should not be touched directly from other files. + */ +struct wpa_ctrl_dst { + struct dl_list list; + struct sockaddr_un addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + + +struct ctrl_iface_priv { + struct wpa_supplicant *wpa_s; + int sock; + struct dl_list ctrl_dst; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len); + + +static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dl_list_add(&priv->ctrl_dst, &dst->list); + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", + (u8 *) from->sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)); + return 0; +} + + +static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) { + if (fromlen == dst->addrlen && + os_memcmp(from->sun_path, dst->addr.sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)) + == 0) { + dl_list_del(&dst->list); + os_free(dst); + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", + (u8 *) from->sun_path, + fromlen - + offsetof(struct sockaddr_un, sun_path)); + return 0; + } + } + return -1; +} + + +static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, + struct sockaddr_un *from, + socklen_t fromlen, + char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) { + if (fromlen == dst->addrlen && + os_memcmp(from->sun_path, dst->addr.sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)) + == 0) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " + "level", (u8 *) from->sun_path, + fromlen - + offsetof(struct sockaddr_un, sun_path)); + dst->debug_level = atoi(level); + return 0; + } + } + + return -1; +} + + +static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct ctrl_iface_priv *priv = sock_ctx; + char buf[4096]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char *reply = NULL; + size_t reply_len = 0; + int new_attached = 0; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + + if (os_strcmp(buf, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) + reply_len = 1; + else { + new_attached = 1; + reply_len = 2; + } + } else if (os_strcmp(buf, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) + reply_len = 1; + else + reply_len = 2; + } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, + buf + 6)) + reply_len = 1; + else + reply_len = 2; + } else { + reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, + &reply_len); + } + + 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 (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); +} + + +static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) +{ + char *buf; + size_t len; + char *pbuf, *dir = NULL, *gid_str = NULL; + int res; + + if (wpa_s->conf->ctrl_interface == NULL) + return NULL; + + pbuf = os_strdup(wpa_s->conf->ctrl_interface); + if (pbuf == NULL) + return NULL; + if (os_strncmp(pbuf, "DIR=", 4) == 0) { + dir = pbuf + 4; + gid_str = os_strstr(dir, " GROUP="); + if (gid_str) { + *gid_str = '\0'; + gid_str += 7; + } + } else + dir = pbuf; + + len = os_strlen(dir) + os_strlen(wpa_s->ifname) + 2; + buf = os_malloc(len); + if (buf == NULL) { + os_free(pbuf); + return NULL; + } + + res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname); + if (res < 0 || (size_t) res >= len) { + os_free(pbuf); + os_free(buf); + return NULL; + } +#ifdef __CYGWIN__ + { + /* Windows/WinPcap uses interface names that are not suitable + * as a file name - convert invalid chars to underscores */ + char *pos = buf; + while (*pos) { + if (*pos == '\\') + *pos = '_'; + pos++; + } + } +#endif /* __CYGWIN__ */ + os_free(pbuf); + return buf; +} + + +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); +} + + +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + struct sockaddr_un addr; + char *fname = NULL; + gid_t gid = 0; + int gid_set = 0; + char *buf, *dir = NULL, *gid_str = NULL; + struct group *grp; + char *endp; + + 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; +#ifdef ANDROID + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s", + wpa_s->conf->ctrl_interface); + priv->sock = android_get_control_socket(addr.sun_path); + if (priv->sock >= 0) + goto havesock; +#endif /* ANDROID */ + if (os_strncmp(buf, "DIR=", 4) == 0) { + dir = buf + 4; + gid_str = os_strstr(dir, " GROUP="); + if (gid_str) { + *gid_str = '\0'; + gid_str += 7; + } + } else { + dir = buf; + gid_str = wpa_s->conf->ctrl_interface_group; + } + + if (mkdir(dir, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + perror("mkdir[ctrl_interface]"); + goto fail; + } + } + + if (gid_str) { + grp = getgrnam(gid_str); + if (grp) { + gid = grp->gr_gid; + gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + (int) gid, gid_str); + } else { + /* Group name not found - try to parse this as gid */ + gid = strtol(gid_str, &endp, 10); + if (*gid_str == '\0' || *endp != '\0') { + wpa_printf(MSG_ERROR, "CTRL: Invalid group " + "'%s'", gid_str); + goto fail; + } + gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + (int) gid); + } + } + + if (gid_set && chown(dir, -1, gid) < 0) { + perror("chown[ctrl_interface]"); + goto fail; + } + + /* Make sure the group can enter and read the directory */ + if (gid_set && + chmod(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) < 0) { + wpa_printf(MSG_ERROR, "CTRL: chmod[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + + if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >= + sizeof(addr.sun_path)) { + wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded"); + goto fail; + } + + priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (priv->sock < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + fname = wpa_supplicant_ctrl_iface_path(wpa_s); + if (fname == NULL) + goto fail; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(priv->sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + fname); + goto fail; + } + if (bind(priv->sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (gid_set && chown(fname, -1, gid) < 0) { + perror("chown[ctrl_interface/ifname]"); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + perror("chmod[ctrl_interface/ifname]"); + goto fail; + } + os_free(fname); + +#ifdef ANDROID +havesock: +#endif /* ANDROID */ + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, + wpa_s, priv); + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + os_free(buf); + return priv; + +fail: + if (priv->sock >= 0) + close(priv->sock); + os_free(priv); + if (fname) { + unlink(fname); + os_free(fname); + } + os_free(buf); + return NULL; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst, *prev; + + if (priv->sock > -1) { + char *fname; + char *buf, *dir = NULL, *gid_str = NULL; + eloop_unregister_read_sock(priv->sock); + if (!dl_list_empty(&priv->ctrl_dst)) { + /* + * Wait a second before closing the control socket if + * there are any attached monitors in order to allow + * them to receive any pending messages. + */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " + "monitors to receive messages"); + os_sleep(1, 0); + } + close(priv->sock); + priv->sock = -1; + fname = wpa_supplicant_ctrl_iface_path(priv->wpa_s); + if (fname) { + unlink(fname); + os_free(fname); + } + + buf = os_strdup(priv->wpa_s->conf->ctrl_interface); + if (buf == NULL) + goto free_dst; + if (os_strncmp(buf, "DIR=", 4) == 0) { + dir = buf + 4; + gid_str = os_strstr(dir, " GROUP="); + if (gid_str) { + *gid_str = '\0'; + gid_str += 7; + } + } else + dir = buf; + + if (rmdir(dir) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + perror("rmdir[ctrl_interface]"); + } + } + os_free(buf); + } + +free_dst: + dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); + os_free(priv); +} + + +/** + * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors + * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() + * @level: Priority level of the message + * @buf: Message data + * @len: Message length + * + * Send a packet to all monitor programs attached to the control interface. + */ +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx, res; + struct msghdr msg; + struct iovec io[2]; + + if (priv->sock < 0 || dl_list_empty(&priv->ctrl_dst)) + return; + + res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + if (res < 0 || (size_t) res >= sizeof(levelstr)) + return; + io[0].iov_base = levelstr; + io[0].iov_len = os_strlen(levelstr); + io[1].iov_base = (char *) buf; + io[1].iov_len = len; + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + + idx = 0; + dl_list_for_each_safe(dst, next, &priv->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(priv->sock, &msg, 0) < 0) { + int _errno = errno; + wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " + "%d - %s", + idx, errno, strerror(errno)); + dst->errors++; + if (dst->errors > 1000 || + (_errno != ENOBUFS && dst->errors > 10) || + _errno == ENOENT) { + wpa_supplicant_ctrl_iface_detach( + priv, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + } +} + + +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + for (;;) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to " + "attach", priv->wpa_s->ifname); + eloop_wait_for_read_sock(priv->sock); + + res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + continue; + } + buf[res] = '\0'; + + if (os_strcmp(buf, "ATTACH") == 0) { + /* handle ATTACH signal of first monitor interface */ + if (!wpa_supplicant_ctrl_iface_attach(priv, &from, + fromlen)) { + sendto(priv->sock, "OK\n", 3, 0, + (struct sockaddr *) &from, fromlen); + /* OK to continue */ + return; + } else { + sendto(priv->sock, "FAIL\n", 5, 0, + (struct sockaddr *) &from, fromlen); + } + } else { + /* return FAIL for all other signals */ + sendto(priv->sock, "FAIL\n", 5, 0, + (struct sockaddr *) &from, fromlen); + } + } +} + + +/* Global ctrl_iface */ + +struct ctrl_iface_global_priv { + struct wpa_global *global; + int sock; +}; + + +static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_global *global = eloop_ctx; + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char *reply; + size_t reply_len; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + + reply = wpa_supplicant_global_ctrl_iface_process(global, buf, + &reply_len); + + if (reply) { + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen); + os_free(reply); + } else if (reply_len) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + } +} + + +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *priv; + struct sockaddr_un addr; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->global = global; + priv->sock = -1; + + if (global->params.ctrl_interface == NULL) + return priv; + +#ifdef ANDROID + priv->sock = android_get_control_socket(global->params.ctrl_interface); + if (priv->sock >= 0) + goto havesock; +#endif /* ANDROID */ + + wpa_printf(MSG_DEBUG, "Global control interface '%s'", + global->params.ctrl_interface); + + priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (priv->sock < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, global->params.ctrl_interface, + sizeof(addr.sun_path)); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + if (connect(priv->sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(global->params.ctrl_interface) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + global->params.ctrl_interface); + goto fail; + } + if (bind(priv->sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", + global->params.ctrl_interface); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", + global->params.ctrl_interface); + goto fail; + } + } + +#ifdef ANDROID +havesock: +#endif /* ANDROID */ + eloop_register_read_sock(priv->sock, + wpa_supplicant_global_ctrl_iface_receive, + global, NULL); + + return priv; + +fail: + if (priv->sock >= 0) + close(priv->sock); + os_free(priv); + return NULL; +} + + +void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ + if (priv->sock >= 0) { + eloop_unregister_read_sock(priv->sock); + close(priv->sock); + } + if (priv->global->params.ctrl_interface) + unlink(priv->global->params.ctrl_interface); + os_free(priv); +} diff --git a/wpa_supplicant/dbus/.gitignore b/wpa_supplicant/dbus/.gitignore new file mode 100644 index 0000000..6db2468 --- /dev/null +++ b/wpa_supplicant/dbus/.gitignore @@ -0,0 +1 @@ +libwpadbus.a diff --git a/wpa_supplicant/dbus/Makefile b/wpa_supplicant/dbus/Makefile new file mode 100644 index 0000000..cfaf58d --- /dev/null +++ b/wpa_supplicant/dbus/Makefile @@ -0,0 +1,84 @@ +all: libwpadbus.a + +clean: + rm -f *~ *.o *.d + rm -f libwpadbus.a + +install: + @echo Nothing to be made. + +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +CFLAGS += -I../../src -I../../src/utils + + +Q=@ +E=echo +ifeq ($(V), 1) +Q= +E=true +endif + +%.o: %.c + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + @$(E) " CC " $< + + +ifdef CONFIG_WPS +CFLAGS += -DCONFIG_WPS +endif + +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS + +ifndef DBUS_LIBS +DBUS_LIBS := $(shell pkg-config --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +endif +ifdef CONFIG_CTRL_IFACE_DBUS_INTRO +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +DBUS_INCLUDE += $(shell xml2-config --cflags) +DBUS_LIBS += $(shell xml2-config --libs) +endif + +dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1)) +DBUS_VERSION_MAJOR=$(word 1,$(dbus_version)) +DBUS_VERSION_MINOR=$(word 2,$(dbus_version)) +ifeq ($(DBUS_VERSION_MAJOR),) +DBUS_VERSION_MAJOR=0 +endif +ifeq ($(DBUS_VERSION_MINOR),) +DBUS_VERSION_MINOR=0 +endif +DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR) +DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR) + +CFLAGS += $(DBUS_INCLUDE) + +LIB_OBJS= \ + dbus_common.o \ + dbus_old.o \ + dbus_old_handlers.o \ + dbus_new.o \ + dbus_new_handlers.o \ + dbus_new_helpers.o \ + dbus_new_introspect.o \ + dbus_dict_helpers.o + +ifdef CONFIG_WPS +LIB_OBJS += dbus_old_handlers_wps.o +LIB_OBJS += dbus_new_handlers_wps.o +endif + +libwpadbus.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf new file mode 100644 index 0000000..c091234 --- /dev/null +++ b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf @@ -0,0 +1,27 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy user="root"> + <allow own="fi.epitest.hostap.WPASupplicant"/> + + <allow send_destination="fi.epitest.hostap.WPASupplicant"/> + <allow send_interface="fi.epitest.hostap.WPASupplicant"/> + + <allow own="fi.w1.wpa_supplicant1"/> + + <allow send_destination="fi.w1.wpa_supplicant1"/> + <allow send_interface="fi.w1.wpa_supplicant1"/> + <allow receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/> + </policy> + <policy context="default"> + <deny own="fi.epitest.hostap.WPASupplicant"/> + <deny send_destination="fi.epitest.hostap.WPASupplicant"/> + <deny send_interface="fi.epitest.hostap.WPASupplicant"/> + + <deny own="fi.w1.wpa_supplicant1"/> + <deny send_destination="fi.w1.wpa_supplicant1"/> + <deny send_interface="fi.w1.wpa_supplicant1"/> + <deny receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/> + </policy> +</busconfig> diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c new file mode 100644 index 0000000..5850636 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_common.c @@ -0,0 +1,371 @@ +/* + * wpa_supplicant D-Bus control interface - common functionality + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> + * 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. + */ + +#include "utils/includes.h" +#include <dbus/dbus.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "dbus_common.h" +#include "dbus_common_i.h" +#include "dbus_new.h" +#include "dbus_old.h" + + +#ifndef SIGPOLL +#ifdef SIGIO +/* + * If we do not have SIGPOLL, try to use SIGIO instead. This is needed for + * FreeBSD. + */ +#define SIGPOLL SIGIO +#endif +#endif + + +static void dispatch_data(DBusConnection *con) +{ + while (dbus_connection_get_dispatch_status(con) == + DBUS_DISPATCH_DATA_REMAINS) + dbus_connection_dispatch(con); +} + + +/** + * dispatch_initial_dbus_messages - Dispatch initial dbus messages after + * claiming bus name + * @eloop_ctx: the DBusConnection to dispatch on + * @timeout_ctx: unused + * + * If clients are quick to notice that service claimed its bus name, + * there may have been messages that came in before initialization was + * all finished. Dispatch those here. + */ +static void dispatch_initial_dbus_messages(void *eloop_ctx, void *timeout_ctx) +{ + DBusConnection *con = eloop_ctx; + dispatch_data(con); +} + + +static void process_watch(struct wpas_dbus_priv *priv, + DBusWatch *watch, eloop_event_type type) +{ + dbus_connection_ref(priv->con); + + priv->should_dispatch = 0; + + if (type == EVENT_TYPE_READ) + dbus_watch_handle(watch, DBUS_WATCH_READABLE); + else if (type == EVENT_TYPE_WRITE) + dbus_watch_handle(watch, DBUS_WATCH_WRITABLE); + else if (type == EVENT_TYPE_EXCEPTION) + dbus_watch_handle(watch, DBUS_WATCH_ERROR); + + if (priv->should_dispatch) { + dispatch_data(priv->con); + priv->should_dispatch = 0; + } + + dbus_connection_unref(priv->con); +} + + +static void process_watch_exception(int sock, void *eloop_ctx, void *sock_ctx) +{ + process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_EXCEPTION); +} + + +static void process_watch_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_READ); +} + + +static void process_watch_write(int sock, void *eloop_ctx, void *sock_ctx) +{ + process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_WRITE); +} + + +static dbus_bool_t add_watch(DBusWatch *watch, void *data) +{ + struct wpas_dbus_priv *priv = data; + unsigned int flags; + int fd; + + if (!dbus_watch_get_enabled(watch)) + return TRUE; + + flags = dbus_watch_get_flags(watch); + fd = dbus_watch_get_unix_fd(watch); + + eloop_register_sock(fd, EVENT_TYPE_EXCEPTION, process_watch_exception, + priv, watch); + + if (flags & DBUS_WATCH_READABLE) { + eloop_register_sock(fd, EVENT_TYPE_READ, process_watch_read, + priv, watch); + } + if (flags & DBUS_WATCH_WRITABLE) { + eloop_register_sock(fd, EVENT_TYPE_WRITE, process_watch_write, + priv, watch); + } + + dbus_watch_set_data(watch, priv, NULL); + + return TRUE; +} + + +static void remove_watch(DBusWatch *watch, void *data) +{ + unsigned int flags; + int fd; + + flags = dbus_watch_get_flags(watch); + fd = dbus_watch_get_unix_fd(watch); + + eloop_unregister_sock(fd, EVENT_TYPE_EXCEPTION); + + if (flags & DBUS_WATCH_READABLE) + eloop_unregister_sock(fd, EVENT_TYPE_READ); + if (flags & DBUS_WATCH_WRITABLE) + eloop_unregister_sock(fd, EVENT_TYPE_WRITE); + + dbus_watch_set_data(watch, NULL, NULL); +} + + +static void watch_toggled(DBusWatch *watch, void *data) +{ + if (dbus_watch_get_enabled(watch)) + add_watch(watch, data); + else + remove_watch(watch, data); +} + + +static void process_timeout(void *eloop_ctx, void *sock_ctx) +{ + DBusTimeout *timeout = sock_ctx; + dbus_timeout_handle(timeout); +} + + +static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) +{ + struct wpas_dbus_priv *priv = data; + if (!dbus_timeout_get_enabled(timeout)) + return TRUE; + + eloop_register_timeout(0, dbus_timeout_get_interval(timeout) * 1000, + process_timeout, priv, timeout); + + dbus_timeout_set_data(timeout, priv, NULL); + + return TRUE; +} + + +static void remove_timeout(DBusTimeout *timeout, void *data) +{ + struct wpas_dbus_priv *priv = data; + eloop_cancel_timeout(process_timeout, priv, timeout); + dbus_timeout_set_data(timeout, NULL, NULL); +} + + +static void timeout_toggled(DBusTimeout *timeout, void *data) +{ + if (dbus_timeout_get_enabled(timeout)) + add_timeout(timeout, data); + else + remove_timeout(timeout, data); +} + + +static void process_wakeup_main(int sig, void *signal_ctx) +{ + struct wpas_dbus_priv *priv = signal_ctx; + + if (sig != SIGPOLL || !priv->con) + return; + + if (dbus_connection_get_dispatch_status(priv->con) != + DBUS_DISPATCH_DATA_REMAINS) + return; + + /* Only dispatch once - we do not want to starve other events */ + dbus_connection_ref(priv->con); + dbus_connection_dispatch(priv->con); + dbus_connection_unref(priv->con); +} + + +/** + * wakeup_main - Attempt to wake our mainloop up + * @data: dbus control interface private data + * + * Try to wake up the main eloop so it will process + * dbus events that may have happened. + */ +static void wakeup_main(void *data) +{ + struct wpas_dbus_priv *priv = data; + + /* Use SIGPOLL to break out of the eloop select() */ + raise(SIGPOLL); + priv->should_dispatch = 1; +} + + +/** + * integrate_with_eloop - Register our mainloop integration with dbus + * @connection: connection to the system message bus + * @priv: a dbus control interface data structure + * Returns: 0 on success, -1 on failure + */ +static int integrate_with_eloop(struct wpas_dbus_priv *priv) +{ + if (!dbus_connection_set_watch_functions(priv->con, add_watch, + remove_watch, watch_toggled, + priv, NULL) || + !dbus_connection_set_timeout_functions(priv->con, add_timeout, + remove_timeout, + timeout_toggled, priv, + NULL)) { + wpa_printf(MSG_ERROR, "dbus: Failed to set callback " + "functions"); + return -1; + } + + if (eloop_register_signal(SIGPOLL, process_wakeup_main, priv)) + return -1; + dbus_connection_set_wakeup_main_function(priv->con, wakeup_main, + priv, NULL); + + return 0; +} + + +static int wpas_dbus_init_common(struct wpas_dbus_priv *priv) +{ + DBusError error; + int ret = 0; + + /* Get a reference to the system bus */ + dbus_error_init(&error); + priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (!priv->con) { + wpa_printf(MSG_ERROR, "dbus: Could not acquire the system " + "bus: %s - %s", error.name, error.message); + ret = -1; + } + dbus_error_free(&error); + + return ret; +} + + +static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv) +{ + /* Tell dbus about our mainloop integration functions */ + integrate_with_eloop(priv); + + /* + * Dispatch initial DBus messages that may have come in since the bus + * name was claimed above. Happens when clients are quick to notice the + * service. + * + * FIXME: is there a better solution to this problem? + */ + eloop_register_timeout(0, 50, dispatch_initial_dbus_messages, + priv->con, NULL); + + return 0; +} + + +static void wpas_dbus_deinit_common(struct wpas_dbus_priv *priv) +{ + if (priv->con) { + eloop_cancel_timeout(dispatch_initial_dbus_messages, + priv->con, NULL); + dbus_connection_set_watch_functions(priv->con, NULL, NULL, + NULL, NULL, NULL); + dbus_connection_set_timeout_functions(priv->con, NULL, NULL, + NULL, NULL, NULL); + dbus_connection_unref(priv->con); + } + + os_free(priv); +} + + +struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global) +{ + struct wpas_dbus_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->global = global; + + if (wpas_dbus_init_common(priv) < 0) { + wpas_dbus_deinit(priv); + return NULL; + } + +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW + if (wpas_dbus_ctrl_iface_init(priv) < 0) { + wpas_dbus_deinit(priv); + return NULL; + } +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ + +#ifdef CONFIG_CTRL_IFACE_DBUS + if (wpa_supplicant_dbus_ctrl_iface_init(priv) < 0) { + wpas_dbus_deinit(priv); + return NULL; + } +#endif /* CONFIG_CTRL_IFACE_DBUS */ + + if (wpas_dbus_init_common_finish(priv) < 0) { + wpas_dbus_deinit(priv); + return NULL; + } + + return priv; +} + + +void wpas_dbus_deinit(struct wpas_dbus_priv *priv) +{ + if (priv == NULL) + return; + +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW + wpas_dbus_ctrl_iface_deinit(priv); +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ + +#ifdef CONFIG_CTRL_IFACE_DBUS + /* TODO: is any deinit needed? */ +#endif /* CONFIG_CTRL_IFACE_DBUS */ + + wpas_dbus_deinit_common(priv); +} diff --git a/wpa_supplicant/dbus/dbus_common.h b/wpa_supplicant/dbus/dbus_common.h new file mode 100644 index 0000000..50da09b --- /dev/null +++ b/wpa_supplicant/dbus/dbus_common.h @@ -0,0 +1,26 @@ +/* + * wpa_supplicant D-Bus control interface - common definitions + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> + * 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. + */ + +#ifndef DBUS_COMMON_H +#define DBUS_COMMON_H + +struct wpas_dbus_priv; +struct wpa_global; + +struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global); +void wpas_dbus_deinit(struct wpas_dbus_priv *priv); + +#endif /* DBUS_COMMON_H */ diff --git a/wpa_supplicant/dbus/dbus_common_i.h b/wpa_supplicant/dbus/dbus_common_i.h new file mode 100644 index 0000000..9dab1ee --- /dev/null +++ b/wpa_supplicant/dbus/dbus_common_i.h @@ -0,0 +1,30 @@ +/* + * wpa_supplicant D-Bus control interface - internal definitions + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> + * 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. + */ + +#ifndef DBUS_COMMON_I_H +#define DBUS_COMMON_I_H + +#include <dbus/dbus.h> + +struct wpas_dbus_priv { + DBusConnection *con; + int should_dispatch; + struct wpa_global *global; + u32 next_objid; + int dbus_new_initialized; +}; + +#endif /* DBUS_COMMON_I_H */ diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c new file mode 100644 index 0000000..d900487 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -0,0 +1,923 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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. + */ + +#include "includes.h" +#include <dbus/dbus.h> + +#include "common.h" +#include "dbus_dict_helpers.h" + + +/** + * Start a dict in a dbus message. Should be paired with a call to + * wpa_dbus_dict_close_write(). + * + * @param iter A valid dbus message iterator + * @param iter_dict (out) A dict iterator to pass to further dict functions + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_open_write(DBusMessageIter *iter, + DBusMessageIter *iter_dict) +{ + dbus_bool_t result; + + if (!iter || !iter_dict) + return FALSE; + + result = dbus_message_iter_open_container( + iter, + DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + iter_dict); + return result; +} + + +/** + * End a dict element in a dbus message. Should be paired with + * a call to wpa_dbus_dict_open_write(). + * + * @param iter valid dbus message iterator, same as passed to + * wpa_dbus_dict_open_write() + * @param iter_dict a dbus dict iterator returned from + * wpa_dbus_dict_open_write() + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter, + DBusMessageIter *iter_dict) +{ + if (!iter || !iter_dict) + return FALSE; + + return dbus_message_iter_close_container(iter, iter_dict); +} + + +const char * wpa_dbus_type_as_string(const int type) +{ + switch(type) { + case DBUS_TYPE_BYTE: + return DBUS_TYPE_BYTE_AS_STRING; + case DBUS_TYPE_BOOLEAN: + return DBUS_TYPE_BOOLEAN_AS_STRING; + case DBUS_TYPE_INT16: + return DBUS_TYPE_INT16_AS_STRING; + case DBUS_TYPE_UINT16: + return DBUS_TYPE_UINT16_AS_STRING; + case DBUS_TYPE_INT32: + return DBUS_TYPE_INT32_AS_STRING; + case DBUS_TYPE_UINT32: + return DBUS_TYPE_UINT32_AS_STRING; + case DBUS_TYPE_INT64: + return DBUS_TYPE_INT64_AS_STRING; + case DBUS_TYPE_UINT64: + return DBUS_TYPE_UINT64_AS_STRING; + case DBUS_TYPE_DOUBLE: + return DBUS_TYPE_DOUBLE_AS_STRING; + case DBUS_TYPE_STRING: + return DBUS_TYPE_STRING_AS_STRING; + case DBUS_TYPE_OBJECT_PATH: + return DBUS_TYPE_OBJECT_PATH_AS_STRING; + case DBUS_TYPE_ARRAY: + return DBUS_TYPE_ARRAY_AS_STRING; + default: + return NULL; + } +} + + +static dbus_bool_t _wpa_dbus_add_dict_entry_start( + DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_entry, + const char *key, const int value_type) +{ + if (!dbus_message_iter_open_container(iter_dict, + DBUS_TYPE_DICT_ENTRY, NULL, + iter_dict_entry)) + return FALSE; + + if (!dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING, + &key)) + return FALSE; + + return TRUE; +} + + +static dbus_bool_t _wpa_dbus_add_dict_entry_end( + DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val) +{ + if (!dbus_message_iter_close_container(iter_dict_entry, iter_dict_val)) + return FALSE; + if (!dbus_message_iter_close_container(iter_dict, iter_dict_entry)) + return FALSE; + + return TRUE; +} + + +static dbus_bool_t _wpa_dbus_add_dict_entry_basic(DBusMessageIter *iter_dict, + const char *key, + const int value_type, + const void *value) +{ + DBusMessageIter iter_dict_entry, iter_dict_val; + const char *type_as_string = NULL; + + if (key == NULL) + return FALSE; + + type_as_string = wpa_dbus_type_as_string(value_type); + if (!type_as_string) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry, + key, value_type)) + return FALSE; + + if (!dbus_message_iter_open_container(&iter_dict_entry, + DBUS_TYPE_VARIANT, + type_as_string, &iter_dict_val)) + return FALSE; + + if (!dbus_message_iter_append_basic(&iter_dict_val, value_type, value)) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, + &iter_dict_val)) + return FALSE; + + return TRUE; +} + + +static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array( + DBusMessageIter *iter_dict, const char *key, + const char *value, const dbus_uint32_t value_len) +{ + DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; + dbus_uint32_t i; + + if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry, + key, DBUS_TYPE_ARRAY)) + return FALSE; + + if (!dbus_message_iter_open_container(&iter_dict_entry, + DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING, + &iter_dict_val)) + return FALSE; + + if (!dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &iter_array)) + return FALSE; + + for (i = 0; i < value_len; i++) { + if (!dbus_message_iter_append_basic(&iter_array, + DBUS_TYPE_BYTE, + &(value[i]))) + return FALSE; + } + + if (!dbus_message_iter_close_container(&iter_dict_val, &iter_array)) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, + &iter_dict_val)) + return FALSE; + + return TRUE; +} + + +/** + * Add a string entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The string value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict, + const char *key, const char *value) +{ + if (!value) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_STRING, + &value); +} + + +/** + * Add a byte entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The byte value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict, + const char *key, const char value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_BYTE, + &value); +} + + +/** + * Add a boolean entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The boolean value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict, + const char *key, const dbus_bool_t value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, + DBUS_TYPE_BOOLEAN, &value); +} + + +/** + * Add a 16-bit signed integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 16-bit signed integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_int16(DBusMessageIter *iter_dict, + const char *key, + const dbus_int16_t value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT16, + &value); +} + + +/** + * Add a 16-bit unsigned integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 16-bit unsigned integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_uint16(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint16_t value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT16, + &value); +} + + +/** + * Add a 32-bit signed integer to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 32-bit signed integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_int32(DBusMessageIter *iter_dict, + const char *key, + const dbus_int32_t value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT32, + &value); +} + + +/** + * Add a 32-bit unsigned integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 32-bit unsigned integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint32_t value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT32, + &value); +} + + +/** + * Add a 64-bit integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 64-bit integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict, + const char *key, + const dbus_int64_t value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT64, + &value); +} + + +/** + * Add a 64-bit unsigned integer entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The 64-bit unsigned integer value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint64_t value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64, + &value); +} + + +/** + * Add a double-precision floating point entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The double-precision floating point value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict, + const char *key, const double value) +{ + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE, + &value); +} + + +/** + * Add a DBus object path entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The DBus object path value + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict, + const char *key, + const char *value) +{ + if (!value) + return FALSE; + return _wpa_dbus_add_dict_entry_basic(iter_dict, key, + DBUS_TYPE_OBJECT_PATH, &value); +} + + +/** + * Add a byte array entry to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param value The byte array + * @param value_len The length of the byte array, in bytes + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, + const char *key, + const char *value, + const dbus_uint32_t value_len) +{ + if (!key) + return FALSE; + if (!value && (value_len != 0)) + return FALSE; + return _wpa_dbus_add_dict_entry_byte_array(iter_dict, key, value, + value_len); +} + + +/** + * Begin a string array entry in the dict + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param iter_dict_entry A private DBusMessageIter provided by the caller to + * be passed to wpa_dbus_dict_end_string_array() + * @param iter_dict_val A private DBusMessageIter provided by the caller to + * be passed to wpa_dbus_dict_end_string_array() + * @param iter_array On return, the DBusMessageIter to be passed to + * wpa_dbus_dict_string_array_add_element() + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, + const char *key, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array) +{ + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry, + key, DBUS_TYPE_ARRAY)) + return FALSE; + + if (!dbus_message_iter_open_container(iter_dict_entry, + DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_STRING_AS_STRING, + iter_dict_val)) + return FALSE; + + if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, + iter_array)) + return FALSE; + + return TRUE; +} + + +/** + * Add a single string element to a string array dict entry + * + * @param iter_array A valid DBusMessageIter returned from + * wpa_dbus_dict_begin_string_array()'s + * iter_array parameter + * @param elem The string element to be added to the dict entry's string array + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array, + const char *elem) +{ + if (!iter_array || !elem) + return FALSE; + + return dbus_message_iter_append_basic(iter_array, DBUS_TYPE_STRING, + &elem); +} + + +/** + * End a string array dict entry + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param iter_dict_entry A private DBusMessageIter returned from + * wpa_dbus_dict_end_string_array() + * @param iter_dict_val A private DBusMessageIter returned from + * wpa_dbus_dict_end_string_array() + * @param iter_array A DBusMessageIter returned from + * wpa_dbus_dict_end_string_array() + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array) +{ + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) + return FALSE; + + if (!dbus_message_iter_close_container(iter_dict_val, iter_array)) + return FALSE; + + if (!_wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry, + iter_dict_val)) + return FALSE; + + return TRUE; +} + + +/** + * Convenience function to add an entire string array to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param items The array of strings + * @param num_items The number of strings in the array + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, + const char *key, + const char **items, + const dbus_uint32_t num_items) +{ + DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; + dbus_uint32_t i; + + if (!key) + return FALSE; + if (!items && (num_items != 0)) + return FALSE; + + if (!wpa_dbus_dict_begin_string_array(iter_dict, key, + &iter_dict_entry, &iter_dict_val, + &iter_array)) + return FALSE; + + for (i = 0; i < num_items; i++) { + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + items[i])) + return FALSE; + } + + if (!wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry, + &iter_dict_val, &iter_array)) + return FALSE; + + return TRUE; +} + + +/*****************************************************/ +/* Stuff for reading dicts */ +/*****************************************************/ + +/** + * Start reading from a dbus dict. + * + * @param iter A valid DBusMessageIter pointing to the start of the dict + * @param iter_dict (out) A DBusMessageIter to be passed to + * wpa_dbus_dict_read_next_entry() + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, + DBusMessageIter *iter_dict) +{ + if (!iter || !iter_dict) + return FALSE; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) + return FALSE; + + dbus_message_iter_recurse(iter, iter_dict); + return TRUE; +} + + +#define BYTE_ARRAY_CHUNK_SIZE 34 +#define BYTE_ARRAY_ITEM_SIZE (sizeof(char)) + +static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( + DBusMessageIter *iter, int array_type, + struct wpa_dbus_dict_entry *entry) +{ + dbus_uint32_t count = 0; + dbus_bool_t success = FALSE; + char *buffer, *nbuffer;; + + entry->bytearray_value = NULL; + entry->array_type = DBUS_TYPE_BYTE; + + buffer = os_zalloc(BYTE_ARRAY_ITEM_SIZE * BYTE_ARRAY_CHUNK_SIZE); + if (!buffer) + return FALSE; + + entry->bytearray_value = buffer; + entry->array_len = 0; + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BYTE) { + char byte; + + if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) { + nbuffer = os_realloc(buffer, BYTE_ARRAY_ITEM_SIZE * + (count + BYTE_ARRAY_CHUNK_SIZE)); + if (nbuffer == NULL) { + os_free(buffer); + wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" + "entry_get_byte_array out of " + "memory trying to retrieve the " + "string array"); + goto done; + } + buffer = nbuffer; + } + entry->bytearray_value = buffer; + + dbus_message_iter_get_basic(iter, &byte); + entry->bytearray_value[count] = byte; + entry->array_len = ++count; + dbus_message_iter_next(iter); + } + + /* Zero-length arrays are valid. */ + if (entry->array_len == 0) { + os_free(entry->bytearray_value); + entry->bytearray_value = NULL; + } + + success = TRUE; + +done: + return success; +} + + +#define STR_ARRAY_CHUNK_SIZE 8 +#define STR_ARRAY_ITEM_SIZE (sizeof(char *)) + +static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( + DBusMessageIter *iter, int array_type, + struct wpa_dbus_dict_entry *entry) +{ + dbus_uint32_t count = 0; + dbus_bool_t success = FALSE; + char **buffer, **nbuffer; + + entry->strarray_value = NULL; + entry->array_type = DBUS_TYPE_STRING; + + buffer = os_zalloc(STR_ARRAY_ITEM_SIZE * STR_ARRAY_CHUNK_SIZE); + if (buffer == NULL) + return FALSE; + + entry->strarray_value = buffer; + entry->array_len = 0; + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) { + const char *value; + char *str; + + if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) { + nbuffer = os_realloc(buffer, STR_ARRAY_ITEM_SIZE * + (count + STR_ARRAY_CHUNK_SIZE)); + if (nbuffer == NULL) { + os_free(buffer); + wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" + "entry_get_string_array out of " + "memory trying to retrieve the " + "string array"); + goto done; + } + buffer = nbuffer; + } + entry->strarray_value = buffer; + + dbus_message_iter_get_basic(iter, &value); + str = os_strdup(value); + if (str == NULL) { + wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_entry_get_" + "string_array out of memory trying to " + "duplicate the string array"); + goto done; + } + entry->strarray_value[count] = str; + entry->array_len = ++count; + dbus_message_iter_next(iter); + } + + /* Zero-length arrays are valid. */ + if (entry->array_len == 0) { + os_free(entry->strarray_value); + entry->strarray_value = NULL; + } + + success = TRUE; + +done: + return success; +} + + +static dbus_bool_t _wpa_dbus_dict_entry_get_array( + DBusMessageIter *iter_dict_val, struct wpa_dbus_dict_entry *entry) +{ + int array_type = dbus_message_iter_get_element_type(iter_dict_val); + dbus_bool_t success = FALSE; + DBusMessageIter iter_array; + + if (!entry) + return FALSE; + + dbus_message_iter_recurse(iter_dict_val, &iter_array); + + switch (array_type) { + case DBUS_TYPE_BYTE: + success = _wpa_dbus_dict_entry_get_byte_array(&iter_array, + array_type, + entry); + break; + case DBUS_TYPE_STRING: + success = _wpa_dbus_dict_entry_get_string_array(&iter_array, + array_type, + entry); + break; + default: + break; + } + + return success; +} + + +static dbus_bool_t _wpa_dbus_dict_fill_value_from_variant( + struct wpa_dbus_dict_entry *entry, DBusMessageIter *iter) +{ + const char *v; + + switch (entry->type) { + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_STRING: + dbus_message_iter_get_basic(iter, &v); + entry->str_value = os_strdup(v); + if (entry->str_value == NULL) + return FALSE; + break; + case DBUS_TYPE_BOOLEAN: + dbus_message_iter_get_basic(iter, &entry->bool_value); + break; + case DBUS_TYPE_BYTE: + dbus_message_iter_get_basic(iter, &entry->byte_value); + break; + case DBUS_TYPE_INT16: + dbus_message_iter_get_basic(iter, &entry->int16_value); + break; + case DBUS_TYPE_UINT16: + dbus_message_iter_get_basic(iter, &entry->uint16_value); + break; + case DBUS_TYPE_INT32: + dbus_message_iter_get_basic(iter, &entry->int32_value); + break; + case DBUS_TYPE_UINT32: + dbus_message_iter_get_basic(iter, &entry->uint32_value); + break; + case DBUS_TYPE_INT64: + dbus_message_iter_get_basic(iter, &entry->int64_value); + break; + case DBUS_TYPE_UINT64: + dbus_message_iter_get_basic(iter, &entry->uint64_value); + break; + case DBUS_TYPE_DOUBLE: + dbus_message_iter_get_basic(iter, &entry->double_value); + break; + case DBUS_TYPE_ARRAY: + return _wpa_dbus_dict_entry_get_array(iter, entry); + default: + return FALSE; + } + + return TRUE; +} + + +/** + * Read the current key/value entry from the dict. Entries are dynamically + * allocated when needed and must be freed after use with the + * wpa_dbus_dict_entry_clear() function. + * + * The returned entry object will be filled with the type and value of the next + * entry in the dict, or the type will be DBUS_TYPE_INVALID if an error + * occurred. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_read() + * @param entry A valid dict entry object into which the dict key and value + * will be placed + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict, + struct wpa_dbus_dict_entry * entry) +{ + DBusMessageIter iter_dict_entry, iter_dict_val; + int type; + const char *key; + + if (!iter_dict || !entry) + goto error; + + if (dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) + goto error; + + dbus_message_iter_recurse(iter_dict, &iter_dict_entry); + dbus_message_iter_get_basic(&iter_dict_entry, &key); + entry->key = key; + + if (!dbus_message_iter_next(&iter_dict_entry)) + goto error; + type = dbus_message_iter_get_arg_type(&iter_dict_entry); + if (type != DBUS_TYPE_VARIANT) + goto error; + + dbus_message_iter_recurse(&iter_dict_entry, &iter_dict_val); + entry->type = dbus_message_iter_get_arg_type(&iter_dict_val); + if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) + goto error; + + dbus_message_iter_next(iter_dict); + return TRUE; + +error: + if (entry) { + wpa_dbus_dict_entry_clear(entry); + entry->type = DBUS_TYPE_INVALID; + entry->array_type = DBUS_TYPE_INVALID; + } + + return FALSE; +} + + +/** + * Return whether or not there are additional dictionary entries. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_read() + * @return TRUE if more dict entries exists, FALSE if no more dict entries + * exist + */ +dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict) +{ + if (!iter_dict) + return FALSE; + return dbus_message_iter_get_arg_type(iter_dict) == + DBUS_TYPE_DICT_ENTRY; +} + + +/** + * Free any memory used by the entry object. + * + * @param entry The entry object + */ +void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry) +{ + unsigned int i; + + if (!entry) + return; + switch (entry->type) { + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_STRING: + os_free(entry->str_value); + break; + case DBUS_TYPE_ARRAY: + switch (entry->array_type) { + case DBUS_TYPE_BYTE: + os_free(entry->bytearray_value); + break; + case DBUS_TYPE_STRING: + for (i = 0; i < entry->array_len; i++) + os_free(entry->strarray_value[i]); + os_free(entry->strarray_value); + break; + } + break; + } + + memset(entry, 0, sizeof(struct wpa_dbus_dict_entry)); +} diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h new file mode 100644 index 0000000..eb31575 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_dict_helpers.h @@ -0,0 +1,137 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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. + */ + +#ifndef DBUS_DICT_HELPERS_H +#define DBUS_DICT_HELPERS_H + +/* + * Adding a dict to a DBusMessage + */ + +dbus_bool_t wpa_dbus_dict_open_write(DBusMessageIter *iter, + DBusMessageIter *iter_dict); + +dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter, + DBusMessageIter *iter_dict); + +const char * wpa_dbus_type_as_string(const int type); + +dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict, + const char *key, const char *value); + +dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict, + const char *key, const char value); + +dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict, + const char *key, + const dbus_bool_t value); + +dbus_bool_t wpa_dbus_dict_append_int16(DBusMessageIter *iter_dict, + const char *key, + const dbus_int16_t value); + +dbus_bool_t wpa_dbus_dict_append_uint16(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint16_t value); + +dbus_bool_t wpa_dbus_dict_append_int32(DBusMessageIter *iter_dict, + const char *key, + const dbus_int32_t value); + +dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint32_t value); + +dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict, + const char *key, + const dbus_int64_t value); + +dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict, + const char *key, + const dbus_uint64_t value); + +dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict, + const char *key, + const double value); + +dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict, + const char *key, + const char *value); + +dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, + const char *key, + const char *value, + const dbus_uint32_t value_len); + +/* Manual construction and addition of string array elements */ +dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, + const char *key, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); + +dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array, + const char *elem); + +dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); + +/* Convenience function to add a whole string list */ +dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, + const char *key, + const char **items, + const dbus_uint32_t num_items); + +/* + * Reading a dict from a DBusMessage + */ + +struct wpa_dbus_dict_entry { + int type; /** the dbus type of the dict entry's value */ + int array_type; /** the dbus type of the array elements if the dict + entry value contains an array */ + const char *key; /** key of the dict entry */ + + /** Possible values of the property */ + union { + char *str_value; + char byte_value; + dbus_bool_t bool_value; + dbus_int16_t int16_value; + dbus_uint16_t uint16_value; + dbus_int32_t int32_value; + dbus_uint32_t uint32_value; + dbus_int64_t int64_value; + dbus_uint64_t uint64_value; + double double_value; + char *bytearray_value; + char **strarray_value; + }; + dbus_uint32_t array_len; /** length of the array if the dict entry's + value contains an array */ +}; + +dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, + DBusMessageIter *iter_dict); + +dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict, + struct wpa_dbus_dict_entry *entry); + +dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict); + +void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry); + +#endif /* DBUS_DICT_HELPERS_H */ diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c new file mode 100644 index 0000000..49a0895 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new.c @@ -0,0 +1,1605 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com> + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "wps/wps.h" +#include "../config.h" +#include "../wpa_supplicant_i.h" +#include "../bss.h" +#include "dbus_new_helpers.h" +#include "dbus_dict_helpers.h" +#include "dbus_new.h" +#include "dbus_new_handlers.h" +#include "dbus_common.h" +#include "dbus_common_i.h" + + +/** + * wpas_dbus_signal_interface - Send a interface related event signal + * @wpa_s: %wpa_supplicant network interface data + * @sig_name: signal name - InterfaceAdded or InterfaceRemoved + * @properties: Whether to add second argument with object properties + * + * Notify listeners about event related with interface + */ +static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s, + const char *sig_name, int properties) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, iter_dict; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_INTERFACE, sig_name); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &wpa_s->dbus_new_path)) + goto err; + + if (properties) { + if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) + goto err; + + wpa_dbus_get_object_properties(iface, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + &iter_dict); + + if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + goto err; + } + + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_interface_added - Send a interface created signal + * @wpa_s: %wpa_supplicant network interface data + * + * Notify listeners about creating new interface + */ +static void wpas_dbus_signal_interface_added(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_interface(wpa_s, "InterfaceAdded", TRUE); +} + + +/** + * wpas_dbus_signal_interface_removed - Send a interface removed signal + * @wpa_s: %wpa_supplicant network interface data + * + * Notify listeners about removing interface + */ +static void wpas_dbus_signal_interface_removed(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_interface(wpa_s, "InterfaceRemoved", FALSE); + +} + + +/** + * wpas_dbus_signal_scan_done - send scan done signal + * @wpa_s: %wpa_supplicant network interface data + * @success: indicates if scanning succeed or failed + * + * Notify listeners about finishing a scan + */ +void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + dbus_bool_t succ; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "ScanDone"); + if (msg == NULL) + return; + + succ = success ? TRUE : FALSE; + if (dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &succ, + DBUS_TYPE_INVALID)) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_blob - Send a BSS related event signal + * @wpa_s: %wpa_supplicant network interface data + * @bss_obj_path: BSS object path + * @sig_name: signal name - BSSAdded or BSSRemoved + * @properties: Whether to add second argument with object properties + * + * Notify listeners about event related with BSS + */ +static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s, + const char *bss_obj_path, + const char *sig_name, int properties) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, iter_dict; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + sig_name); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &bss_obj_path)) + goto err; + + if (properties) { + if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) + goto err; + + wpa_dbus_get_object_properties(iface, bss_obj_path, + WPAS_DBUS_NEW_IFACE_BSS, + &iter_dict); + + if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + goto err; + } + + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_bss_added - Send a BSS added signal + * @wpa_s: %wpa_supplicant network interface data + * @bss_obj_path: new BSS object path + * + * Notify listeners about adding new BSS + */ +static void wpas_dbus_signal_bss_added(struct wpa_supplicant *wpa_s, + const char *bss_obj_path) +{ + wpas_dbus_signal_bss(wpa_s, bss_obj_path, "BSSAdded", TRUE); +} + + +/** + * wpas_dbus_signal_bss_removed - Send a BSS removed signal + * @wpa_s: %wpa_supplicant network interface data + * @bss_obj_path: BSS object path + * + * Notify listeners about removing BSS + */ +static void wpas_dbus_signal_bss_removed(struct wpa_supplicant *wpa_s, + const char *bss_obj_path) +{ + wpas_dbus_signal_bss(wpa_s, bss_obj_path, "BSSRemoved", FALSE); +} + + +/** + * wpas_dbus_signal_blob - Send a blob related event signal + * @wpa_s: %wpa_supplicant network interface data + * @name: blob name + * @sig_name: signal name - BlobAdded or BlobRemoved + * + * Notify listeners about event related with blob + */ +static void wpas_dbus_signal_blob(struct wpa_supplicant *wpa_s, + const char *name, const char *sig_name) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + sig_name); + if (msg == NULL) + return; + + if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_blob_added - Send a blob added signal + * @wpa_s: %wpa_supplicant network interface data + * @name: blob name + * + * Notify listeners about adding a new blob + */ +void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s, + const char *name) +{ + wpas_dbus_signal_blob(wpa_s, name, "BlobAdded"); +} + + +/** + * wpas_dbus_signal_blob_removed - Send a blob removed signal + * @wpa_s: %wpa_supplicant network interface data + * @name: blob name + * + * Notify listeners about removing blob + */ +void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s, + const char *name) +{ + wpas_dbus_signal_blob(wpa_s, name, "BlobRemoved"); +} + + +/** + * wpas_dbus_signal_network - Send a network related event signal + * @wpa_s: %wpa_supplicant network interface data + * @id: new network id + * @sig_name: signal name - NetworkAdded, NetworkRemoved or NetworkSelected + * @properties: determines if add second argument with object properties + * + * Notify listeners about event related with configured network + */ +static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s, + int id, const char *sig_name, + int properties) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, iter_dict; + char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", + wpa_s->dbus_new_path, id); + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + sig_name); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + path = net_obj_path; + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path)) + goto err; + + if (properties) { + if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) + goto err; + + wpa_dbus_get_object_properties(iface, net_obj_path, + WPAS_DBUS_NEW_IFACE_NETWORK, + &iter_dict); + + if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + goto err; + } + + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_network_added - Send a network added signal + * @wpa_s: %wpa_supplicant network interface data + * @id: new network id + * + * Notify listeners about adding new network + */ +static void wpas_dbus_signal_network_added(struct wpa_supplicant *wpa_s, + int id) +{ + wpas_dbus_signal_network(wpa_s, id, "NetworkAdded", TRUE); +} + + +/** + * wpas_dbus_signal_network_removed - Send a network removed signal + * @wpa_s: %wpa_supplicant network interface data + * @id: network id + * + * Notify listeners about removing a network + */ +static void wpas_dbus_signal_network_removed(struct wpa_supplicant *wpa_s, + int id) +{ + wpas_dbus_signal_network(wpa_s, id, "NetworkRemoved", FALSE); +} + + +/** + * wpas_dbus_signal_network_selected - Send a network selected signal + * @wpa_s: %wpa_supplicant network interface data + * @id: network id + * + * Notify listeners about selecting a network + */ +void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id) +{ + wpas_dbus_signal_network(wpa_s, id, "NetworkSelected", FALSE); +} + + +/** + * wpas_dbus_signal_network_enabled_changed - Signals Enabled property changes + * @wpa_s: %wpa_supplicant network interface data + * @ssid: configured network which Enabled property has changed + * + * Sends PropertyChanged signals containing new value of Enabled property + * for specified network + */ +void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + + char path[WPAS_DBUS_OBJECT_PATH_MAX]; + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d", + wpa_s->dbus_new_path, ssid->id); + + wpa_dbus_mark_property_changed(wpa_s->global->dbus, path, + WPAS_DBUS_NEW_IFACE_NETWORK, "Enabled"); +} + + +#ifdef CONFIG_WPS + +/** + * wpas_dbus_signal_wps_event_success - Signals Success WPS event + * @wpa_s: %wpa_supplicant network interface data + * + * Sends Event dbus signal with name "success" and empty dict as arguments + */ +void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s) +{ + + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char *key = "success"; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_WPS, "Event"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) || + !wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_wps_event_fail - Signals Fail WPS event + * @wpa_s: %wpa_supplicant network interface data + * + * Sends Event dbus signal with name "fail" and dictionary containing + * "msg field with fail message number (int32) as arguments + */ +void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ + + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char *key = "fail"; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_WPS, "Event"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) || + !wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_wps_event_m2d - Signals M2D WPS event + * @wpa_s: %wpa_supplicant network interface data + * + * Sends Event dbus signal with name "m2d" and dictionary containing + * fields of wps_event_m2d structure. + */ +void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, + struct wps_event_m2d *m2d) +{ + + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char *key = "m2d"; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_WPS, "Event"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) || + !wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_uint16(&dict_iter, "config_methods", + m2d->config_methods) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "manufacturer", + (const char *) m2d->manufacturer, + m2d->manufacturer_len) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "model_name", + (const char *) m2d->model_name, + m2d->model_name_len) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "model_number", + (const char *) m2d->model_number, + m2d->model_number_len) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "serial_number", + (const char *) + m2d->serial_number, + m2d->serial_number_len) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "dev_name", + (const char *) m2d->dev_name, + m2d->dev_name_len) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "primary_dev_type", + (const char *) + m2d->primary_dev_type, 8) || + !wpa_dbus_dict_append_uint16(&dict_iter, "config_error", + m2d->config_error) || + !wpa_dbus_dict_append_uint16(&dict_iter, "dev_password_id", + m2d->dev_password_id) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_wps_cred - Signals new credentials + * @wpa_s: %wpa_supplicant network interface data + * + * Sends signal with credentials in directory argument + */ +void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char *auth_type[6]; /* we have six possible authorization types */ + int at_num = 0; + char *encr_type[4]; /* we have four possible encryption types */ + int et_num = 0; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_WPS, + "Credentials"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto nomem; + + if (cred->auth_type & WPS_AUTH_OPEN) + 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"; + + 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) + encr_type[et_num++] = "aes"; + + if (wpa_s->current_ssid) { + if (!wpa_dbus_dict_append_byte_array( + &dict_iter, "BSSID", + (const char *) wpa_s->current_ssid->bssid, + ETH_ALEN)) + goto nomem; + } + + if (!wpa_dbus_dict_append_byte_array(&dict_iter, "SSID", + (const char *) cred->ssid, + cred->ssid_len) || + !wpa_dbus_dict_append_string_array(&dict_iter, "AuthType", + (const char **) auth_type, + at_num) || + !wpa_dbus_dict_append_string_array(&dict_iter, "EncrType", + (const char **) encr_type, + et_num) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "Key", + (const char *) cred->key, + cred->key_len) || + !wpa_dbus_dict_append_uint32(&dict_iter, "KeyIndex", + cred->key_idx) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto nomem; + + dbus_connection_send(iface->con, msg, NULL); + +nomem: + dbus_message_unref(msg); +} + +#endif /* CONFIG_WPS */ + + +/** + * wpas_dbus_signal_prop_changed - Signals change of property + * @wpa_s: %wpa_supplicant network interface data + * @property: indicates which property has changed + * + * Sends ProertyChanged signals with path, interface and arguments + * depending on which property has changed. + */ +void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, + enum wpas_dbus_prop property) +{ + WPADBusPropertyAccessor getter; + char *prop; + + if (wpa_s->dbus_new_path == NULL) + return; /* Skip signal since D-Bus setup is not yet ready */ + + switch (property) { + case WPAS_DBUS_PROP_AP_SCAN: + getter = (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan; + prop = "ApScan"; + break; + case WPAS_DBUS_PROP_SCANNING: + getter = (WPADBusPropertyAccessor) wpas_dbus_getter_scanning; + prop = "Scanning"; + break; + case WPAS_DBUS_PROP_STATE: + getter = (WPADBusPropertyAccessor) wpas_dbus_getter_state; + prop = "State"; + break; + case WPAS_DBUS_PROP_CURRENT_BSS: + getter = (WPADBusPropertyAccessor) + wpas_dbus_getter_current_bss; + prop = "CurrentBSS"; + break; + case WPAS_DBUS_PROP_CURRENT_NETWORK: + getter = (WPADBusPropertyAccessor) + wpas_dbus_getter_current_network; + prop = "CurrentNetwork"; + break; + case WPAS_DBUS_PROP_BSSS: + getter = (WPADBusPropertyAccessor) wpas_dbus_getter_bsss; + prop = "BSSs"; + break; + case WPAS_DBUS_PROP_CURRENT_AUTH_MODE: + getter = (WPADBusPropertyAccessor) + wpas_dbus_getter_current_auth_mode; + prop = "CurrentAuthMode"; + break; + default: + wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", + __func__, property); + return; + } + + wpa_dbus_mark_property_changed(wpa_s->global->dbus, + wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, prop); +} + + +/** + * wpas_dbus_bss_signal_prop_changed - Signals change of BSS property + * @wpa_s: %wpa_supplicant network interface data + * @property: indicates which property has changed + * @id: unique BSS identifier + * + * Sends PropertyChanged signals with path, interface, and arguments depending + * on which property has changed. + */ +void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, + enum wpas_dbus_bss_prop property, + unsigned int id) +{ + char path[WPAS_DBUS_OBJECT_PATH_MAX]; + char *prop; + + switch (property) { + case WPAS_DBUS_BSS_PROP_SIGNAL: + prop = "Signal"; + break; + case WPAS_DBUS_BSS_PROP_FREQ: + prop = "Frequency"; + break; + case WPAS_DBUS_BSS_PROP_MODE: + prop = "Mode"; + break; + case WPAS_DBUS_BSS_PROP_PRIVACY: + prop = "Privacy"; + break; + case WPAS_DBUS_BSS_PROP_RATES: + prop = "Rates"; + break; + case WPAS_DBUS_BSS_PROP_WPA: + prop = "WPA"; + break; + case WPAS_DBUS_BSS_PROP_RSN: + prop = "RSN"; + break; + case WPAS_DBUS_BSS_PROP_IES: + prop = "IEs"; + break; + default: + wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", + __func__, property); + return; + } + + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, id); + + wpa_dbus_mark_property_changed(wpa_s->global->dbus, path, + WPAS_DBUS_NEW_IFACE_BSS, prop); +} + + +/** + * wpas_dbus_signal_debug_level_changed - Signals change of debug param + * @global: wpa_global structure + * + * Sends ProertyChanged signals informing that debug level has changed. + */ +void wpas_dbus_signal_debug_level_changed(struct wpa_global *global) +{ + wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_INTERFACE, + "DebugLevel"); +} + + +/** + * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param + * @global: wpa_global structure + * + * Sends ProertyChanged signals informing that debug timestamp has changed. + */ +void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global) +{ + wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_INTERFACE, + "DebugTimestamp"); +} + + +/** + * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param + * @global: wpa_global structure + * + * Sends ProertyChanged signals informing that debug show_keys has changed. + */ +void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global) +{ + wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_INTERFACE, + "DebugShowKeys"); +} + + +static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc, + void *priv, + WPADBusArgumentFreeFunction priv_free, + const struct wpa_dbus_method_desc *methods, + const struct wpa_dbus_property_desc *properties, + const struct wpa_dbus_signal_desc *signals) +{ + int n; + + obj_desc->user_data = priv; + obj_desc->user_data_free_func = priv_free; + obj_desc->methods = methods; + obj_desc->properties = properties; + obj_desc->signals = signals; + + for (n = 0; properties && properties->dbus_property; properties++) + n++; + + obj_desc->prop_changed_flags = os_zalloc(n); + if (!obj_desc->prop_changed_flags) + wpa_printf(MSG_DEBUG, "dbus: %s: can't register handlers", + __func__); +} + + +static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { + { "CreateInterface", WPAS_DBUS_NEW_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_create_interface, + { + { "args", "a{sv}", ARG_IN }, + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface, + { + { "path", "o", ARG_IN }, + END_ARGS + } + }, + { "GetInterface", WPAS_DBUS_NEW_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_get_interface, + { + { "ifname", "s", ARG_IN }, + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, NULL, { END_ARGS } } +}; + +static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = { + { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s", + (WPADBusPropertyAccessor) wpas_dbus_getter_debug_level, + (WPADBusPropertyAccessor) wpas_dbus_setter_debug_level, + RW + }, + { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b", + (WPADBusPropertyAccessor) wpas_dbus_getter_debug_timestamp, + (WPADBusPropertyAccessor) wpas_dbus_setter_debug_timestamp, + RW + }, + { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b", + (WPADBusPropertyAccessor) wpas_dbus_getter_debug_show_keys, + (WPADBusPropertyAccessor) wpas_dbus_setter_debug_show_keys, + RW + }, + { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao", + (WPADBusPropertyAccessor) &wpas_dbus_getter_interfaces, + NULL, + R + }, + { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as", + (WPADBusPropertyAccessor) wpas_dbus_getter_eap_methods, + NULL, + R + }, + { NULL, NULL, NULL, NULL, NULL, 0 } +}; + +static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { + { "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, { END_ARGS } } +}; + + +/** + * wpas_dbus_ctrl_iface_init - Initialize dbus control interface + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: 0 on success or -1 on failure + * + * Initialize the dbus control interface for wpa_supplicantand and start + * receiving commands from external programs over the bus. + */ +int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) +{ + struct wpa_dbus_object_desc *obj_desc; + int ret; + + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create object description"); + return -1; + } + + wpas_dbus_register(obj_desc, priv->global, NULL, + wpas_dbus_global_methods, + wpas_dbus_global_properties, + wpas_dbus_global_signals); + + wpa_printf(MSG_DEBUG, "dbus: Register D-Bus object '%s'", + WPAS_DBUS_NEW_PATH); + ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_SERVICE, + obj_desc); + if (ret < 0) + free_dbus_object_desc(obj_desc); + else + priv->dbus_new_initialized = 1; + + return ret; +} + + +/** + * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for + * wpa_supplicant + * @iface: Pointer to dbus private data from wpas_dbus_init() + * + * Deinitialize the dbus control interface that was initialized with + * wpas_dbus_ctrl_iface_init(). + */ +void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface) +{ + if (!iface->dbus_new_initialized) + return; + wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'", + WPAS_DBUS_NEW_PATH); + dbus_connection_unregister_object_path(iface->con, + WPAS_DBUS_NEW_PATH); +} + + +static void wpa_dbus_free(void *ptr) +{ + os_free(ptr); +} + + +static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = { + { "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}", + (WPADBusPropertyAccessor) wpas_dbus_getter_network_properties, + (WPADBusPropertyAccessor) wpas_dbus_setter_network_properties, + RW + }, + { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b", + (WPADBusPropertyAccessor) wpas_dbus_getter_enabled, + (WPADBusPropertyAccessor) wpas_dbus_setter_enabled, + RW + }, + { NULL, NULL, NULL, NULL, NULL, 0 } +}; + + +static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = { + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, { END_ARGS } } +}; + + +/** + * wpas_dbus_register_network - Register a configured network with dbus + * @wpa_s: wpa_supplicant interface structure + * @ssid: network configuration data + * Returns: 0 on success, -1 on failure + * + * Registers network representing object with dbus + */ +int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + struct network_handler_args *arg; + char net_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 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", + wpa_s->dbus_new_path, ssid->id); + + wpa_printf(MSG_DEBUG, "dbus: Register network object '%s'", + net_obj_path); + 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 network_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create arguments for method"); + goto err; + } + + arg->wpa_s = wpa_s; + arg->ssid = ssid; + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, + wpas_dbus_network_properties, + wpas_dbus_network_signals); + + if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path, + wpa_s->ifname, obj_desc)) + goto err; + + wpas_dbus_signal_network_added(wpa_s, ssid->id); + + return 0; + +err: + free_dbus_object_desc(obj_desc); + return -1; +} + + +/** + * wpas_dbus_unregister_network - Unregister a configured network from dbus + * @wpa_s: wpa_supplicant interface structure + * @nid: network id + * Returns: 0 on success, -1 on failure + * + * Unregisters network representing object from dbus + */ +int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid) +{ + struct wpas_dbus_priv *ctrl_iface; + char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + int ret; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL || + wpa_s->dbus_new_path == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", + wpa_s->dbus_new_path, nid); + + wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'", + net_obj_path); + ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path); + + if (!ret) + wpas_dbus_signal_network_removed(wpa_s, nid); + + return ret; +} + + +static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = { + { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ssid, + NULL, + R + }, + { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_bssid, + NULL, + R + }, + { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_privacy, + NULL, + R + }, + { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_mode, + NULL, + R + }, + { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_signal, + NULL, + R + }, + { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_frequency, + NULL, + R + }, + { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rates, + NULL, + R + }, + { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_wpa, + NULL, + R + }, + { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rsn, + NULL, + R + }, + { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ies, + NULL, + R + }, + { NULL, NULL, NULL, NULL, NULL, 0 } +}; + + +static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = { + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, { END_ARGS } } +}; + + +/** + * wpas_dbus_unregister_bss - Unregister a scanned BSS from dbus + * @wpa_s: wpa_supplicant interface structure + * @bssid: scanned network bssid + * @id: unique BSS identifier + * Returns: 0 on success, -1 on failure + * + * Unregisters BSS representing object from dbus + */ +int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, + u8 bssid[ETH_ALEN], unsigned int id) +{ + struct wpas_dbus_priv *ctrl_iface; + char bss_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 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, id); + + wpa_printf(MSG_DEBUG, "dbus: Unregister BSS object '%s'", + bss_obj_path); + if (wpa_dbus_unregister_object_per_iface(ctrl_iface, bss_obj_path)) { + wpa_printf(MSG_ERROR, "dbus: Cannot unregister BSS object %s", + bss_obj_path); + return -1; + } + + wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path); + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS); + + return 0; +} + + +/** + * wpas_dbus_register_bss - Register a scanned BSS with dbus + * @wpa_s: wpa_supplicant interface structure + * @bssid: scanned network bssid + * @id: unique BSS identifier + * Returns: 0 on success, -1 on failure + * + * Registers BSS representing object with dbus + */ +int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, + u8 bssid[ETH_ALEN], unsigned int id) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + struct bss_handler_args *arg; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, id); + + 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; + } + + arg = os_zalloc(sizeof(struct bss_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create arguments for handler"); + goto err; + } + arg->wpa_s = wpa_s; + arg->id = id; + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, + wpas_dbus_bss_properties, + wpas_dbus_bss_signals); + + wpa_printf(MSG_DEBUG, "dbus: Register BSS object '%s'", + bss_obj_path); + if (wpa_dbus_register_object_per_iface(ctrl_iface, bss_obj_path, + wpa_s->ifname, obj_desc)) { + wpa_printf(MSG_ERROR, + "Cannot register BSSID dbus object %s.", + bss_obj_path); + goto err; + } + + wpas_dbus_signal_bss_added(wpa_s, bss_obj_path); + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS); + + return 0; + +err: + free_dbus_object_desc(obj_desc); + return -1; +} + + +static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { + { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_scan, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_disconnect, + { + END_ARGS + } + }, + { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_add_network, + { + { "args", "a{sv}", ARG_IN }, + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_remove_network, + { + { "path", "o", ARG_IN }, + END_ARGS + } + }, + { "RemoveAllNetworks", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_remove_all_networks, + { + END_ARGS + } + }, + { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_select_network, + { + { "path", "o", ARG_IN }, + END_ARGS + } + }, + { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_add_blob, + { + { "name", "s", ARG_IN }, + { "data", "ay", ARG_IN }, + END_ARGS + } + }, + { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_get_blob, + { + { "name", "s", ARG_IN }, + { "data", "ay", ARG_OUT }, + END_ARGS + } + }, + { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob, + { + { "name", "s", ARG_IN }, + END_ARGS + } + }, +#ifdef CONFIG_WPS + { "Start", WPAS_DBUS_NEW_IFACE_WPS, + (WPADBusMethodHandler) &wpas_dbus_handler_wps_start, + { + { "args", "a{sv}", ARG_IN }, + { "output", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_WPS */ + { "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_flush_bss, + { + { "age", "u", ARG_IN }, + END_ARGS + } + }, + { NULL, NULL, NULL, { END_ARGS } } +}; + +static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { + { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}", + (WPADBusPropertyAccessor) wpas_dbus_getter_capabilities, + NULL, R + }, + { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + (WPADBusPropertyAccessor) wpas_dbus_getter_state, + NULL, R + }, + { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", + (WPADBusPropertyAccessor) wpas_dbus_getter_scanning, + NULL, R + }, + { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan, + (WPADBusPropertyAccessor) wpas_dbus_setter_ap_scan, + RW + }, + { "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_expire_age, + (WPADBusPropertyAccessor) wpas_dbus_setter_bss_expire_age, + RW + }, + { "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + (WPADBusPropertyAccessor) wpas_dbus_getter_bss_expire_count, + (WPADBusPropertyAccessor) wpas_dbus_setter_bss_expire_count, + RW + }, + { "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + (WPADBusPropertyAccessor) wpas_dbus_getter_country, + (WPADBusPropertyAccessor) wpas_dbus_setter_country, + RW + }, + { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + (WPADBusPropertyAccessor) wpas_dbus_getter_ifname, + NULL, R + }, + { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + (WPADBusPropertyAccessor) wpas_dbus_getter_driver, + NULL, R + }, + { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + (WPADBusPropertyAccessor) wpas_dbus_getter_bridge_ifname, + NULL, R + }, + { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", + (WPADBusPropertyAccessor) wpas_dbus_getter_current_bss, + NULL, R + }, + { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", + (WPADBusPropertyAccessor) wpas_dbus_getter_current_network, + NULL, R + }, + { "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + (WPADBusPropertyAccessor) wpas_dbus_getter_current_auth_mode, + NULL, R + }, + { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}", + (WPADBusPropertyAccessor) wpas_dbus_getter_blobs, + NULL, R + }, + { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", + (WPADBusPropertyAccessor) wpas_dbus_getter_bsss, + NULL, R + }, + { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", + (WPADBusPropertyAccessor) wpas_dbus_getter_networks, + NULL, R + }, +#ifdef CONFIG_WPS + { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b", + (WPADBusPropertyAccessor) wpas_dbus_getter_process_credentials, + (WPADBusPropertyAccessor) wpas_dbus_setter_process_credentials, + RW + }, +#endif /* CONFIG_WPS */ + { NULL, NULL, NULL, NULL, NULL, 0 } +}; + +static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { + { "ScanDone", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "success", "b", ARG_OUT }, + END_ARGS + } + }, + { "BSSAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, + { "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, + { "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#ifdef CONFIG_WPS + { "Event", WPAS_DBUS_NEW_IFACE_WPS, + { + { "name", "s", ARG_OUT }, + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "Credentials", WPAS_DBUS_NEW_IFACE_WPS, + { + { "credentials", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_WPS */ + { NULL, NULL, { END_ARGS } } +}; + + +int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) +{ + + struct wpa_dbus_object_desc *obj_desc = NULL; + struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus; + int next; + + /* Do nothing if the control interface is not turned on */ + if (ctrl_iface == NULL) + return 0; + + /* Create and set the interface's object path */ + wpa_s->dbus_new_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (wpa_s->dbus_new_path == NULL) + return -1; + next = ctrl_iface->next_objid++; + os_snprintf(wpa_s->dbus_new_path, WPAS_DBUS_OBJECT_PATH_MAX, + WPAS_DBUS_NEW_PATH_INTERFACES "/%u", + next); + + 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; + } + + wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods, + wpas_dbus_interface_properties, + wpas_dbus_interface_signals); + + wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'", + wpa_s->dbus_new_path); + if (wpa_dbus_register_object_per_iface(ctrl_iface, + wpa_s->dbus_new_path, + wpa_s->ifname, obj_desc)) + goto err; + + wpas_dbus_signal_interface_added(wpa_s); + + return 0; + +err: + os_free(wpa_s->dbus_new_path); + wpa_s->dbus_new_path = NULL; + free_dbus_object_desc(obj_desc); + return -1; +} + + +int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *ctrl_iface; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'", + wpa_s->dbus_new_path); + if (wpa_dbus_unregister_object_per_iface(ctrl_iface, + wpa_s->dbus_new_path)) + return -1; + + wpas_dbus_signal_interface_removed(wpa_s); + + os_free(wpa_s->dbus_new_path); + wpa_s->dbus_new_path = NULL; + + return 0; +} diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h new file mode 100644 index 0000000..377e381 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new.h @@ -0,0 +1,236 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.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 CTRL_IFACE_DBUS_NEW_H +#define CTRL_IFACE_DBUS_NEW_H + +struct wpa_global; +struct wpa_supplicant; +struct wpa_ssid; +struct wps_event_m2d; +struct wps_event_fail; +struct wps_credential; +enum wpa_states; + +enum wpas_dbus_prop { + WPAS_DBUS_PROP_AP_SCAN, + WPAS_DBUS_PROP_SCANNING, + WPAS_DBUS_PROP_STATE, + WPAS_DBUS_PROP_CURRENT_BSS, + WPAS_DBUS_PROP_CURRENT_NETWORK, + WPAS_DBUS_PROP_CURRENT_AUTH_MODE, + WPAS_DBUS_PROP_BSSS, +}; + +enum wpas_dbus_bss_prop { + WPAS_DBUS_BSS_PROP_SIGNAL, + WPAS_DBUS_BSS_PROP_FREQ, + WPAS_DBUS_BSS_PROP_MODE, + WPAS_DBUS_BSS_PROP_PRIVACY, + WPAS_DBUS_BSS_PROP_RATES, + WPAS_DBUS_BSS_PROP_WPA, + WPAS_DBUS_BSS_PROP_RSN, + WPAS_DBUS_BSS_PROP_IES, +}; + +#define WPAS_DBUS_OBJECT_PATH_MAX 150 + +#define WPAS_DBUS_NEW_SERVICE "fi.w1.wpa_supplicant1" +#define WPAS_DBUS_NEW_PATH "/fi/w1/wpa_supplicant1" +#define WPAS_DBUS_NEW_INTERFACE "fi.w1.wpa_supplicant1" + +#define WPAS_DBUS_NEW_PATH_INTERFACES WPAS_DBUS_NEW_PATH "/Interfaces" +#define WPAS_DBUS_NEW_IFACE_INTERFACE WPAS_DBUS_NEW_INTERFACE ".Interface" +#define WPAS_DBUS_NEW_IFACE_WPS WPAS_DBUS_NEW_IFACE_INTERFACE ".WPS" + +#define WPAS_DBUS_NEW_NETWORKS_PART "Networks" +#define WPAS_DBUS_NEW_IFACE_NETWORK WPAS_DBUS_NEW_INTERFACE ".Network" + +#define WPAS_DBUS_NEW_BSSIDS_PART "BSSs" +#define WPAS_DBUS_NEW_IFACE_BSS WPAS_DBUS_NEW_INTERFACE ".BSS" + + +/* Errors */ +#define WPAS_DBUS_ERROR_UNKNOWN_ERROR \ + WPAS_DBUS_NEW_INTERFACE ".UnknownError" +#define WPAS_DBUS_ERROR_INVALID_ARGS \ + WPAS_DBUS_NEW_INTERFACE ".InvalidArgs" + +#define WPAS_DBUS_ERROR_IFACE_EXISTS \ + WPAS_DBUS_NEW_INTERFACE ".InterfaceExists" +#define WPAS_DBUS_ERROR_IFACE_UNKNOWN \ + WPAS_DBUS_NEW_INTERFACE ".InterfaceUnknown" + +#define WPAS_DBUS_ERROR_NOT_CONNECTED \ + WPAS_DBUS_NEW_INTERFACE ".NotConnected" +#define WPAS_DBUS_ERROR_NETWORK_UNKNOWN \ + WPAS_DBUS_NEW_INTERFACE ".NetworkUnknown" + +#define WPAS_DBUS_ERROR_BLOB_EXISTS \ + WPAS_DBUS_NEW_INTERFACE ".BlobExists" +#define WPAS_DBUS_ERROR_BLOB_UNKNOWN \ + WPAS_DBUS_NEW_INTERFACE ".BlobUnknown" + + +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW + +int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv); +void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface); + +int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s); +int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s); +void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, + enum wpas_dbus_prop property); +void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, + enum wpas_dbus_bss_prop property, + unsigned int id); +void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id); +void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success); +void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred); +void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, + struct wps_event_m2d *m2d); +void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail); +void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s); +int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid); +int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, + u8 bssid[ETH_ALEN], unsigned int id); +int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, + u8 bssid[ETH_ALEN], unsigned int id); +void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s, + const char *name); +void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s, + const char *name); +void wpas_dbus_signal_debug_level_changed(struct wpa_global *global); +void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global); +void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global); + +#else /* CONFIG_CTRL_IFACE_DBUS_NEW */ + +static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +#define wpas_dbus_signal_state_changed(w, n, o) do { } while (0) + +static inline void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, + enum wpas_dbus_prop property) +{ +} + +static inline void wpas_dbus_bss_signal_prop_changed( + struct wpa_supplicant *wpa_s, enum wpas_dbus_bss_prop property, + unsigned int id) +{ +} + +static inline void wpas_dbus_signal_network_enabled_changed( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ +} + +static inline void wpas_dbus_signal_network_selected( + struct wpa_supplicant *wpa_s, int id) +{ +} + +static inline void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, + int success) +{ +} + +static inline void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ +} + +static inline void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, + struct wps_event_m2d *m2d) +{ +} + +static inline void wpas_dbus_signal_wps_event_fail( + struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) +{ +} + +static inline void wpas_dbus_signal_wps_event_success( + struct wpa_supplicant *wpa_s) +{ +} + +static inline int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + return 0; +} + +static inline int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, + int nid) +{ + return 0; +} + +static inline int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, + u8 bssid[ETH_ALEN], unsigned int id) +{ + return 0; +} + +static inline int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, + u8 bssid[ETH_ALEN], unsigned int id) +{ + return 0; +} + +static inline void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s, + const char *name) +{ +} + +static inline void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s, + const char *name) +{ +} + +static inline void wpas_dbus_signal_debug_level_changed( + struct wpa_global *global) +{ +} + +static inline void wpas_dbus_signal_debug_timestamp_changed( + struct wpa_global *global) +{ +} + +static inline void wpas_dbus_signal_debug_show_keys_changed( + struct wpa_global *global) +{ +} + +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ + +#endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c new file mode 100644 index 0000000..68e5465 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -0,0 +1,3228 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com> + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "eap_peer/eap_methods.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "rsn_supp/wpa.h" +#include "../config.h" +#include "../wpa_supplicant_i.h" +#include "../driver_i.h" +#include "../notify.h" +#include "../wpas_glue.h" +#include "../bss.h" +#include "../scan.h" +#include "dbus_new_helpers.h" +#include "dbus_new.h" +#include "dbus_new_handlers.h" +#include "dbus_dict_helpers.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 +}; + + +/** + * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts + * @path: The dbus object path + * @network: (out) the configured network this object path refers to, if any + * @bssid: (out) the scanned bssid this object path refers to, if any + * Returns: The object path of the network interface this path refers to + * + * For a given object path, decomposes the object path into object id, network, + * and BSSID parts, if those parts exist. + */ +static char * wpas_dbus_new_decompose_object_path(const char *path, + char **network, + char **bssid) +{ + const unsigned int dev_path_prefix_len = + strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/"); + char *obj_path_only; + char *next_sep; + + /* Be a bit paranoid about path */ + if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", + dev_path_prefix_len)) + return NULL; + + /* Ensure there's something at the end of the path */ + if ((path + dev_path_prefix_len)[0] == '\0') + return NULL; + + obj_path_only = os_strdup(path); + if (obj_path_only == NULL) + return NULL; + + next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/'); + if (next_sep != NULL) { + const char *net_part = os_strstr( + next_sep, WPAS_DBUS_NEW_NETWORKS_PART "/"); + const char *bssid_part = os_strstr( + next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/"); + + if (network && net_part) { + /* Deal with a request for a configured network */ + const char *net_name = net_part + + os_strlen(WPAS_DBUS_NEW_NETWORKS_PART "/"); + *network = NULL; + if (os_strlen(net_name)) + *network = os_strdup(net_name); + } else if (bssid && bssid_part) { + /* Deal with a request for a scanned BSSID */ + const char *bssid_name = bssid_part + + os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/"); + if (strlen(bssid_name)) + *bssid = os_strdup(bssid_name); + else + *bssid = NULL; + } + + /* Cut off interface object path before "/" */ + *next_sep = '\0'; + } + + return obj_path_only; +} + + +/** + * wpas_dbus_error_unknown_error - Return a new InvalidArgs error message + * @message: Pointer to incoming dbus message this error refers to + * @arg: Optional string appended to error message + * Returns: a dbus error message + * + * Convenience function to create and return an UnknownError + */ +DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, + const char *arg) +{ + /* + * This function can be called as a result of a failure + * within internal getter calls, which will call this function + * with a NULL message parameter. However, dbus_message_new_error + * looks very unkindly (i.e, abort()) on a NULL message, so + * in this case, we should not call it. + */ + if (message == NULL) { + wpa_printf(MSG_INFO, "dbus: wpas_dbus_error_unknown_error " + "called with NULL message (arg=%s)", + arg ? arg : "N/A"); + return NULL; + } + + return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR, + arg); +} + + +/** + * wpas_dbus_error_iface_unknown - Return a new invalid interface error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: A dbus error message + * + * Convenience function to create and return an invalid interface error + */ +static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_UNKNOWN, + "wpa_supplicant knows nothing about " + "this interface."); +} + + +/** + * wpas_dbus_error_network_unknown - Return a new NetworkUnknown error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid network error + */ +static DBusMessage * wpas_dbus_error_network_unknown(DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, + "There is no such a network in this " + "interface."); +} + + +/** + * wpas_dbus_error_invalid_args - Return a new InvalidArgs error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid options error + */ +DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, + const char *arg) +{ + DBusMessage *reply; + + reply = dbus_message_new_error(message, WPAS_DBUS_ERROR_INVALID_ARGS, + "Did not receive correct message " + "arguments."); + if (arg != NULL) + dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + + return reply; +} + + +static const char *dont_quote[] = { + "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", + "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", + "bssid", NULL +}; + +static dbus_bool_t should_quote_opt(const char *key) +{ + int i = 0; + while (dont_quote[i] != NULL) { + if (os_strcmp(key, dont_quote[i]) == 0) + return FALSE; + i++; + } + return TRUE; +} + +/** + * get_iface_by_dbus_path - Get a new network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @path: Pointer to a dbus object path representing an interface + * Returns: Pointer to the interface or %NULL if not found + */ +static struct wpa_supplicant * get_iface_by_dbus_path( + struct wpa_global *global, const char *path) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->dbus_new_path, path) == 0) + return wpa_s; + } + return NULL; +} + + +/** + * set_network_properties - Set properties of a configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network + * @iter: DBus message iterator containing dictionary of network + * properties to set. + * Returns: NULL when succeed or DBus error on failure + * + * Sets network configuration with parameters given id DBus dictionary + */ +static DBusMessage * set_network_properties(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + DBusMessageIter *iter) +{ + + struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; + DBusMessage *reply = NULL; + DBusMessageIter iter_dict; + + if (!wpa_dbus_dict_open_read(iter, &iter_dict)) + return wpas_dbus_error_invalid_args(message, NULL); + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + char *value = NULL; + size_t size = 50; + int ret; + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { + reply = wpas_dbus_error_invalid_args(message, NULL); + break; + } + if (entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_BYTE) { + if (entry.array_len <= 0) + goto error; + + size = entry.array_len * 2 + 1; + value = os_zalloc(size); + if (value == NULL) + goto error; + + ret = wpa_snprintf_hex(value, size, + (u8 *) entry.bytearray_value, + entry.array_len); + if (ret <= 0) + goto error; + } else if (entry.type == DBUS_TYPE_STRING) { + if (should_quote_opt(entry.key)) { + size = os_strlen(entry.str_value); + if (size <= 0) + goto error; + + size += 3; + value = os_zalloc(size); + if (value == NULL) + goto error; + + ret = os_snprintf(value, size, "\"%s\"", + entry.str_value); + if (ret < 0 || (size_t) ret != (size - 1)) + goto error; + } else { + value = os_strdup(entry.str_value); + if (value == NULL) + goto error; + } + } else if (entry.type == DBUS_TYPE_UINT32) { + value = os_zalloc(size); + if (value == NULL) + goto error; + + ret = os_snprintf(value, size, "%u", + entry.uint32_value); + if (ret <= 0) + goto error; + } else if (entry.type == DBUS_TYPE_INT32) { + value = os_zalloc(size); + if (value == NULL) + goto error; + + ret = os_snprintf(value, size, "%d", + entry.int32_value); + if (ret <= 0) + goto error; + } else + goto error; + + if (wpa_config_set(ssid, entry.key, value, 0) < 0) + goto error; + + if ((os_strcmp(entry.key, "psk") == 0 && + value[0] == '"' && ssid->ssid_len) || + (strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) + wpa_config_update_psk(ssid); + else if (os_strcmp(entry.key, "priority") == 0) + wpa_config_update_prio_list(wpa_s->conf); + + os_free(value); + wpa_dbus_dict_entry_clear(&entry); + continue; + + error: + os_free(value); + reply = wpas_dbus_error_invalid_args(message, entry.key); + wpa_dbus_dict_entry_clear(&entry); + break; + } + + return reply; +} + + +/** + * wpas_dbus_simple_property_getter - Get basic type property + * @message: Pointer to incoming dbus message + * @type: DBus type of property (must be basic type) + * @val: pointer to place holding property value + * Returns: The DBus message containing response for Properties.Get call + * or DBus error message if error occurred. + * + * Generic getter for basic type properties. Type is required to be basic. + */ +DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message, + const int type, const void *val) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, variant_iter; + + if (!dbus_type_is_basic(type)) { + wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:" + " given type is not basic"); + return wpas_dbus_error_unknown_error(message, NULL); + } + + if (message == NULL) + reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); + else + reply = dbus_message_new_method_return(message); + + if (reply != NULL) { + dbus_message_iter_init_append(reply, &iter); + if (!dbus_message_iter_open_container( + &iter, DBUS_TYPE_VARIANT, + wpa_dbus_type_as_string(type), &variant_iter) || + !dbus_message_iter_append_basic(&variant_iter, type, + val) || + !dbus_message_iter_close_container(&iter, &variant_iter)) { + wpa_printf(MSG_ERROR, "dbus: " + "wpas_dbus_simple_property_getter: out of " + "memory to put property value into " + "message"); + dbus_message_unref(reply); + reply = dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, + NULL); + } + } else { + wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:" + " out of memory to return property value"); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + return reply; +} + + +/** + * wpas_dbus_simple_property_setter - Set basic type property + * @message: Pointer to incoming dbus message + * @type: DBus type of property (must be basic type) + * @val: pointer to place where value being set will be stored + * Returns: NULL or DBus error message if error occurred. + * + * Generic setter for basic type properties. Type is required to be basic. + */ +DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message, + const int type, void *val) +{ + DBusMessageIter iter, variant_iter; + + if (!dbus_type_is_basic(type)) { + wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:" + " given type is not basic"); + return wpas_dbus_error_unknown_error(message, NULL); + } + + if (!dbus_message_iter_init(message, &iter)) { + wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:" + " out of memory to return scanning state"); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + /* omit first and second argument and get value from third */ + dbus_message_iter_next(&iter); + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &variant_iter); + + if (dbus_message_iter_get_arg_type(&variant_iter) != type) { + wpa_printf(MSG_DEBUG, "dbus: wpas_dbus_simple_property_setter:" + " wrong property type"); + return wpas_dbus_error_invalid_args(message, + "wrong property type"); + } + dbus_message_iter_get_basic(&variant_iter, val); + + return NULL; +} + + +/** + * wpas_dbus_simple_array_property_getter - Get array type property + * @message: Pointer to incoming dbus message + * @type: DBus type of property array elements (must be basic type) + * @array: pointer to array of elements to put into response message + * @array_len: length of above array + * Returns: The DBus message containing response for Properties.Get call + * or DBus error message if error occurred. + * + * Generic getter for array type properties. Array elements type is + * required to be basic. + */ +DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message, + const int type, + const void *array, + size_t array_len) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, variant_iter, array_iter; + char type_str[] = "a?"; /* ? will be replaced with subtype letter; */ + const char *sub_type_str; + size_t element_size, i; + + if (!dbus_type_is_basic(type)) { + wpa_printf(MSG_ERROR, "dbus: " + "wpas_dbus_simple_array_property_getter: given " + "type is not basic"); + return wpas_dbus_error_unknown_error(message, NULL); + } + + sub_type_str = wpa_dbus_type_as_string(type); + type_str[1] = sub_type_str[0]; + + if (message == NULL) + reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); + else + reply = dbus_message_new_method_return(message); + if (reply == NULL) { + wpa_printf(MSG_ERROR, "dbus: " + "wpas_dbus_simple_array_property_getter: out of " + "memory to create return message"); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + type_str, &variant_iter) || + !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + sub_type_str, &array_iter)) { + wpa_printf(MSG_ERROR, "dbus: " + "wpas_dbus_simple_array_property_getter: out of " + "memory to open container"); + dbus_message_unref(reply); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + switch(type) { + case DBUS_TYPE_BYTE: + case DBUS_TYPE_BOOLEAN: + element_size = 1; + break; + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + element_size = sizeof(uint16_t); + break; + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + element_size = sizeof(uint32_t); + break; + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + element_size = sizeof(uint64_t); + break; + case DBUS_TYPE_DOUBLE: + element_size = sizeof(double); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + element_size = sizeof(char *); + break; + default: + wpa_printf(MSG_ERROR, "dbus: " + "wpas_dbus_simple_array_property_getter: " + "fatal: unknown element type"); + element_size = 1; + break; + } + + for (i = 0; i < array_len; i++) { + dbus_message_iter_append_basic(&array_iter, type, + array + i * element_size); + } + + if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || + !dbus_message_iter_close_container(&iter, &variant_iter)) { + wpa_printf(MSG_ERROR, "dbus: " + "wpas_dbus_simple_array_property_getter: out of " + "memory to close container"); + dbus_message_unref(reply); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + return reply; +} + + +/** + * wpas_dbus_handler_create_interface - Request registration of a network iface + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: The object path of the new interface object, + * or a dbus error message with more information + * + * Handler function for "CreateInterface" method call. Handles requests + * by dbus clients to register a network interface that wpa_supplicant + * will manage. + */ +DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + char *driver = NULL; + char *ifname = NULL; + char *confname = NULL; + char *bridge_ifname = NULL; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + goto error; + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + if (!strcmp(entry.key, "Driver") && + (entry.type == DBUS_TYPE_STRING)) { + driver = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + if (driver == NULL) + goto error; + } else if (!strcmp(entry.key, "Ifname") && + (entry.type == DBUS_TYPE_STRING)) { + ifname = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + if (ifname == NULL) + goto error; + } else if (!strcmp(entry.key, "ConfigFile") && + (entry.type == DBUS_TYPE_STRING)) { + confname = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + if (confname == NULL) + goto error; + } else if (!strcmp(entry.key, "BridgeIfname") && + (entry.type == DBUS_TYPE_STRING)) { + bridge_ifname = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + if (bridge_ifname == NULL) + goto error; + } else { + wpa_dbus_dict_entry_clear(&entry); + goto error; + } + } + + if (ifname == NULL) + goto error; /* Required Ifname argument missing */ + + /* + * Try to get the wpa_supplicant record for this iface, return + * an error if we already control it. + */ + if (wpa_supplicant_get_iface(global, ifname) != NULL) { + reply = dbus_message_new_error(message, + WPAS_DBUS_ERROR_IFACE_EXISTS, + "wpa_supplicant already " + "controls this interface."); + } else { + struct wpa_supplicant *wpa_s; + struct wpa_interface iface; + os_memset(&iface, 0, sizeof(iface)); + iface.driver = driver; + iface.ifname = ifname; + iface.confname = confname; + iface.bridge_ifname = bridge_ifname; + /* Otherwise, have wpa_supplicant attach to it. */ + if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { + const char *path = wpa_s->dbus_new_path; + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, + &path, DBUS_TYPE_INVALID); + } else { + reply = wpas_dbus_error_unknown_error( + message, "wpa_supplicant couldn't grab this " + "interface."); + } + } + +out: + os_free(driver); + os_free(ifname); + os_free(bridge_ifname); + return reply; + +error: + reply = wpas_dbus_error_invalid_args(message, NULL); + goto out; +} + + +/** + * wpas_dbus_handler_remove_interface - Request deregistration of an interface + * @message: Pointer to incoming dbus message + * @global: wpa_supplicant global data structure + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0), or returns a dbus error message with more information + * + * Handler function for "removeInterface" method call. Handles requests + * by dbus clients to deregister a network interface that wpa_supplicant + * currently manages. + */ +DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, + struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s; + char *path; + DBusMessage *reply = NULL; + + dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + wpa_s = get_iface_by_dbus_path(global, path); + if (wpa_s == NULL) + reply = wpas_dbus_error_iface_unknown(message); + else if (wpa_supplicant_remove_iface(global, wpa_s)) { + reply = wpas_dbus_error_unknown_error( + message, "wpa_supplicant couldn't remove this " + "interface."); + } + + return reply; +} + + +/** + * wpas_dbus_handler_get_interface - Get the object path for an interface name + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: The object path of the interface object, + * or a dbus error message with more information + * + * Handler function for "getInterface" method call. + */ +DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply = NULL; + const char *ifname; + const char *path; + struct wpa_supplicant *wpa_s; + + dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &ifname, + DBUS_TYPE_INVALID); + + wpa_s = wpa_supplicant_get_iface(global, ifname); + if (wpa_s == NULL) + return wpas_dbus_error_iface_unknown(message); + + path = wpa_s->dbus_new_path; + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + dbus_message_unref(reply); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + return reply; +} + + +/** + * wpas_dbus_getter_debug_level - Get debug level + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: DBus message with value of debug level + * + * Getter for "DebugLevel" property. + */ +DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message, + struct wpa_global *global) +{ + const char *str; + int idx = wpa_debug_level; + if (idx < 0) + idx = 0; + if (idx > 5) + idx = 5; + str = debug_strings[idx]; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, + &str); +} + + +/** + * wpas_dbus_getter_debug_timestamp - Get debug timestamp + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: DBus message with value of debug timestamp + * + * Getter for "DebugTimestamp" property. + */ +DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message, + struct wpa_global *global) +{ + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, + &wpa_debug_timestamp); + +} + + +/** + * wpas_dbus_getter_debug_show_keys - Get debug show keys + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: DBus message with value of debug show_keys + * + * Getter for "DebugShowKeys" property. + */ +DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message, + struct wpa_global *global) +{ + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, + &wpa_debug_show_keys); + +} + +/** + * wpas_dbus_setter_debug_level - Set debug level + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: %NULL or DBus error message + * + * Setter for "DebugLevel" property. + */ +DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply; + const char *str = NULL; + int i, val = -1; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_STRING, + &str); + if (reply) + return reply; + + for (i = 0; debug_strings[i]; i++) + if (os_strcmp(debug_strings[i], str) == 0) { + val = i; + break; + } + + if (val < 0 || + wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp, + wpa_debug_show_keys)) { + return wpas_dbus_error_invalid_args( + message, "Wrong debug level value"); + } + + return NULL; +} + + +/** + * wpas_dbus_setter_debug_timestamp - Set debug timestamp + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: %NULL or DBus error message + * + * Setter for "DebugTimestamp" property. + */ +DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply; + dbus_bool_t val; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN, + &val); + if (reply) + return reply; + + wpa_supplicant_set_debug_params(global, wpa_debug_level, val ? 1 : 0, + wpa_debug_show_keys); + + return NULL; +} + + +/** + * wpas_dbus_setter_debug_show_keys - Set debug show keys + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: %NULL or DBus error message + * + * Setter for "DebugShowKeys" property. + */ +DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply; + dbus_bool_t val; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN, + &val); + if (reply) + return reply; + + wpa_supplicant_set_debug_params(global, wpa_debug_level, + wpa_debug_timestamp, + val ? 1 : 0); + + return NULL; +} + + +/** + * wpas_dbus_getter_interfaces - Request registered interfaces list + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: The object paths array containing registered interfaces + * objects paths or DBus error on failure + * + * Getter for "Interfaces" property. Handles requests + * by dbus clients to return list of registered interfaces objects + * paths + */ +DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply = NULL; + struct wpa_supplicant *wpa_s; + const char **paths; + unsigned int i = 0, num = 0; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) + num++; + + paths = os_zalloc(num * sizeof(char*)); + if (!paths) { + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) + paths[i++] = wpa_s->dbus_new_path; + + reply = wpas_dbus_simple_array_property_getter(message, + DBUS_TYPE_OBJECT_PATH, + paths, num); + + os_free(paths); + return reply; +} + + +/** + * wpas_dbus_getter_eap_methods - Request supported EAP methods list + * @message: Pointer to incoming dbus message + * @nothing: not used argument. may be NULL or anything else + * Returns: The object paths array containing supported EAP methods + * represented by strings or DBus error on failure + * + * Getter for "EapMethods" property. Handles requests + * by dbus clients to return list of strings with supported EAP methods + */ +DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, void *nothing) +{ + DBusMessage *reply = NULL; + char **eap_methods; + size_t num_items = 0; + + eap_methods = eap_get_names_as_string_array(&num_items); + if (!eap_methods) { + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + reply = wpas_dbus_simple_array_property_getter(message, + DBUS_TYPE_STRING, + eap_methods, num_items); + + while (num_items) + os_free(eap_methods[--num_items]); + os_free(eap_methods); + return reply; +} + + +static int wpas_dbus_get_scan_type(DBusMessage *message, DBusMessageIter *var, + char **type, DBusMessage **reply) +{ + if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_STRING) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Type must be a string"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong Type value type. String required"); + return -1; + } + dbus_message_iter_get_basic(var, type); + return 0; +} + + +static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, + struct wpa_driver_scan_params *params, + DBusMessage **reply) +{ + struct wpa_driver_scan_ssid *ssids = params->ssids; + size_t ssids_num = 0; + u8 *ssid; + DBusMessageIter array_iter, sub_array_iter; + char *val; + int len; + + if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids " + "must be an array of arrays of bytes"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong SSIDs value type. Array of arrays of " + "bytes required"); + return -1; + } + + dbus_message_iter_recurse(var, &array_iter); + + if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) + { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids " + "must be an array of arrays of bytes"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong SSIDs value type. Array of arrays of " + "bytes required"); + return -1; + } + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) + { + if (ssids_num >= WPAS_MAX_SCAN_SSIDS) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Too many ssids specified on scan dbus " + "call"); + *reply = wpas_dbus_error_invalid_args( + message, "Too many ssids specified. Specify " + "at most four"); + return -1; + } + + dbus_message_iter_recurse(&array_iter, &sub_array_iter); + + dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len); + + if (len != 0) { + ssid = os_malloc(len); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, + "wpas_dbus_handler_scan[dbus]: " + "out of memory. Cannot allocate " + "memory for SSID"); + *reply = dbus_message_new_error( + message, DBUS_ERROR_NO_MEMORY, NULL); + return -1; + } + os_memcpy(ssid, val, len); + } else { + /* Allow zero-length SSIDs */ + ssid = NULL; + } + + ssids[ssids_num].ssid = ssid; + ssids[ssids_num].ssid_len = len; + + dbus_message_iter_next(&array_iter); + ssids_num++; + } + + params->num_ssids = ssids_num; + return 0; +} + + +static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var, + struct wpa_driver_scan_params *params, + DBusMessage **reply) +{ + u8 *ies = NULL, *nies; + int ies_len = 0; + DBusMessageIter array_iter, sub_array_iter; + char *val; + int len; + + if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must " + "be an array of arrays of bytes"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong IEs value type. Array of arrays of " + "bytes required"); + return -1; + } + + dbus_message_iter_recurse(var, &array_iter); + + if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) + { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must " + "be an array of arrays of bytes"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong IEs value type. Array required"); + return -1; + } + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) + { + dbus_message_iter_recurse(&array_iter, &sub_array_iter); + + dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len); + if (len == 0) { + dbus_message_iter_next(&array_iter); + continue; + } + + nies = os_realloc(ies, ies_len + len); + if (nies == NULL) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "out of memory. Cannot allocate memory for " + "IE"); + os_free(ies); + *reply = dbus_message_new_error( + message, DBUS_ERROR_NO_MEMORY, NULL); + return -1; + } + ies = nies; + os_memcpy(ies + ies_len, val, len); + ies_len += len; + + dbus_message_iter_next(&array_iter); + } + + params->extra_ies = ies; + params->extra_ies_len = ies_len; + return 0; +} + + +static int wpas_dbus_get_scan_channels(DBusMessage *message, + DBusMessageIter *var, + struct wpa_driver_scan_params *params, + DBusMessage **reply) +{ + DBusMessageIter array_iter, sub_array_iter; + int *freqs = NULL, *nfreqs; + int freqs_num = 0; + + if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Channels must be an array of structs"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong Channels value type. Array of structs " + "required"); + return -1; + } + + dbus_message_iter_recurse(var, &array_iter); + + if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_STRUCT) { + wpa_printf(MSG_DEBUG, + "wpas_dbus_handler_scan[dbus]: Channels must be an " + "array of structs"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong Channels value type. Array of structs " + "required"); + return -1; + } + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT) + { + int freq, width; + + dbus_message_iter_recurse(&array_iter, &sub_array_iter); + + if (dbus_message_iter_get_arg_type(&sub_array_iter) != + DBUS_TYPE_UINT32) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Channel must by specified by struct of " + "two UINT32s %c", + dbus_message_iter_get_arg_type( + &sub_array_iter)); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong Channel struct. Two UINT32s " + "required"); + os_free(freqs); + return -1; + } + dbus_message_iter_get_basic(&sub_array_iter, &freq); + + if (!dbus_message_iter_next(&sub_array_iter) || + dbus_message_iter_get_arg_type(&sub_array_iter) != + DBUS_TYPE_UINT32) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Channel must by specified by struct of " + "two UINT32s"); + *reply = wpas_dbus_error_invalid_args( + message, + "Wrong Channel struct. Two UINT32s required"); + os_free(freqs); + return -1; + } + + dbus_message_iter_get_basic(&sub_array_iter, &width); + +#define FREQS_ALLOC_CHUNK 32 + if (freqs_num % FREQS_ALLOC_CHUNK == 0) { + nfreqs = os_realloc(freqs, sizeof(int) * + (freqs_num + FREQS_ALLOC_CHUNK)); + if (nfreqs == NULL) + os_free(freqs); + freqs = nfreqs; + } + if (freqs == NULL) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "out of memory. can't allocate memory for " + "freqs"); + *reply = dbus_message_new_error( + message, DBUS_ERROR_NO_MEMORY, NULL); + return -1; + } + + freqs[freqs_num] = freq; + + freqs_num++; + dbus_message_iter_next(&array_iter); + } + + nfreqs = os_realloc(freqs, + sizeof(int) * (freqs_num + 1)); + if (nfreqs == NULL) + os_free(freqs); + freqs = nfreqs; + if (freqs == NULL) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "out of memory. Can't allocate memory for freqs"); + *reply = dbus_message_new_error( + message, DBUS_ERROR_NO_MEMORY, NULL); + return -1; + } + freqs[freqs_num] = 0; + + params->freqs = freqs; + return 0; +} + + +/** + * wpas_dbus_handler_scan - Request a wireless scan on an interface + * @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 "Scan" method call of a network device. Requests + * that wpa_supplicant perform a wireless scan as soon as possible + * on a particular wireless interface. + */ +DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, dict_iter, entry_iter, variant_iter; + char *key = NULL, *type = NULL; + struct wpa_driver_scan_params params; + size_t i; + + os_memset(¶ms, 0, sizeof(params)); + + dbus_message_iter_init(message, &iter); + + dbus_message_iter_recurse(&iter, &dict_iter); + + while (dbus_message_iter_get_arg_type(&dict_iter) == + DBUS_TYPE_DICT_ENTRY) { + dbus_message_iter_recurse(&dict_iter, &entry_iter); + dbus_message_iter_get_basic(&entry_iter, &key); + dbus_message_iter_next(&entry_iter); + dbus_message_iter_recurse(&entry_iter, &variant_iter); + + if (os_strcmp(key, "Type") == 0) { + if (wpas_dbus_get_scan_type(message, &variant_iter, + &type, &reply) < 0) + goto out; + } else if (os_strcmp(key, "SSIDs") == 0) { + if (wpas_dbus_get_scan_ssids(message, &variant_iter, + ¶ms, &reply) < 0) + goto out; + } else if (os_strcmp(key, "IEs") == 0) { + if (wpas_dbus_get_scan_ies(message, &variant_iter, + ¶ms, &reply) < 0) + goto out; + } else if (os_strcmp(key, "Channels") == 0) { + if (wpas_dbus_get_scan_channels(message, &variant_iter, + ¶ms, &reply) < 0) + goto out; + } else { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Unknown argument %s", key); + reply = wpas_dbus_error_invalid_args(message, key); + goto out; + } + + dbus_message_iter_next(&dict_iter); + } + + if (!type) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Scan type not specified"); + reply = wpas_dbus_error_invalid_args(message, key); + goto out; + } + + if (!os_strcmp(type, "passive")) { + if (params.num_ssids || params.extra_ies_len) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "SSIDs or IEs specified for passive scan."); + reply = wpas_dbus_error_invalid_args( + message, "You can specify only Channels in " + "passive scan"); + goto out; + } else if (params.freqs && params.freqs[0]) { + wpa_supplicant_trigger_scan(wpa_s, ¶ms); + } else { + wpa_s->scan_req = 2; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + } else if (!os_strcmp(type, "active")) { + if (!params.num_ssids) { + /* Add wildcard ssid */ + params.num_ssids++; + } + wpa_supplicant_trigger_scan(wpa_s, ¶ms); + } else { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Unknown scan type: %s", type); + reply = wpas_dbus_error_invalid_args(message, + "Wrong scan type"); + goto out; + } + +out: + for (i = 0; i < WPAS_MAX_SCAN_SSIDS; i++) + os_free((u8 *) params.ssids[i].ssid); + os_free((u8 *) params.extra_ies); + os_free(params.freqs); + return reply; +} + + +/* + * wpas_dbus_handler_disconnect - Terminate the current connection + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NotConnected DBus error message if already not connected + * or NULL otherwise. + * + * Handler function for "Disconnect" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->current_ssid != NULL) { + wpa_s->disconnected = 1; + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + + return NULL; + } + + return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED, + "This interface is not connected"); +} + + +/** + * wpas_dbus_new_iface_add_network - Add a new configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing the object path of the new network + * + * Handler function for "AddNetwork" method call of a network interface. + */ +DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_ssid *ssid = NULL; + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; + + dbus_message_iter_init(message, &iter); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "wpas_dbus_handler_add_network[dbus]: " + "can't add new interface."); + reply = wpas_dbus_error_unknown_error( + message, + "wpa_supplicant could not add " + "a network on this interface."); + goto err; + } + wpas_notify_network_added(wpa_s, ssid); + ssid->disabled = 1; + wpa_config_set_network_defaults(ssid); + + reply = set_network_properties(message, wpa_s, ssid, &iter); + if (reply) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:" + "control interface couldn't set network " + "properties"); + goto err; + } + + /* Construct the object path for this network. */ + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d", + wpa_s->dbus_new_path, ssid->id); + + reply = dbus_message_new_method_return(message); + if (reply == NULL) { + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto err; + } + if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto err; + } + + return reply; + +err: + if (ssid) { + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + } + return reply; +} + + +/** + * wpas_dbus_handler_remove_network - Remove a configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "RemoveNetwork" method call of a network interface. + */ +DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *op; + char *iface = NULL, *net_id = NULL; + int id; + struct wpa_ssid *ssid; + + dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID); + + /* Extract the network ID and ensure the network */ + /* is actually a child of this interface */ + iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL); + if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + id = strtoul(net_id, NULL, 10); + if (errno == EINVAL) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + reply = wpas_dbus_error_network_unknown(message); + goto out; + } + + 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); + +out: + os_free(iface); + os_free(net_id); + return reply; +} + + +static void remove_network(void *arg, struct wpa_ssid *ssid) +{ + struct wpa_supplicant *wpa_s = arg; + + wpas_notify_network_removed(wpa_s, ssid); + + if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) { + wpa_printf(MSG_ERROR, + "wpas_dbus_handler_remove_all_networks[dbus]: " + "error occurred when removing network %d", + ssid->id); + return; + } + + if (ssid == wpa_s->current_ssid) + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); +} + + +/** + * wpas_dbus_handler_remove_all_networks - Remove all configured networks + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "RemoveAllNetworks" method call of a network interface. + */ +DBusMessage * wpas_dbus_handler_remove_all_networks( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + /* NB: could check for failure and return an error */ + wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s); + return NULL; +} + + +/** + * wpas_dbus_handler_select_network - Attempt association with a network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "SelectNetwork" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *op; + char *iface = NULL, *net_id = NULL; + int id; + struct wpa_ssid *ssid; + + dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID); + + /* Extract the network ID and ensure the network */ + /* is actually a child of this interface */ + iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL); + if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + id = strtoul(net_id, NULL, 10); + if (errno == EINVAL) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + reply = wpas_dbus_error_network_unknown(message); + goto out; + } + + /* Finally, associate with the network */ + wpa_supplicant_select_network(wpa_s, ssid); + +out: + os_free(iface); + os_free(net_id); + return reply; +} + + +/** + * wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates) + * @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 + * + * Asks wpa_supplicant to internally store a binary blobs. + */ +DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, array_iter; + + char *blob_name; + u8 *blob_data; + int blob_len; + struct wpa_config_blob *blob = NULL; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &blob_name); + + if (wpa_config_get_blob(wpa_s->conf, blob_name)) { + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_BLOB_EXISTS, + NULL); + } + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &array_iter); + + dbus_message_iter_get_fixed_array(&array_iter, &blob_data, &blob_len); + + blob = os_zalloc(sizeof(*blob)); + if (!blob) { + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto err; + } + + blob->data = os_malloc(blob_len); + if (!blob->data) { + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto err; + } + os_memcpy(blob->data, blob_data, blob_len); + + blob->len = blob_len; + blob->name = os_strdup(blob_name); + if (!blob->name) { + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto err; + } + + wpa_config_set_blob(wpa_s->conf, blob); + wpas_notify_blob_added(wpa_s, blob->name); + + return reply; + +err: + if (blob) { + os_free(blob->name); + os_free(blob->data); + os_free(blob); + } + return reply; +} + + +/** + * wpas_dbus_handler_get_blob - Get named binary blob (ie, for certificates) + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing array of bytes (blob) + * + * Gets one wpa_supplicant's binary blobs. + */ +DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, array_iter; + + char *blob_name; + const struct wpa_config_blob *blob; + + dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &blob_name, + DBUS_TYPE_INVALID); + + blob = wpa_config_get_blob(wpa_s->conf, blob_name); + if (!blob) { + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_BLOB_UNKNOWN, + "Blob id not set"); + } + + reply = dbus_message_new_method_return(message); + if (!reply) { + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &array_iter)) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + + if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, + &(blob->data), blob->len)) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + + if (!dbus_message_iter_close_container(&iter, &array_iter)) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + +out: + return reply; +} + + +/** + * wpas_remove_handler_remove_blob - Remove named binary blob + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: NULL on success or dbus error + * + * Asks wpa_supplicant to internally remove a binary blobs. + */ +DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + char *blob_name; + + dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &blob_name, + DBUS_TYPE_INVALID); + + if (wpa_config_remove_blob(wpa_s->conf, blob_name)) { + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_BLOB_UNKNOWN, + "Blob id not set"); + } + wpas_notify_blob_removed(wpa_s, blob_name); + + return reply; + +} + +/* + * wpas_dbus_handler_flush_bss - Flush the BSS cache + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Handler function for "FlushBSS" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t age; + + dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &age, + DBUS_TYPE_INVALID); + + if (age == 0) + wpa_bss_flush(wpa_s); + else + wpa_bss_flush_by_age(wpa_s, age); + + return NULL; +} + + +/** + * wpas_dbus_getter_capabilities - Return interface capabilities + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a dict of strings + * + * Getter for "Capabilities" property of an interface. + */ +DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_driver_capa capa; + int res; + DBusMessageIter iter, iter_dict; + DBusMessageIter iter_dict_entry, iter_dict_val, iter_array, + variant_iter; + const char *scans[] = { "active", "passive", "ssid" }; + const char *modes[] = { "infrastructure", "ad-hoc", "ap" }; + int n = sizeof(modes) / sizeof(char *); + + if (message == NULL) + reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); + else + reply = dbus_message_new_method_return(message); + if (!reply) + goto nomem; + + dbus_message_iter_init_append(reply, &iter); + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter)) + goto nomem; + + if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) + goto nomem; + + res = wpa_drv_get_capa(wpa_s, &capa); + + /***** pairwise cipher */ + if (res < 0) { + const char *args[] = {"ccmp", "tkip", "none"}; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "Pairwise", args, + sizeof(args) / sizeof(char*))) + goto nomem; + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp")) + goto nomem; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "tkip")) + goto nomem; + } + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "none")) + goto nomem; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + } + + /***** group cipher */ + if (res < 0) { + const char *args[] = { + "ccmp", "tkip", "wep104", "wep40" + }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "Group", args, + sizeof(args) / sizeof(char*))) + goto nomem; + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp")) + goto nomem; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "tkip")) + goto nomem; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wep104")) + goto nomem; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wep40")) + goto nomem; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + } + + /***** key management */ + if (res < 0) { + const char *args[] = { + "wpa-psk", "wpa-eap", "ieee8021x", "wpa-none", +#ifdef CONFIG_WPS + "wps", +#endif /* CONFIG_WPS */ + "none" + }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "KeyMgmt", args, + sizeof(args) / sizeof(char*))) + goto nomem; + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + "none")) + goto nomem; + + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + "ieee8021x")) + goto nomem; + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-eap")) + goto nomem; + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-ft-eap")) + goto nomem; + +/* TODO: Ensure that driver actually supports sha256 encryption. */ +#ifdef CONFIG_IEEE80211W + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-eap-sha256")) + goto nomem; +#endif /* CONFIG_IEEE80211W */ + } + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-psk")) + goto nomem; + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-ft-psk")) + goto nomem; + +/* TODO: Ensure that driver actually supports sha256 encryption. */ +#ifdef CONFIG_IEEE80211W + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-psk-sha256")) + goto nomem; +#endif /* CONFIG_IEEE80211W */ + } + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-none")) + goto nomem; + } + + +#ifdef CONFIG_WPS + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + "wps")) + goto nomem; +#endif /* CONFIG_WPS */ + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + } + + /***** WPA protocol */ + if (res < 0) { + const char *args[] = { "rsn", "wpa" }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "Protocol", args, + sizeof(args) / sizeof(char*))) + goto nomem; + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "rsn")) + goto nomem; + } + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa")) + goto nomem; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + } + + /***** auth alg */ + if (res < 0) { + const char *args[] = { "open", "shared", "leap" }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "AuthAlg", args, + sizeof(args) / sizeof(char*))) + goto nomem; + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + + if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "open")) + goto nomem; + } + + if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "shared")) + goto nomem; + } + + if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "leap")) + goto nomem; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + } + + /***** Scan */ + if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans, + sizeof(scans) / sizeof(char *))) + goto nomem; + + /***** Modes */ + if (res < 0 || !(capa.flags & WPA_DRIVER_FLAGS_AP)) + n--; /* exclude ap mode if it is not supported by the driver */ + if (!wpa_dbus_dict_append_string_array(&iter_dict, "Modes", modes, n)) + goto nomem; + + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) + goto nomem; + if (!dbus_message_iter_close_container(&iter, &variant_iter)) + goto nomem; + + return reply; + +nomem: + if (reply) + dbus_message_unref(reply); + + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL); +} + + +/** + * wpas_dbus_getter_state - Get interface state + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a STRING representing the current + * interface state + * + * Getter for "State" property. + */ +DBusMessage * wpas_dbus_getter_state(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *str_state; + char *state_ls, *tmp; + + str_state = wpa_supplicant_state_txt(wpa_s->wpa_state); + + /* make state string lowercase to fit new DBus API convention + */ + state_ls = tmp = os_strdup(str_state); + if (!tmp) { + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + while (*tmp) { + *tmp = tolower(*tmp); + tmp++; + } + + reply = wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, + &state_ls); + + os_free(state_ls); + + return reply; +} + + +/** + * wpas_dbus_new_iface_get_scanning - Get interface scanning state + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing whether the interface is scanning + * + * Getter for "scanning" property. + */ +DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, + &scanning); +} + + +/** + * wpas_dbus_getter_ap_scan - Control roaming mode + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A message containong value of ap_scan variable + * + * Getter function for "ApScan" property. + */ +DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t ap_scan = wpa_s->conf->ap_scan; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT32, + &ap_scan); +} + + +/** + * wpas_dbus_setter_ap_scan - Control roaming mode + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Setter function for "ApScan" property. + */ +DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + dbus_uint32_t ap_scan; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_UINT32, + &ap_scan); + if (reply) + return reply; + + if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) { + return wpas_dbus_error_invalid_args( + message, "ap_scan must equal 0, 1 or 2"); + } + return NULL; +} + + +/** + * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A message containing value of bss_expiration_age variable + * + * Getter function for "BSSExpireAge" property. + */ +DBusMessage * wpas_dbus_getter_bss_expire_age(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT32, + &expire_age); +} + + +/** + * wpas_dbus_setter_bss_expire_age - Control BSS entry expiration age + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Setter function for "BSSExpireAge" property. + */ +DBusMessage * wpas_dbus_setter_bss_expire_age(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + dbus_uint32_t expire_age; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_UINT32, + &expire_age); + if (reply) + return reply; + + if (wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age)) { + return wpas_dbus_error_invalid_args( + message, "BSSExpireAge must be >=10"); + } + return NULL; +} + + +/** + * wpas_dbus_getter_bss_expire_count - Get BSS entry expiration scan count + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A message containing value of bss_expire_count variable + * + * Getter function for "BSSExpireCount" property. + */ +DBusMessage * wpas_dbus_getter_bss_expire_count(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_age; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT32, + &expire_count); +} + + +/** + * wpas_dbus_setter_bss_expire_count - Control BSS entry expiration scan count + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Setter function for "BSSExpireCount" property. + */ +DBusMessage * wpas_dbus_setter_bss_expire_count(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + dbus_uint32_t expire_count; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_UINT32, + &expire_count); + if (reply) + return reply; + + if (wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count)) { + return wpas_dbus_error_invalid_args( + message, "BSSExpireCount must be >0"); + } + return NULL; +} + + +/** + * wpas_dbus_getter_country - Control country code + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A message containong value of country variable + * + * Getter function for "Country" property. + */ +DBusMessage * wpas_dbus_getter_country(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + char country[3]; + char *str = country; + + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = '\0'; + + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, + &str); +} + + +/** + * wpas_dbus_setter_country - Control country code + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Setter function for "Country" property. + */ +DBusMessage * wpas_dbus_setter_country(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *country; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_STRING, + &country); + if (reply) + return reply; + + if (!country[0] || !country[1]) + return wpas_dbus_error_invalid_args(message, + "invalid country code"); + + if (wpa_s->drv_priv != NULL && wpa_drv_set_country(wpa_s, country)) { + wpa_printf(MSG_DEBUG, "Failed to set country"); + return wpas_dbus_error_invalid_args( + message, "failed to set country code"); + } + + wpa_s->conf->country[0] = country[0]; + wpa_s->conf->country[1] = country[1]; + return NULL; +} + + +/** + * wpas_dbus_getter_ifname - Get interface name + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a name of network interface + * associated with with wpa_s + * + * Getter for "Ifname" property. + */ +DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + const char *ifname = wpa_s->ifname; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, + &ifname); +} + + +/** + * wpas_dbus_getter_driver - Get interface name + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a name of network interface + * driver associated with with wpa_s + * + * Getter for "Driver" property. + */ +DBusMessage * wpas_dbus_getter_driver(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + const char *driver; + + if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) { + wpa_printf(MSG_DEBUG, "wpas_dbus_getter_driver[dbus]: " + "wpa_s has no driver set"); + return wpas_dbus_error_unknown_error(message, NULL); + } + + driver = wpa_s->driver->name; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, + &driver); +} + + +/** + * wpas_dbus_getter_current_bss - Get current bss object path + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a DBus object path to + * current BSS + * + * Getter for "CurrentBSS" property. + */ +DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply; + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf; + + if (wpa_s->current_bss) + os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, wpa_s->current_bss->id); + else + os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); + + reply = wpas_dbus_simple_property_getter(message, + DBUS_TYPE_OBJECT_PATH, + &bss_obj_path); + + return reply; +} + + +/** + * wpas_dbus_getter_current_network - Get current network object path + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a DBus object path to + * current network + * + * Getter for "CurrentNetwork" property. + */ +DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply; + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf; + + if (wpa_s->current_ssid) + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", + wpa_s->dbus_new_path, wpa_s->current_ssid->id); + else + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); + + reply = wpas_dbus_simple_property_getter(message, + DBUS_TYPE_OBJECT_PATH, + &net_obj_path); + + return reply; +} + + +/** + * wpas_dbus_getter_current_auth_mode - Get current authentication type + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a string indicating the current + * authentication type. + * + * Getter for "CurrentAuthMode" property. + */ +DBusMessage * wpas_dbus_getter_current_auth_mode(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply; + const char *eap_mode; + const char *auth_mode; + char eap_mode_buf[WPAS_DBUS_AUTH_MODE_MAX]; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + auth_mode = "INACTIVE"; + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + eap_mode = wpa_supplicant_get_eap_mode(wpa_s); + os_snprintf(eap_mode_buf, WPAS_DBUS_AUTH_MODE_MAX, + "EAP-%s", eap_mode); + auth_mode = eap_mode_buf; + + } else { + auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt, + wpa_s->current_ssid->proto); + } + + reply = wpas_dbus_simple_property_getter(message, + DBUS_TYPE_STRING, + &auth_mode); + + return reply; +} + + +/** + * wpas_dbus_getter_bridge_ifname - Get interface name + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a name of bridge network + * interface associated with with wpa_s + * + * Getter for "BridgeIfname" property. + */ +DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + const char *bridge_ifname = NULL; + + bridge_ifname = wpa_s->bridge_ifname; + if (bridge_ifname == NULL) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bridge_ifname[dbus]: " + "wpa_s has no bridge interface name set"); + return wpas_dbus_error_unknown_error(message, NULL); + } + + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, + &bridge_ifname); +} + + +/** + * wpas_dbus_getter_bsss - Get array of BSSs objects + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: a dbus message containing an array of all known BSS objects + * dbus paths + * + * Getter for "BSSs" property. + */ +DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_bss *bss; + char **paths; + unsigned int i = 0; + + paths = os_zalloc(wpa_s->num_bss * sizeof(char *)); + if (!paths) { + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + /* 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) { + paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (paths[i] == NULL) { + reply = dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + /* Construct the object path for this BSS. */ + os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, bss->id); + } + + reply = wpas_dbus_simple_array_property_getter(message, + DBUS_TYPE_OBJECT_PATH, + paths, wpa_s->num_bss); + +out: + while (i) + os_free(paths[--i]); + os_free(paths); + return reply; +} + + +/** + * wpas_dbus_getter_networks - Get array of networks objects + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: a dbus message containing an array of all configured + * networks dbus object paths. + * + * Getter for "Networks" property. + */ +DBusMessage * wpas_dbus_getter_networks(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_ssid *ssid; + char **paths; + unsigned int i = 0, num = 0; + + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_networks[dbus]: " + "An error occurred getting networks list."); + return wpas_dbus_error_unknown_error(message, NULL); + } + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) + num++; + + paths = os_zalloc(num * sizeof(char *)); + if (!paths) { + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + /* Loop through configured networks and append object path of each */ + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (paths[i] == NULL) { + reply = dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + + /* Construct the object path for this network. */ + os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d", + wpa_s->dbus_new_path, ssid->id); + } + + reply = wpas_dbus_simple_array_property_getter(message, + DBUS_TYPE_OBJECT_PATH, + paths, num); + +out: + while (i) + os_free(paths[--i]); + os_free(paths); + return reply; +} + + +/** + * wpas_dbus_getter_blobs - Get all blobs defined for this interface + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: a dbus message containing a dictionary of pairs (blob_name, blob) + * + * Getter for "Blobs" property. + */ +DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, variant_iter, dict_iter, entry_iter, array_iter; + struct wpa_config_blob *blob; + + if (message == NULL) + reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); + else + reply = dbus_message_new_method_return(message); + if (!reply) + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + "a{say}", &variant_iter) || + !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + "{say}", &dict_iter)) { + dbus_message_unref(reply); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + blob = wpa_s->conf->blobs; + while (blob) { + if (!dbus_message_iter_open_container(&dict_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &entry_iter) || + !dbus_message_iter_append_basic(&entry_iter, + DBUS_TYPE_STRING, + &(blob->name)) || + !dbus_message_iter_open_container(&entry_iter, + DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &array_iter) || + !dbus_message_iter_append_fixed_array(&array_iter, + DBUS_TYPE_BYTE, + &(blob->data), + blob->len) || + !dbus_message_iter_close_container(&entry_iter, + &array_iter) || + !dbus_message_iter_close_container(&dict_iter, + &entry_iter)) { + dbus_message_unref(reply); + return dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, + NULL); + } + + blob = blob->next; + } + + if (!dbus_message_iter_close_container(&variant_iter, &dict_iter) || + !dbus_message_iter_close_container(&iter, &variant_iter)) { + dbus_message_unref(reply); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + return reply; +} + + +/** + * wpas_dbus_getter_bss_bssid - Return the BSSID of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing the bssid for the requested bss + * + * Getter for "BSSID" property. + */ +DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_bssid[dbus]: no " + "bss with id %d found", bss->id); + return NULL; + } + + return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE, + res->bssid, ETH_ALEN); +} + + +/** + * wpas_dbus_getter_bss_ssid - Return the SSID of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing the ssid for the requested bss + * + * Getter for "SSID" property. + */ +DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ssid[dbus]: no " + "bss with id %d found", bss->id); + return NULL; + } + + return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE, + res->ssid, + res->ssid_len); +} + + +/** + * wpas_dbus_getter_bss_privacy - Return the privacy flag of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing the privacy flag value of requested bss + * + * Getter for "Privacy" property. + */ +DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + dbus_bool_t privacy; + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_privacy[dbus]: no " + "bss with id %d found", bss->id); + return NULL; + } + + privacy = (res->caps & IEEE80211_CAP_PRIVACY) ? TRUE : FALSE; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, + &privacy); +} + + +/** + * wpas_dbus_getter_bss_mode - Return the mode of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing the mode of requested bss + * + * Getter for "Mode" property. + */ +DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + const char *mode; + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_mode[dbus]: no " + "bss with id %d found", bss->id); + return NULL; + } + + if (res->caps & IEEE80211_CAP_IBSS) + mode = "ad-hoc"; + else + mode = "infrastructure"; + + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, + &mode); +} + + +/** + * wpas_dbus_getter_bss_level - Return the signal strength of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing the signal strength of requested bss + * + * Getter for "Level" property. + */ +DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_signal[dbus]: no " + "bss with id %d found", bss->id); + return NULL; + } + + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_INT16, + &res->level); +} + + +/** + * wpas_dbus_getter_bss_frequency - Return the frequency of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing the frequency of requested bss + * + * Getter for "Frequency" property. + */ +DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_frequency[dbus]: " + "no bss with id %d found", bss->id); + return NULL; + } + + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT16, + &res->freq); +} + + +static int cmp_u8s_desc(const void *a, const void *b) +{ + return (*(u8 *) b - *(u8 *) a); +} + + +/** + * wpas_dbus_getter_bss_rates - Return available bit rates of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing sorted array of bit rates + * + * Getter for "Rates" property. + */ +DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message, + struct bss_handler_args *bss) +{ + DBusMessage *reply; + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + u8 *ie_rates = NULL; + u32 *real_rates; + int rates_num, i; + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rates[dbus]: " + "no bss with id %d found", bss->id); + return NULL; + } + + rates_num = wpa_bss_get_bit_rates(res, &ie_rates); + if (rates_num < 0) + return NULL; + + qsort(ie_rates, rates_num, 1, cmp_u8s_desc); + + real_rates = os_malloc(sizeof(u32) * rates_num); + if (!real_rates) { + os_free(ie_rates); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + for (i = 0; i < rates_num; i++) + real_rates[i] = ie_rates[i] * 500000; + + reply = wpas_dbus_simple_array_property_getter(message, + DBUS_TYPE_UINT32, + real_rates, rates_num); + + os_free(ie_rates); + os_free(real_rates); + return reply; +} + + +static DBusMessage * wpas_dbus_get_bss_security_prop( + DBusMessage *message, struct wpa_ie_data *ie_data) +{ + DBusMessage *reply; + DBusMessageIter iter, iter_dict, variant_iter; + const char *group; + const char *pairwise[2]; /* max 2 pairwise ciphers is supported */ + const char *key_mgmt[7]; /* max 7 key managements may be supported */ + int n; + + if (message == NULL) + reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); + else + reply = dbus_message_new_method_return(message); + if (!reply) + goto nomem; + + dbus_message_iter_init_append(reply, &iter); + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter)) + goto nomem; + + if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) + goto nomem; + + /* KeyMgmt */ + n = 0; + if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK) + key_mgmt[n++] = "wpa-psk"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_PSK) + key_mgmt[n++] = "wpa-ft-psk"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + key_mgmt[n++] = "wpa-psk-sha256"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X) + key_mgmt[n++] = "wpa-eap"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + key_mgmt[n++] = "wpa-ft-eap"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + key_mgmt[n++] = "wpa-eap-sha256"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE) + key_mgmt[n++] = "wpa-none"; + + if (!wpa_dbus_dict_append_string_array(&iter_dict, "KeyMgmt", + key_mgmt, n)) + goto nomem; + + /* Group */ + switch (ie_data->group_cipher) { + case WPA_CIPHER_WEP40: + group = "wep40"; + break; + case WPA_CIPHER_TKIP: + group = "tkip"; + break; + case WPA_CIPHER_CCMP: + group = "ccmp"; + break; + case WPA_CIPHER_WEP104: + group = "wep104"; + break; + default: + group = ""; + break; + } + + if (!wpa_dbus_dict_append_string(&iter_dict, "Group", group)) + goto nomem; + + /* Pairwise */ + n = 0; + if (ie_data->pairwise_cipher & WPA_CIPHER_TKIP) + pairwise[n++] = "tkip"; + if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP) + pairwise[n++] = "ccmp"; + + if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise", + pairwise, n)) + goto nomem; + + /* Management group (RSN only) */ + if (ie_data->proto == WPA_PROTO_RSN) { + switch (ie_data->mgmt_group_cipher) { +#ifdef CONFIG_IEEE80211W + case WPA_CIPHER_AES_128_CMAC: + group = "aes128cmac"; + break; +#endif /* CONFIG_IEEE80211W */ + default: + group = ""; + break; + } + + if (!wpa_dbus_dict_append_string(&iter_dict, "MgmtGroup", + group)) + goto nomem; + } + + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) + goto nomem; + if (!dbus_message_iter_close_container(&iter, &variant_iter)) + goto nomem; + + return reply; + +nomem: + if (reply) + dbus_message_unref(reply); + + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL); +} + + +/** + * wpas_dbus_getter_bss_wpa - Return the WPA options of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing the WPA options of requested bss + * + * Getter for "WPA" property. + */ +DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct wpa_ie_data wpa_data; + const u8 *ie; + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_wpa[dbus]: no " + "bss with id %d found", bss->id); + return NULL; + } + + os_memset(&wpa_data, 0, sizeof(wpa_data)); + ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE); + if (ie) { + if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) + return wpas_dbus_error_unknown_error(message, + "invalid WPA IE"); + } + + return wpas_dbus_get_bss_security_prop(message, &wpa_data); +} + + +/** + * wpas_dbus_getter_bss_rsn - Return the RSN options of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing the RSN options of requested bss + * + * Getter for "RSN" property. + */ +DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct wpa_ie_data wpa_data; + const u8 *ie; + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rsn[dbus]: no " + "bss with id %d found", bss->id); + return NULL; + } + + os_memset(&wpa_data, 0, sizeof(wpa_data)); + ie = wpa_bss_get_ie(res, WLAN_EID_RSN); + if (ie) { + if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) + return wpas_dbus_error_unknown_error(message, + "invalid RSN IE"); + } + + return wpas_dbus_get_bss_security_prop(message, &wpa_data); +} + + +/** + * wpas_dbus_getter_bss_ies - Return all IEs of a BSS + * @message: Pointer to incoming dbus message + * @bss: a pair of interface describing structure and bss's id + * Returns: a dbus message containing IEs byte array + * + * Getter for "IEs" property. + */ +DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message, + struct bss_handler_args *bss) +{ + struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + + if (!res) { + wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ies[dbus]: no " + "bss with id %d found", bss->id); + return NULL; + } + + return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE, + res + 1, res->ie_len); +} + + +/** + * wpas_dbus_getter_enabled - Check whether network is enabled or disabled + * @message: Pointer to incoming dbus message + * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface + * and wpa_ssid structure for a configured network + * Returns: DBus message with boolean indicating state of configured network + * or DBus error on failure + * + * Getter for "enabled" property of a configured network. + */ +DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message, + struct network_handler_args *net) +{ + dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE; + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, + &enabled); +} + + +/** + * wpas_dbus_setter_enabled - Mark a configured network as enabled or disabled + * @message: Pointer to incoming dbus message + * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface + * and wpa_ssid structure for a configured network + * Returns: NULL indicating success or DBus error on failure + * + * Setter for "Enabled" property of a configured network. + */ +DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message, + struct network_handler_args *net) +{ + DBusMessage *reply = NULL; + + struct wpa_supplicant *wpa_s; + struct wpa_ssid *ssid; + + dbus_bool_t enable; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN, + &enable); + + if (reply) + return reply; + + wpa_s = net->wpa_s; + ssid = net->ssid; + + if (enable) + wpa_supplicant_enable_network(wpa_s, ssid); + else + wpa_supplicant_disable_network(wpa_s, ssid); + + return NULL; +} + + +/** + * wpas_dbus_getter_network_properties - Get options for a configured network + * @message: Pointer to incoming dbus message + * @net: wpa_supplicant structure for a network interface and + * wpa_ssid structure for a configured network + * Returns: DBus message with network properties or DBus error on failure + * + * Getter for "Properties" property of a configured network. + */ +DBusMessage * wpas_dbus_getter_network_properties( + DBusMessage *message, struct network_handler_args *net) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, variant_iter, dict_iter; + char **iterator; + char **props = wpa_config_get_all(net->ssid, 1); + if (!props) + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + + if (message == NULL) + reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); + else + reply = dbus_message_new_method_return(message); + if (!reply) { + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter) || + !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + + iterator = props; + while (*iterator) { + if (!wpa_dbus_dict_append_string(&dict_iter, *iterator, + *(iterator + 1))) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + iterator += 2; + } + + + if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) || + !dbus_message_iter_close_container(&iter, &variant_iter)) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto out; + } + +out: + iterator = props; + while (*iterator) { + os_free(*iterator); + iterator++; + } + os_free(props); + return reply; +} + + +/** + * wpas_dbus_setter_network_properties - Set options for a configured network + * @message: Pointer to incoming dbus message + * @net: wpa_supplicant structure for a network interface and + * wpa_ssid structure for a configured network + * Returns: NULL indicating success or DBus error on failure + * + * Setter for "Properties" property of a configured network. + */ +DBusMessage * wpas_dbus_setter_network_properties( + DBusMessage *message, struct network_handler_args *net) +{ + struct wpa_ssid *ssid = net->ssid; + + DBusMessage *reply = NULL; + DBusMessageIter iter, variant_iter; + + dbus_message_iter_init(message, &iter); + + dbus_message_iter_next(&iter); + dbus_message_iter_next(&iter); + + dbus_message_iter_recurse(&iter, &variant_iter); + + reply = set_network_properties(message, net->wpa_s, ssid, + &variant_iter); + if (reply) + wpa_printf(MSG_DEBUG, "dbus control interface couldn't set " + "network properties"); + + return reply; +} diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h new file mode 100644 index 0000000..742d33c --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -0,0 +1,223 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.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 CTRL_IFACE_DBUS_NEW_HANDLERS_H +#define CTRL_IFACE_DBUS_NEW_HANDLERS_H + +struct network_handler_args { + struct wpa_supplicant *wpa_s; + struct wpa_ssid *ssid; +}; + +struct bss_handler_args { + struct wpa_supplicant *wpa_s; + unsigned int id; +}; + +DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message, + const int type, + const void *val); + +DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message, + const int type, void *val); + +DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message, + const int type, + const void *array, + size_t array_len); + +DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, + void *nothing); + +DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_remove_all_networks( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_state(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_bss_expire_age(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_setter_bss_expire_age(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_bss_expire_count(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_setter_bss_expire_count(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_country(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_setter_country(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_driver(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_current_auth_mode(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_networks(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message, + struct wpa_supplicant *bss); + +DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message, + struct bss_handler_args *bss); + +DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message, + struct network_handler_args *net); + +DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message, + struct network_handler_args *net); + +DBusMessage * wpas_dbus_getter_network_properties( + DBusMessage *message, struct network_handler_args *net); + +DBusMessage * wpas_dbus_setter_network_properties( + DBusMessage *message, struct network_handler_args *net); + +DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_process_credentials( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_setter_process_credentials( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_getter_credentials(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, + const char *arg); + +#endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c new file mode 100644 index 0000000..c118d73 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -0,0 +1,332 @@ +/* + * WPA Supplicant / dbus-based control interface (WPS) + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.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. + */ + +#include "includes.h" + +#include "common.h" +#include "../config.h" +#include "../wpa_supplicant_i.h" +#include "../wps_supplicant.h" +#include "dbus_new_helpers.h" +#include "dbus_new.h" +#include "dbus_new_handlers.h" +#include "dbus_dict_helpers.h" + + +struct wps_start_params { + int role; /* 0 - not set, 1 - enrollee, 2 - registrar */ + int type; /* 0 - not set, 1 - pin, 2 - pbc */ + u8 *bssid; + char *pin; +}; + + +static int wpas_dbus_handler_wps_role(DBusMessage *message, + DBusMessageIter *entry_iter, + struct wps_start_params *params, + DBusMessage **reply) +{ + DBusMessageIter variant_iter; + char *val; + + dbus_message_iter_recurse(entry_iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != + DBUS_TYPE_STRING) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Role type, " + "string required"); + *reply = wpas_dbus_error_invalid_args(message, + "Role must be a string"); + return -1; + } + dbus_message_iter_get_basic(&variant_iter, &val); + if (os_strcmp(val, "enrollee") == 0) + params->role = 1; + else if (os_strcmp(val, "registrar") == 0) + params->role = 2; + else { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Uknown role %s", val); + *reply = wpas_dbus_error_invalid_args(message, val); + return -1; + } + return 0; +} + + +static int wpas_dbus_handler_wps_type(DBusMessage *message, + DBusMessageIter *entry_iter, + struct wps_start_params *params, + DBusMessage **reply) +{ + DBusMessageIter variant_iter; + char *val; + + dbus_message_iter_recurse(entry_iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != + DBUS_TYPE_STRING) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Type type, " + "string required"); + *reply = wpas_dbus_error_invalid_args(message, + "Type must be a string"); + return -1; + } + dbus_message_iter_get_basic(&variant_iter, &val); + if (os_strcmp(val, "pin") == 0) + params->type = 1; + else if (os_strcmp(val, "pbc") == 0) + params->type = 2; + else { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown type %s", + val); + *reply = wpas_dbus_error_invalid_args(message, val); + return -1; + } + return 0; +} + + +static int wpas_dbus_handler_wps_bssid(DBusMessage *message, + DBusMessageIter *entry_iter, + struct wps_start_params *params, + DBusMessage **reply) +{ + DBusMessageIter variant_iter, array_iter; + int len; + + dbus_message_iter_recurse(entry_iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&variant_iter) != + DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, " + "byte array required"); + *reply = wpas_dbus_error_invalid_args( + message, "Bssid must be a byte array"); + return -1; + } + dbus_message_iter_recurse(&variant_iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, ¶ms->bssid, &len); + if (len != ETH_ALEN) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length " + "%d", len); + *reply = wpas_dbus_error_invalid_args(message, + "Bssid is wrong length"); + return -1; + } + return 0; +} + + +static int wpas_dbus_handler_wps_pin(DBusMessage *message, + DBusMessageIter *entry_iter, + struct wps_start_params *params, + DBusMessage **reply) +{ + DBusMessageIter variant_iter; + + dbus_message_iter_recurse(entry_iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != + DBUS_TYPE_STRING) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Pin type, " + "string required"); + *reply = wpas_dbus_error_invalid_args(message, + "Pin must be a string"); + return -1; + } + dbus_message_iter_get_basic(&variant_iter, ¶ms->pin); + return 0; +} + + +static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key, + DBusMessageIter *entry_iter, + struct wps_start_params *params, + DBusMessage **reply) +{ + if (os_strcmp(key, "Role") == 0) + return wpas_dbus_handler_wps_role(message, entry_iter, + params, reply); + else if (os_strcmp(key, "Type") == 0) + return wpas_dbus_handler_wps_type(message, entry_iter, + params, reply); + else if (os_strcmp(key, "Bssid") == 0) + return wpas_dbus_handler_wps_bssid(message, entry_iter, + params, reply); + else if (os_strcmp(key, "Pin") == 0) + return wpas_dbus_handler_wps_pin(message, entry_iter, + params, reply); + + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - unknown key %s", key); + *reply = wpas_dbus_error_invalid_args(message, key); + return -1; +} + + +/** + * wpas_dbus_handler_wps_start - Start WPS configuration + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: DBus message dictionary on success or DBus error on failure + * + * Handler for "Start" method call. DBus dictionary argument contains + * information about role (enrollee or registrar), authorization method + * (pin or push button) and optionally pin and bssid. Returned message + * has a dictionary argument which may contain newly generated pin (optional). + */ +DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter, dict_iter, entry_iter; + struct wps_start_params params; + char *key; + char npin[9] = { '\0' }; + int ret; + + os_memset(¶ms, 0, sizeof(params)); + dbus_message_iter_init(message, &iter); + + dbus_message_iter_recurse(&iter, &dict_iter); + while (dbus_message_iter_get_arg_type(&dict_iter) == + DBUS_TYPE_DICT_ENTRY) { + dbus_message_iter_recurse(&dict_iter, &entry_iter); + + dbus_message_iter_get_basic(&entry_iter, &key); + dbus_message_iter_next(&entry_iter); + + if (wpas_dbus_handler_wps_start_entry(message, key, + &entry_iter, + ¶ms, &reply)) + return reply; + + dbus_message_iter_next(&dict_iter); + } + + if (params.role == 0) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Role not specified"); + return wpas_dbus_error_invalid_args(message, + "Role not specified"); + } else if (params.role == 1 && params.type == 0) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Type not specified"); + return wpas_dbus_error_invalid_args(message, + "Type not specified"); + } else if (params.role == 2 && params.pin == NULL) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Pin required for " + "registrar role"); + return wpas_dbus_error_invalid_args( + message, "Pin required for registrar role."); + } + + if (params.role == 2) + ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin, + NULL); + else if (params.type == 1) { + ret = wpas_wps_start_pin(wpa_s, params.bssid, params.pin, 0, + DEV_PW_DEFAULT); + if (ret > 0) + os_snprintf(npin, sizeof(npin), "%08d", ret); + } else + ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0); + + if (ret < 0) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in " + "role %s and key %s", + (params.role == 1 ? "enrollee" : "registrar"), + (params.type == 0 ? "" : + (params.type == 1 ? "pin" : "pbc"))); + return wpas_dbus_error_unknown_error(message, + "WPS start failed"); + } + + reply = dbus_message_new_method_return(message); + if (!reply) { + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + dbus_message_iter_init_append(reply, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) { + dbus_message_unref(reply); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + if (os_strlen(npin) > 0) { + if (!wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) { + dbus_message_unref(reply); + return dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, + NULL); + } + } + + if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) { + dbus_message_unref(reply); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + } + + return reply; +} + + +/** + * wpas_dbus_getter_process_credentials - Check if credentials are processed + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: DBus message with a boolean on success or DBus error on failure + * + * Getter for "ProcessCredentials" property. Returns returned boolean will be + * true if wps_cred_processing configuration field is not equal to 1 or false + * if otherwise. + */ +DBusMessage * wpas_dbus_getter_process_credentials( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1); + return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, + &process); +} + + +/** + * wpas_dbus_setter_process_credentials - Set credentials_processed conf param + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: NULL on success or DBus error on failure + * + * Setter for "ProcessCredentials" property. Sets credentials_processed on 2 + * if boolean argument is true or on 1 if otherwise. + */ +DBusMessage * wpas_dbus_setter_process_credentials( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + dbus_bool_t process_credentials, old_pc; + + reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN, + &process_credentials); + if (reply) + return reply; + + old_pc = (wpa_s->conf->wps_cred_processing != 1); + wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1); + + if ((wpa_s->conf->wps_cred_processing != 1) != old_pc) + wpa_dbus_mark_property_changed(wpa_s->global->dbus, + wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_WPS, + "ProcessCredentials"); + + return NULL; +} diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c new file mode 100644 index 0000000..6e6e842 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new_helpers.c @@ -0,0 +1,886 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "dbus_common.h" +#include "dbus_common_i.h" +#include "dbus_new.h" +#include "dbus_new_helpers.h" + + +/** + * recursive_iter_copy - Reads arguments from one iterator and + * writes to another recursively + * @from: iterator to read from + * @to: iterator to write to + * + * Copies one iterator's elements to another. If any element in + * iterator is of container type, its content is copied recursively + */ +static void recursive_iter_copy(DBusMessageIter *from, DBusMessageIter *to) +{ + + char *subtype = NULL; + int type; + + /* iterate over iterator to copy */ + while ((type = dbus_message_iter_get_arg_type(from)) != + DBUS_TYPE_INVALID) { + + /* simply copy basic type entries */ + if (dbus_type_is_basic(type)) { + if (dbus_type_is_fixed(type)) { + /* + * According to DBus documentation all + * fixed-length types are guaranteed to fit + * 8 bytes + */ + dbus_uint64_t v; + dbus_message_iter_get_basic(from, &v); + dbus_message_iter_append_basic(to, type, &v); + } else { + char *v; + dbus_message_iter_get_basic(from, &v); + dbus_message_iter_append_basic(to, type, &v); + } + } else { + /* recursively copy container type entries */ + DBusMessageIter write_subiter, read_subiter; + + dbus_message_iter_recurse(from, &read_subiter); + + if (type == DBUS_TYPE_VARIANT || + type == DBUS_TYPE_ARRAY) { + subtype = dbus_message_iter_get_signature( + &read_subiter); + } + + dbus_message_iter_open_container(to, type, subtype, + &write_subiter); + + recursive_iter_copy(&read_subiter, &write_subiter); + + dbus_message_iter_close_container(to, &write_subiter); + if (subtype) + dbus_free(subtype); + } + + dbus_message_iter_next(from); + } +} + + +static unsigned int fill_dict_with_properties( + DBusMessageIter *dict_iter, const struct wpa_dbus_property_desc *props, + const char *interface, const void *user_data) +{ + DBusMessage *reply; + DBusMessageIter entry_iter, ret_iter; + unsigned int counter = 0; + const struct wpa_dbus_property_desc *dsc; + + for (dsc = props; dsc && dsc->dbus_property; dsc++) { + if (!os_strncmp(dsc->dbus_interface, interface, + WPAS_DBUS_INTERFACE_MAX) && + dsc->access != W && dsc->getter) { + reply = dsc->getter(NULL, user_data); + if (!reply) + continue; + + if (dbus_message_get_type(reply) == + DBUS_MESSAGE_TYPE_ERROR) { + dbus_message_unref(reply); + continue; + } + + dbus_message_iter_init(reply, &ret_iter); + + dbus_message_iter_open_container(dict_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &entry_iter); + dbus_message_iter_append_basic( + &entry_iter, DBUS_TYPE_STRING, + &dsc->dbus_property); + + recursive_iter_copy(&ret_iter, &entry_iter); + + dbus_message_iter_close_container(dict_iter, + &entry_iter); + dbus_message_unref(reply); + counter++; + } + } + + return counter; +} + + +/** + * get_all_properties - Responds for GetAll properties calls on object + * @message: Message with GetAll call + * @interface: interface name which properties will be returned + * @property_dsc: list of object's properties + * Returns: Message with dict of variants as argument with properties values + * + * Iterates over all properties registered with object and execute getters + * of those, which are readable and which interface matches interface + * specified as argument. Returned message contains one dict argument + * with properties names as keys and theirs values as values. + */ +static DBusMessage * get_all_properties( + DBusMessage *message, char *interface, + struct wpa_dbus_object_desc *obj_dsc) +{ + /* Create and initialize the return message */ + DBusMessage *reply = dbus_message_new_method_return(message); + DBusMessageIter iter, dict_iter; + int props_num; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &dict_iter); + + props_num = fill_dict_with_properties(&dict_iter, obj_dsc->properties, + interface, obj_dsc->user_data); + + dbus_message_iter_close_container(&iter, &dict_iter); + + if (props_num == 0) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, + DBUS_ERROR_INVALID_ARGS, + "No readable properties in " + "this interface"); + } + + return reply; +} + + +static int is_signature_correct(DBusMessage *message, + const struct wpa_dbus_method_desc *method_dsc) +{ + /* According to DBus documentation max length of signature is 255 */ +#define MAX_SIG_LEN 256 + char registered_sig[MAX_SIG_LEN], *pos; + const char *sig = dbus_message_get_signature(message); + int ret; + const struct wpa_dbus_argument *arg; + + pos = registered_sig; + *pos = '\0'; + + for (arg = method_dsc->args; arg && arg->name; arg++) { + if (arg->dir == ARG_IN) { + size_t blen = registered_sig + MAX_SIG_LEN - pos; + ret = os_snprintf(pos, blen, "%s", arg->type); + if (ret < 0 || (size_t) ret >= blen) + return 0; + pos += ret; + } + } + + return !os_strncmp(registered_sig, sig, MAX_SIG_LEN); +} + + +static DBusMessage * properties_get_all(DBusMessage *message, char *interface, + struct wpa_dbus_object_desc *obj_dsc) +{ + if (os_strcmp(dbus_message_get_signature(message), "s") != 0) + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + NULL); + + return get_all_properties(message, interface, obj_dsc); +} + + +static DBusMessage * properties_get(DBusMessage *message, + const struct wpa_dbus_property_desc *dsc, + void *user_data) +{ + if (os_strcmp(dbus_message_get_signature(message), "ss")) + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + NULL); + + if (dsc->access != W && dsc->getter) + return dsc->getter(message, user_data); + + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Property is write-only"); +} + + +static DBusMessage * properties_set(DBusMessage *message, + const struct wpa_dbus_property_desc *dsc, + void *user_data) +{ + if (os_strcmp(dbus_message_get_signature(message), "ssv")) + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + NULL); + + if (dsc->access != R && dsc->setter) + return dsc->setter(message, user_data); + + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Property is read-only"); +} + + +static DBusMessage * +properties_get_or_set(DBusMessage *message, DBusMessageIter *iter, + char *interface, + struct wpa_dbus_object_desc *obj_dsc) +{ + const struct wpa_dbus_property_desc *property_dsc; + char *property; + const char *method; + + method = dbus_message_get_member(message); + property_dsc = obj_dsc->properties; + + /* Second argument: property name (DBUS_TYPE_STRING) */ + if (!dbus_message_iter_next(iter) || + dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + NULL); + } + dbus_message_iter_get_basic(iter, &property); + + while (property_dsc && property_dsc->dbus_property) { + /* compare property names and + * interfaces */ + if (!os_strncmp(property_dsc->dbus_property, property, + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && + !os_strncmp(property_dsc->dbus_interface, interface, + WPAS_DBUS_INTERFACE_MAX)) + break; + + property_dsc++; + } + if (property_dsc == NULL || property_dsc->dbus_property == NULL) { + wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s", + interface, property, + dbus_message_get_path(message)); + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "No such property"); + } + + if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method, + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) + return properties_get(message, property_dsc, + obj_dsc->user_data); + + return properties_set(message, property_dsc, obj_dsc->user_data); +} + + +static DBusMessage * properties_handler(DBusMessage *message, + struct wpa_dbus_object_desc *obj_dsc) +{ + DBusMessageIter iter; + char *interface; + const char *method; + + method = dbus_message_get_member(message); + dbus_message_iter_init(message, &iter); + + if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method, + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) || + !os_strncmp(WPA_DBUS_PROPERTIES_SET, method, + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) || + !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method, + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) { + /* First argument: interface name (DBUS_TYPE_STRING) */ + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + { + return dbus_message_new_error(message, + DBUS_ERROR_INVALID_ARGS, + NULL); + } + + dbus_message_iter_get_basic(&iter, &interface); + + if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method, + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) { + /* GetAll */ + return properties_get_all(message, interface, obj_dsc); + } + /* Get or Set */ + return properties_get_or_set(message, &iter, interface, + obj_dsc); + } + return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD, + NULL); +} + + +static DBusMessage * msg_method_handler(DBusMessage *message, + struct wpa_dbus_object_desc *obj_dsc) +{ + const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods; + const char *method; + const char *msg_interface; + + method = dbus_message_get_member(message); + msg_interface = dbus_message_get_interface(message); + + /* try match call to any registered method */ + while (method_dsc && method_dsc->dbus_method) { + /* compare method names and interfaces */ + if (!os_strncmp(method_dsc->dbus_method, method, + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && + !os_strncmp(method_dsc->dbus_interface, msg_interface, + WPAS_DBUS_INTERFACE_MAX)) + break; + + method_dsc++; + } + if (method_dsc == NULL || method_dsc->dbus_method == NULL) { + wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s", + msg_interface, method, + dbus_message_get_path(message)); + return dbus_message_new_error(message, + DBUS_ERROR_UNKNOWN_METHOD, NULL); + } + + if (!is_signature_correct(message, method_dsc)) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + NULL); + } + + return method_dsc->method_handler(message, + obj_dsc->user_data); +} + + +/** + * message_handler - Handles incoming DBus messages + * @connection: DBus connection on which message was received + * @message: Received message + * @user_data: pointer to description of object to which message was sent + * Returns: Returns information whether message was handled or not + * + * Reads message interface and method name, then checks if they matches one + * of the special cases i.e. introspection call or properties get/getall/set + * methods and handles it. Else it iterates over registered methods list + * and tries to match method's name and interface to those read from message + * If appropriate method was found its handler function is called and + * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message + * will be sent. + */ +static DBusHandlerResult message_handler(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct wpa_dbus_object_desc *obj_dsc = user_data; + const char *method; + const char *path; + const char *msg_interface; + DBusMessage *reply; + + /* get method, interface and path the message is addressed to */ + method = dbus_message_get_member(message); + path = dbus_message_get_path(message); + msg_interface = dbus_message_get_interface(message); + if (!method || !path || !msg_interface) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)", + msg_interface, method, path); + + /* if message is introspection method call */ + if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method, + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && + !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface, + WPAS_DBUS_INTERFACE_MAX)) { +#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO + reply = wpa_dbus_introspect(message, obj_dsc); +#else /* CONFIG_CTRL_IFACE_DBUS_INTRO */ + reply = dbus_message_new_error( + message, DBUS_ERROR_UNKNOWN_METHOD, + "wpa_supplicant was compiled without " + "introspection support."); +#endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */ + } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface, + WPAS_DBUS_INTERFACE_MAX)) { + /* if message is properties method call */ + reply = properties_handler(message, obj_dsc); + } else { + reply = msg_method_handler(message, obj_dsc); + } + + /* If handler succeed returning NULL, reply empty message */ + if (!reply) + reply = dbus_message_new_method_return(message); + if (reply) { + if (!dbus_message_get_no_reply(message)) + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + + wpa_dbus_flush_all_changed_properties(connection); + + return DBUS_HANDLER_RESULT_HANDLED; +} + + +/** + * free_dbus_object_desc - Frees object description data structure + * @connection: DBus connection + * @obj_dsc: Object description to free + * + * Frees each of properties, methods and signals description lists and + * the object description structure itself. + */ +void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc) +{ + if (!obj_dsc) + return; + + /* free handler's argument */ + if (obj_dsc->user_data_free_func) + obj_dsc->user_data_free_func(obj_dsc->user_data); + + os_free(obj_dsc->path); + os_free(obj_dsc->prop_changed_flags); + os_free(obj_dsc); +} + + +static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc) +{ + free_dbus_object_desc(obj_dsc); +} + +/** + * wpa_dbus_ctrl_iface_init - Initialize dbus control interface + * @application_data: Pointer to application specific data structure + * @dbus_path: DBus path to interface object + * @dbus_service: DBus service name to register with + * @messageHandler: a pointer to function which will handle dbus messages + * coming on interface + * Returns: 0 on success, -1 on failure + * + * Initialize the dbus control interface and start receiving commands from + * external programs over the bus. + */ +int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, + char *dbus_path, char *dbus_service, + struct wpa_dbus_object_desc *obj_desc) +{ + DBusError error; + int ret = -1; + DBusObjectPathVTable wpa_vtable = { + &free_dbus_object_desc_cb, &message_handler, + NULL, NULL, NULL, NULL + }; + + obj_desc->connection = iface->con; + obj_desc->path = os_strdup(dbus_path); + + /* Register the message handler for the global dbus interface */ + if (!dbus_connection_register_object_path(iface->con, + dbus_path, &wpa_vtable, + obj_desc)) { + wpa_printf(MSG_ERROR, "dbus: Could not set up message " + "handler"); + return -1; + } + + /* Register our service with the message bus */ + dbus_error_init(&error); + switch (dbus_bus_request_name(iface->con, dbus_service, + 0, &error)) { + case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: + ret = 0; + break; + case DBUS_REQUEST_NAME_REPLY_EXISTS: + case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: + case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: + wpa_printf(MSG_ERROR, "dbus: Could not request service name: " + "already registered"); + break; + default: + wpa_printf(MSG_ERROR, "dbus: Could not request service name: " + "%s %s", error.name, error.message); + break; + } + dbus_error_free(&error); + + if (ret != 0) + return -1; + + wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service); + + return 0; +} + + +/** + * wpa_dbus_register_object_per_iface - Register a new object with dbus + * @ctrl_iface: pointer to dbus private data + * @path: DBus path to object + * @ifname: interface name + * @obj_desc: description of object's methods, signals and properties + * Returns: 0 on success, -1 on error + * + * Registers a new interface with dbus and assigns it a dbus object path. + */ +int wpa_dbus_register_object_per_iface( + struct wpas_dbus_priv *ctrl_iface, + const char *path, const char *ifname, + struct wpa_dbus_object_desc *obj_desc) +{ + DBusConnection *con; + DBusError error; + + DBusObjectPathVTable vtable = { + &free_dbus_object_desc_cb, &message_handler, + NULL, NULL, NULL, NULL + }; + + /* Do nothing if the control interface is not turned on */ + if (ctrl_iface == NULL) + return 0; + + con = ctrl_iface->con; + obj_desc->connection = con; + obj_desc->path = os_strdup(path); + + dbus_error_init(&error); + /* Register the message handler for the interface functions */ + if (!dbus_connection_try_register_object_path(con, path, &vtable, + obj_desc, &error)) { + if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) { + wpa_printf(MSG_DEBUG, "dbus: %s", error.message); + } else { + wpa_printf(MSG_ERROR, "dbus: Could not set up message " + "handler for interface %s object %s", + ifname, path); + wpa_printf(MSG_ERROR, "dbus error: %s", error.name); + wpa_printf(MSG_ERROR, "dbus: %s", error.message); + } + dbus_error_free(&error); + return -1; + } + + dbus_error_free(&error); + return 0; +} + + +static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx); + + +/** + * wpa_dbus_unregister_object_per_iface - Unregisters DBus object + * @ctrl_iface: Pointer to dbus private data + * @path: DBus path to object which will be unregistered + * Returns: Zero on success and -1 on failure + * + * Unregisters DBus object given by its path + */ +int wpa_dbus_unregister_object_per_iface( + struct wpas_dbus_priv *ctrl_iface, const char *path) +{ + DBusConnection *con = ctrl_iface->con; + struct wpa_dbus_object_desc *obj_desc = NULL; + + dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's " + "private data: %s", __func__, path); + } else { + eloop_cancel_timeout(flush_object_timeout_handler, con, + obj_desc); + } + + if (!dbus_connection_unregister_object_path(con, path)) + return -1; + + return 0; +} + + +static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc, + const char *interface, + DBusMessageIter *dict_iter) +{ + DBusMessage *getter_reply; + DBusMessageIter prop_iter, entry_iter; + const struct wpa_dbus_property_desc *dsc; + int i; + + for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property; + dsc++, i++) { + if (obj_dsc->prop_changed_flags == NULL || + !obj_dsc->prop_changed_flags[i]) + continue; + if (os_strcmp(dsc->dbus_interface, interface) != 0) + continue; + obj_dsc->prop_changed_flags[i] = 0; + + getter_reply = dsc->getter(NULL, obj_dsc->user_data); + if (!getter_reply || + dbus_message_get_type(getter_reply) == + DBUS_MESSAGE_TYPE_ERROR) { + wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value " + "of property %s", __func__, + dsc->dbus_property); + continue; + } + + if (!dbus_message_iter_init(getter_reply, &prop_iter) || + !dbus_message_iter_open_container(dict_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &entry_iter) || + !dbus_message_iter_append_basic(&entry_iter, + DBUS_TYPE_STRING, + &dsc->dbus_property)) + goto err; + + recursive_iter_copy(&prop_iter, &entry_iter); + + if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) + goto err; + + dbus_message_unref(getter_reply); + } + + return; + +err: + wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__); +} + + +static void send_prop_changed_signal( + DBusConnection *con, const char *path, const char *interface, + const struct wpa_dbus_object_desc *obj_dsc) +{ + DBusMessage *msg; + DBusMessageIter signal_iter, dict_iter; + + msg = dbus_message_new_signal(path, interface, "PropertiesChanged"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &signal_iter); + + if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, + "{sv}", &dict_iter)) + goto err; + + put_changed_properties(obj_dsc, interface, &dict_iter); + + if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) + goto err; + + dbus_connection_send(con, msg, NULL); + +out: + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", + __func__); + goto out; +} + + +static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) +{ + DBusConnection *con = eloop_ctx; + struct wpa_dbus_object_desc *obj_desc = timeout_ctx; + + wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties " + "of object %s", __func__, obj_desc->path); + wpa_dbus_flush_object_changed_properties(con, obj_desc->path); +} + + +static void recursive_flush_changed_properties(DBusConnection *con, + const char *path) +{ + char **objects = NULL; + char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + int i; + + wpa_dbus_flush_object_changed_properties(con, path); + + if (!dbus_connection_list_registered(con, path, &objects)) + goto out; + + for (i = 0; objects[i]; i++) { + os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/%s", path, objects[i]); + recursive_flush_changed_properties(con, subobj_path); + } + +out: + dbus_free_string_array(objects); +} + + +/** + * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals + * @con: DBus connection + * + * Traverses through all registered objects and sends PropertiesChanged for + * each properties. + */ +void wpa_dbus_flush_all_changed_properties(DBusConnection *con) +{ + recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH); +} + + +/** + * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object + * @con: DBus connection + * @path: path to a DBus object for which PropertiesChanged will be sent. + * + * Iterates over all properties registered with object and for each interface + * containing properties marked as changed, sends a PropertiesChanged signal + * containing names and new values of properties that have changed. + * + * You need to call this function after wpa_dbus_mark_property_changed() + * if you want to send PropertiesChanged signal immediately (i.e., without + * waiting timeout to expire). PropertiesChanged signal for an object is sent + * automatically short time after first marking property as changed. All + * PropertiesChanged signals are sent automatically after responding on DBus + * message, so if you marked a property changed as a result of DBus call + * (e.g., param setter), you usually do not need to call this function. + */ +void wpa_dbus_flush_object_changed_properties(DBusConnection *con, + const char *path) +{ + struct wpa_dbus_object_desc *obj_desc = NULL; + const struct wpa_dbus_property_desc *dsc; + int i; + + dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); + if (!obj_desc) + 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 || + !obj_desc->prop_changed_flags[i]) + continue; + send_prop_changed_signal(con, path, dsc->dbus_interface, + obj_desc); + } +} + + +#define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000 + + +/** + * wpa_dbus_mark_property_changed - Mark a property as changed and + * @iface: dbus priv struct + * @path: path to DBus object which property has changed + * @interface: interface containing changed property + * @property: property name which has changed + * + * Iterates over all properties registered with an object and marks the one + * given in parameters as changed. All parameters registered for an object + * within a single interface will be aggregated together and sent in one + * PropertiesChanged signal when function + * wpa_dbus_flush_object_changed_properties() is called. + */ +void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, + const char *path, const char *interface, + const char *property) +{ + struct wpa_dbus_object_desc *obj_desc = NULL; + const struct wpa_dbus_property_desc *dsc; + int i = 0; + + if (iface == NULL) + return; + + dbus_connection_get_object_path_data(iface->con, path, + (void **) &obj_desc); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " + "could not obtain object's private data: %s", path); + return; + } + + for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++) + if (os_strcmp(property, dsc->dbus_property) == 0 && + os_strcmp(interface, dsc->dbus_interface) == 0) { + if (obj_desc->prop_changed_flags) + obj_desc->prop_changed_flags[i] = 1; + break; + } + + if (!dsc || !dsc->dbus_property) { + wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " + "no property %s in object %s", property, path); + return; + } + + if (!eloop_is_timeout_registered(flush_object_timeout_handler, + iface->con, obj_desc->path)) { + eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT, + flush_object_timeout_handler, + iface->con, obj_desc); + } +} + + +/** + * wpa_dbus_get_object_properties - Put object's properties into dictionary + * @iface: dbus priv struct + * @path: path to DBus object which properties will be obtained + * @interface: interface name which properties will be obtained + * @dict_iter: correct, open DBus dictionary iterator. + * + * Iterates over all properties registered with object and execute getters + * of those, which are readable and which interface matches interface + * specified as argument. Obtained properties values are stored in + * dict_iter dictionary. + */ +void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, + const char *path, const char *interface, + DBusMessageIter *dict_iter) +{ + struct wpa_dbus_object_desc *obj_desc = NULL; + + dbus_connection_get_object_path_data(iface->con, path, + (void **) &obj_desc); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "dbus: wpa_dbus_get_object_properties: " + "could not obtain object's private data: %s", path); + return; + } + + fill_dict_with_properties(dict_iter, obj_desc->properties, + interface, obj_desc->user_data); +} diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h new file mode 100644 index 0000000..7038b63 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new_helpers.h @@ -0,0 +1,148 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.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 WPA_DBUS_CTRL_H +#define WPA_DBUS_CTRL_H + +#include <dbus/dbus.h> + +typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message, + void *user_data); +typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg); + +typedef DBusMessage * (* WPADBusPropertyAccessor)(DBusMessage *message, + const void *user_data); + +struct wpa_dbus_object_desc { + DBusConnection *connection; + char *path; + + /* list of methods, properties and signals registered with object */ + const struct wpa_dbus_method_desc *methods; + const struct wpa_dbus_signal_desc *signals; + const struct wpa_dbus_property_desc *properties; + + /* property changed flags */ + u8 *prop_changed_flags; + + /* argument for method handlers and properties + * getter and setter functions */ + void *user_data; + /* function used to free above argument */ + WPADBusArgumentFreeFunction user_data_free_func; +}; + +enum dbus_prop_access { R, W, RW }; + +enum dbus_arg_direction { ARG_IN, ARG_OUT }; + +struct wpa_dbus_argument { + char *name; + char *type; + enum dbus_arg_direction dir; +}; + +#define END_ARGS { NULL, NULL, ARG_IN } + +/** + * struct wpa_dbus_method_desc - DBus method description + */ +struct wpa_dbus_method_desc { + /* method name */ + const char *dbus_method; + /* method interface */ + const char *dbus_interface; + /* method handling function */ + WPADBusMethodHandler method_handler; + /* array of arguments */ + struct wpa_dbus_argument args[3]; +}; + +/** + * struct wpa_dbus_signal_desc - DBus signal description + */ +struct wpa_dbus_signal_desc { + /* signal name */ + const char *dbus_signal; + /* signal interface */ + const char *dbus_interface; + /* array of arguments */ + struct wpa_dbus_argument args[3]; +}; + +/** + * struct wpa_dbus_property_desc - DBus property description + */ +struct wpa_dbus_property_desc { + /* property name */ + const char *dbus_property; + /* property interface */ + const char *dbus_interface; + /* property type signature in DBus type notation */ + const char *type; + /* property getter function */ + WPADBusPropertyAccessor getter; + /* property setter function */ + WPADBusPropertyAccessor setter; + /* property access permissions */ + enum dbus_prop_access access; +}; + + +#define WPAS_DBUS_OBJECT_PATH_MAX 150 +#define WPAS_DBUS_INTERFACE_MAX 150 +#define WPAS_DBUS_METHOD_SIGNAL_PROP_MAX 50 +#define WPAS_DBUS_AUTH_MODE_MAX 64 + +#define WPA_DBUS_INTROSPECTION_INTERFACE "org.freedesktop.DBus.Introspectable" +#define WPA_DBUS_INTROSPECTION_METHOD "Introspect" +#define WPA_DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" +#define WPA_DBUS_PROPERTIES_GET "Get" +#define WPA_DBUS_PROPERTIES_SET "Set" +#define WPA_DBUS_PROPERTIES_GETALL "GetAll" + +void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc); + +int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, char *dbus_path, + char *dbus_service, + struct wpa_dbus_object_desc *obj_desc); + +int wpa_dbus_register_object_per_iface( + struct wpas_dbus_priv *ctrl_iface, + const char *path, const char *ifname, + struct wpa_dbus_object_desc *obj_desc); + +int wpa_dbus_unregister_object_per_iface( + struct wpas_dbus_priv *ctrl_iface, + const char *path); + +void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, + const char *path, const char *interface, + DBusMessageIter *dict_iter); + + +void wpa_dbus_flush_all_changed_properties(DBusConnection *con); + +void wpa_dbus_flush_object_changed_properties(DBusConnection *con, + const char *path); + +void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, + const char *path, const char *interface, + const char *property); + +DBusMessage * wpa_dbus_introspect(DBusMessage *message, + struct wpa_dbus_object_desc *obj_dsc); + +#endif /* WPA_DBUS_CTRL_H */ diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c new file mode 100644 index 0000000..fd433df --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new_introspect.c @@ -0,0 +1,278 @@ +/* + * wpa_supplicant - D-Bus introspection + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> + * Copyright (c) 2010, 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/list.h" +#include "utils/wpabuf.h" +#include "dbus_common_i.h" +#include "dbus_new_helpers.h" + + +struct interfaces { + struct dl_list list; + char *dbus_interface; + struct wpabuf *xml; +}; + + +static struct interfaces * add_interface(struct dl_list *list, + const char *dbus_interface) +{ + struct interfaces *iface; + + dl_list_for_each(iface, list, struct interfaces, list) { + if (os_strcmp(iface->dbus_interface, dbus_interface) == 0) + return iface; /* already in the list */ + } + + iface = os_zalloc(sizeof(struct interfaces)); + if (!iface) + return NULL; + iface->xml = wpabuf_alloc(6000); + if (iface->xml == NULL) { + os_free(iface); + return NULL; + } + wpabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface); + dl_list_add_tail(list, &iface->list); + iface->dbus_interface = os_strdup(dbus_interface); + return iface; +} + + +static void add_arg(struct wpabuf *xml, const char *name, const char *type, + const char *direction) +{ + wpabuf_printf(xml, "<arg name=\"%s\"", name); + if (type) + wpabuf_printf(xml, " type=\"%s\"", type); + if (direction) + wpabuf_printf(xml, " direction=\"%s\"", direction); + wpabuf_put_str(xml, "/>"); +} + + +static void add_entry(struct wpabuf *xml, const char *type, const char *name, + const struct wpa_dbus_argument *args, int include_dir) +{ + const struct wpa_dbus_argument *arg; + + if (args == NULL || args->name == NULL) { + wpabuf_printf(xml, "<%s name=\"%s\"/>", type, name); + return; + } + wpabuf_printf(xml, "<%s name=\"%s\">", type, name); + for (arg = args; arg && arg->name; arg++) { + add_arg(xml, arg->name, arg->type, + include_dir ? (arg->dir == ARG_IN ? "in" : "out") : + NULL); + } + wpabuf_printf(xml, "</%s>", type); +} + + +static void add_property(struct wpabuf *xml, + const struct wpa_dbus_property_desc *dsc) +{ + wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" access=\"%s\"/>", + dsc->dbus_property, dsc->type, + (dsc->access == R ? "read" : + (dsc->access == W ? "write" : "readwrite"))); +} + + +static void extract_interfaces_methods( + struct dl_list *list, const struct wpa_dbus_method_desc *methods) +{ + const struct wpa_dbus_method_desc *dsc; + struct interfaces *iface; + for (dsc = methods; dsc && dsc->dbus_method; dsc++) { + iface = add_interface(list, dsc->dbus_interface); + if (iface) + add_entry(iface->xml, "method", dsc->dbus_method, + dsc->args, 1); + } +} + + +static void extract_interfaces_signals( + struct dl_list *list, const struct wpa_dbus_signal_desc *signals) +{ + const struct wpa_dbus_signal_desc *dsc; + struct interfaces *iface; + for (dsc = signals; dsc && dsc->dbus_signal; dsc++) { + iface = add_interface(list, dsc->dbus_interface); + if (iface) + add_entry(iface->xml, "signal", dsc->dbus_signal, + dsc->args, 0); + } +} + + +static void extract_interfaces_properties( + struct dl_list *list, const struct wpa_dbus_property_desc *properties) +{ + const struct wpa_dbus_property_desc *dsc; + struct interfaces *iface; + for (dsc = properties; dsc && dsc->dbus_property; dsc++) { + iface = add_interface(list, dsc->dbus_interface); + if (iface) + add_property(iface->xml, dsc); + } +} + + +/** + * extract_interfaces - Extract interfaces from methods, signals and props + * @list: Interface list to be filled + * @obj_dsc: Description of object from which interfaces will be extracted + * + * Iterates over all methods, signals, and properties registered with an + * object and collects all declared DBus interfaces and create interfaces' + * node in XML root node for each. Returned list elements contain interface + * name and XML node of corresponding interface. + */ +static void extract_interfaces(struct dl_list *list, + struct wpa_dbus_object_desc *obj_dsc) +{ + extract_interfaces_methods(list, obj_dsc->methods); + extract_interfaces_signals(list, obj_dsc->signals); + extract_interfaces_properties(list, obj_dsc->properties); +} + + +static void add_interfaces(struct dl_list *list, struct wpabuf *xml) +{ + struct interfaces *iface, *n; + dl_list_for_each_safe(iface, n, list, struct interfaces, list) { + if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) { + wpabuf_put_buf(xml, iface->xml); + wpabuf_put_str(xml, "</interface>"); + } + dl_list_del(&iface->list); + wpabuf_free(iface->xml); + os_free(iface->dbus_interface); + os_free(iface); + } +} + + +static void add_child_nodes(struct wpabuf *xml, DBusConnection *con, + const char *path) +{ + char **children; + int i; + + /* add child nodes to introspection tree */ + dbus_connection_list_registered(con, path, &children); + for (i = 0; children[i]; i++) + wpabuf_printf(xml, "<node name=\"%s\"/>", children[i]); + dbus_free_string_array(children); +} + + +static void add_introspectable_interface(struct wpabuf *xml) +{ + wpabuf_printf(xml, "<interface name=\"%s\">" + "<method name=\"%s\">" + "<arg name=\"data\" type=\"s\" direction=\"out\"/>" + "</method>" + "</interface>", + WPA_DBUS_INTROSPECTION_INTERFACE, + WPA_DBUS_INTROSPECTION_METHOD); +} + + +static void add_properties_interface(struct wpabuf *xml) +{ + wpabuf_printf(xml, "<interface name=\"%s\">", + WPA_DBUS_PROPERTIES_INTERFACE); + + wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GET); + add_arg(xml, "interface", "s", "in"); + add_arg(xml, "propname", "s", "in"); + add_arg(xml, "value", "v", "out"); + wpabuf_put_str(xml, "</method>"); + + wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GETALL); + add_arg(xml, "interface", "s", "in"); + add_arg(xml, "props", "a{sv}", "out"); + wpabuf_put_str(xml, "</method>"); + + wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_SET); + add_arg(xml, "interface", "s", "in"); + add_arg(xml, "propname", "s", "in"); + add_arg(xml, "value", "v", "in"); + wpabuf_put_str(xml, "</method>"); + + wpabuf_put_str(xml, "</interface>"); +} + + +static void add_wpas_interfaces(struct wpabuf *xml, + struct wpa_dbus_object_desc *obj_dsc) +{ + struct dl_list ifaces; + dl_list_init(&ifaces); + extract_interfaces(&ifaces, obj_dsc); + add_interfaces(&ifaces, xml); +} + + +/** + * wpa_dbus_introspect - Responds for Introspect calls on object + * @message: Message with Introspect call + * @obj_dsc: Object description on which Introspect was called + * Returns: Message with introspection result XML string as only argument + * + * Iterates over all methods, signals and properties registered with + * object and generates introspection data for the object as XML string. + */ +DBusMessage * wpa_dbus_introspect(DBusMessage *message, + struct wpa_dbus_object_desc *obj_dsc) +{ + + DBusMessage *reply; + struct wpabuf *xml; + + xml = wpabuf_alloc(8000); + if (xml == NULL) + return NULL; + + wpabuf_put_str(xml, "<?xml version=\"1.0\"?>\n"); + wpabuf_put_str(xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + wpabuf_put_str(xml, "<node>"); + + add_introspectable_interface(xml); + add_properties_interface(xml); + add_wpas_interfaces(xml, obj_dsc); + add_child_nodes(xml, obj_dsc->connection, + dbus_message_get_path(message)); + + wpabuf_put_str(xml, "</node>\n"); + + reply = dbus_message_new_method_return(message); + if (reply) { + const char *intro_str = wpabuf_head(xml); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str, + DBUS_TYPE_INVALID); + } + wpabuf_free(xml); + + return reply; +} diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c new file mode 100644 index 0000000..6a00f3e --- /dev/null +++ b/wpa_supplicant/dbus/dbus_old.c @@ -0,0 +1,697 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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. + */ + +#include "includes.h" +#include <dbus/dbus.h> + +#include "common.h" +#include "eloop.h" +#include "wps/wps.h" +#include "../config.h" +#include "../wpa_supplicant_i.h" +#include "../bss.h" +#include "dbus_old.h" +#include "dbus_old_handlers.h" +#include "dbus_common.h" +#include "dbus_common_i.h" + + +/** + * wpas_dbus_decompose_object_path - Decompose an interface object path into parts + * @path: The dbus object path + * @network: (out) the configured network this object path refers to, if any + * @bssid: (out) the scanned bssid this object path refers to, if any + * Returns: The object path of the network interface this path refers to + * + * For a given object path, decomposes the object path into object id, network, + * and BSSID parts, if those parts exist. + */ +char * wpas_dbus_decompose_object_path(const char *path, char **network, + char **bssid) +{ + const unsigned int dev_path_prefix_len = + strlen(WPAS_DBUS_PATH_INTERFACES "/"); + char *obj_path_only; + char *next_sep; + + /* Be a bit paranoid about path */ + if (!path || strncmp(path, WPAS_DBUS_PATH_INTERFACES "/", + dev_path_prefix_len)) + return NULL; + + /* Ensure there's something at the end of the path */ + if ((path + dev_path_prefix_len)[0] == '\0') + return NULL; + + obj_path_only = os_strdup(path); + if (obj_path_only == NULL) + return NULL; + + next_sep = strchr(obj_path_only + dev_path_prefix_len, '/'); + if (next_sep != NULL) { + const char *net_part = strstr(next_sep, + WPAS_DBUS_NETWORKS_PART "/"); + const char *bssid_part = strstr(next_sep, + WPAS_DBUS_BSSIDS_PART "/"); + + if (network && net_part) { + /* Deal with a request for a configured network */ + const char *net_name = net_part + + strlen(WPAS_DBUS_NETWORKS_PART "/"); + *network = NULL; + if (strlen(net_name)) + *network = os_strdup(net_name); + } else if (bssid && bssid_part) { + /* Deal with a request for a scanned BSSID */ + const char *bssid_name = bssid_part + + strlen(WPAS_DBUS_BSSIDS_PART "/"); + if (strlen(bssid_name)) + *bssid = os_strdup(bssid_name); + else + *bssid = NULL; + } + + /* Cut off interface object path before "/" */ + *next_sep = '\0'; + } + + return obj_path_only; +} + + +/** + * wpas_dbus_new_invalid_iface_error - Return a new invalid interface error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: A dbus error message + * + * Convenience function to create and return an invalid interface error + */ +DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_ERROR_INVALID_IFACE, + "wpa_supplicant knows nothing about " + "this interface."); +} + + +/** + * wpas_dbus_new_invalid_network_error - Return a new invalid network error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid network error + */ +DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_ERROR_INVALID_NETWORK, + "The requested network does not exist."); +} + + +/** + * wpas_dbus_new_invalid_bssid_error - Return a new invalid bssid error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid bssid error + */ +static DBusMessage * wpas_dbus_new_invalid_bssid_error(DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_ERROR_INVALID_BSSID, + "The BSSID requested was invalid."); +} + + +/** + * wpas_dispatch_network_method - dispatch messages for configured networks + * @message: the incoming dbus message + * @wpa_s: a network interface's data + * @network_id: id of the configured network we're interested in + * Returns: a reply dbus message, or a dbus error message + * + * This function dispatches all incoming dbus messages for configured networks. + */ +static DBusMessage * wpas_dispatch_network_method(DBusMessage *message, + struct wpa_supplicant *wpa_s, + int network_id) +{ + DBusMessage *reply = NULL; + const char *method = dbus_message_get_member(message); + struct wpa_ssid *ssid; + + ssid = wpa_config_get_network(wpa_s->conf, network_id); + if (ssid == NULL) + return wpas_dbus_new_invalid_network_error(message); + + if (!strcmp(method, "set")) + reply = wpas_dbus_iface_set_network(message, wpa_s, ssid); + else if (!strcmp(method, "enable")) + reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid); + else if (!strcmp(method, "disable")) + reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid); + + return reply; +} + + +/** + * wpas_dispatch_bssid_method - dispatch messages for scanned networks + * @message: the incoming dbus message + * @wpa_s: a network interface's data + * @bssid: bssid of the scanned network we're interested in + * Returns: a reply dbus message, or a dbus error message + * + * This function dispatches all incoming dbus messages for scanned networks. + */ +static DBusMessage * wpas_dispatch_bssid_method(DBusMessage *message, + struct wpa_supplicant *wpa_s, + const char *bssid_txt) +{ + u8 bssid[ETH_ALEN]; + struct wpa_bss *bss; + + if (hexstr2bin(bssid_txt, bssid, ETH_ALEN) < 0) + return wpas_dbus_new_invalid_bssid_error(message); + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (bss == NULL) + return wpas_dbus_new_invalid_bssid_error(message); + + /* Dispatch the method call against the scanned bssid */ + if (os_strcmp(dbus_message_get_member(message), "properties") == 0) + return wpas_dbus_bssid_properties(message, wpa_s, bss); + + return NULL; +} + + +/** + * wpas_iface_message_handler - Dispatch messages for interfaces or networks + * @connection: Connection to the system message bus + * @message: An incoming dbus message + * @user_data: A pointer to a dbus control interface data structure + * Returns: Whether or not the message was handled + * + * This function dispatches all incoming dbus messages for network interfaces, + * or objects owned by them, such as scanned BSSIDs and configured networks. + */ +static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *method = dbus_message_get_member(message); + const char *path = dbus_message_get_path(message); + const char *msg_interface = dbus_message_get_interface(message); + char *iface_obj_path = NULL; + char *network = NULL; + char *bssid = NULL; + DBusMessage *reply = NULL; + + /* Caller must specify a message interface */ + if (!msg_interface) + goto out; + + iface_obj_path = wpas_dbus_decompose_object_path(path, &network, + &bssid); + if (iface_obj_path == NULL) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + + /* Make sure the message's object path actually refers to the + * wpa_supplicant structure it's supposed to (which is wpa_s) + */ + if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global, + iface_obj_path) != wpa_s) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + + if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) { + /* A method for one of this interface's configured networks */ + int nid = strtoul(network, NULL, 10); + if (errno != EINVAL) + reply = wpas_dispatch_network_method(message, wpa_s, + nid); + else + reply = wpas_dbus_new_invalid_network_error(message); + } else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) { + /* A method for one of this interface's scanned BSSIDs */ + reply = wpas_dispatch_bssid_method(message, wpa_s, bssid); + } else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) { + /* A method for an interface only. */ + if (!strcmp(method, "scan")) + reply = wpas_dbus_iface_scan(message, wpa_s); + else if (!strcmp(method, "scanResults")) + reply = wpas_dbus_iface_scan_results(message, wpa_s); + else if (!strcmp(method, "addNetwork")) + reply = wpas_dbus_iface_add_network(message, wpa_s); + else if (!strcmp(method, "removeNetwork")) + reply = wpas_dbus_iface_remove_network(message, wpa_s); + else if (!strcmp(method, "selectNetwork")) + reply = wpas_dbus_iface_select_network(message, wpa_s); + else if (!strcmp(method, "capabilities")) + reply = wpas_dbus_iface_capabilities(message, wpa_s); + else if (!strcmp(method, "disconnect")) + reply = wpas_dbus_iface_disconnect(message, wpa_s); + else if (!strcmp(method, "setAPScan")) + reply = wpas_dbus_iface_set_ap_scan(message, wpa_s); + else if (!strcmp(method, "setSmartcardModules")) + reply = wpas_dbus_iface_set_smartcard_modules(message, + wpa_s); + else if (!strcmp(method, "state")) + reply = wpas_dbus_iface_get_state(message, wpa_s); + else if (!strcmp(method, "scanning")) + reply = wpas_dbus_iface_get_scanning(message, wpa_s); + else if (!strcmp(method, "setBlobs")) + reply = wpas_dbus_iface_set_blobs(message, wpa_s); + else if (!strcmp(method, "removeBlobs")) + reply = wpas_dbus_iface_remove_blobs(message, wpa_s); +#ifdef CONFIG_WPS + else if (!os_strcmp(method, "wpsPbc")) + reply = wpas_dbus_iface_wps_pbc(message, wpa_s); + else if (!os_strcmp(method, "wpsPin")) + reply = wpas_dbus_iface_wps_pin(message, wpa_s); + else if (!os_strcmp(method, "wpsReg")) + reply = wpas_dbus_iface_wps_reg(message, wpa_s); +#endif /* CONFIG_WPS */ + else if (!os_strcmp(method, "flush")) + reply = wpas_dbus_iface_flush(message, wpa_s); + } + + /* If the message was handled, send back the reply */ + if (reply) { + if (!dbus_message_get_no_reply(message)) + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + +out: + os_free(iface_obj_path); + os_free(network); + os_free(bssid); + return reply ? DBUS_HANDLER_RESULT_HANDLED : + DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +/** + * wpas_message_handler - dispatch incoming dbus messages + * @connection: connection to the system message bus + * @message: an incoming dbus message + * @user_data: a pointer to a dbus control interface data structure + * Returns: whether or not the message was handled + * + * This function dispatches all incoming dbus messages to the correct + * handlers, depending on what the message's target object path is, + * and what the method call is. + */ +static DBusHandlerResult wpas_message_handler(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct wpas_dbus_priv *ctrl_iface = user_data; + const char *method; + const char *path; + const char *msg_interface; + DBusMessage *reply = NULL; + + method = dbus_message_get_member(message); + path = dbus_message_get_path(message); + msg_interface = dbus_message_get_interface(message); + if (!method || !path || !ctrl_iface || !msg_interface) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + /* Validate the method interface */ + if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (!strcmp(path, WPAS_DBUS_PATH)) { + /* dispatch methods against our global dbus interface here */ + if (!strcmp(method, "addInterface")) { + reply = wpas_dbus_global_add_interface( + message, ctrl_iface->global); + } else if (!strcmp(method, "removeInterface")) { + reply = wpas_dbus_global_remove_interface( + message, ctrl_iface->global); + } else if (!strcmp(method, "getInterface")) { + reply = wpas_dbus_global_get_interface( + message, ctrl_iface->global); + } else if (!strcmp(method, "setDebugParams")) { + reply = wpas_dbus_global_set_debugparams( + message, ctrl_iface->global); + } + } + + /* If the message was handled, send back the reply */ + if (reply) { + if (!dbus_message_get_no_reply(message)) + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + + return reply ? DBUS_HANDLER_RESULT_HANDLED : + DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +/** + * wpa_supplicant_dbus_notify_scan_results - Send a scan results signal + * @wpa_s: %wpa_supplicant network interface data + * Returns: 0 on success, -1 on failure + * + * Notify listeners that this interface has updated scan results. + */ +void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *iface = wpa_s->global->dbus; + DBusMessage *_signal; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + _signal = dbus_message_new_signal(wpa_s->dbus_path, + WPAS_DBUS_IFACE_INTERFACE, + "ScanResultsAvailable"); + if (_signal == NULL) { + wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan " + "results signal"); + return; + } + dbus_connection_send(iface->con, _signal, NULL); + dbus_message_unref(_signal); +} + + +/** + * wpa_supplicant_dbus_notify_state_change - Send a state change signal + * @wpa_s: %wpa_supplicant network interface data + * @new_state: new state wpa_supplicant is entering + * @old_state: old state wpa_supplicant is leaving + * Returns: 0 on success, -1 on failure + * + * Notify listeners that wpa_supplicant has changed state + */ +void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, + enum wpa_states new_state, + enum wpa_states old_state) +{ + struct wpas_dbus_priv *iface; + DBusMessage *_signal = NULL; + const char *new_state_str, *old_state_str; + + if (wpa_s->dbus_path == NULL) + return; /* Skip signal since D-Bus setup is not yet ready */ + + /* Do nothing if the control interface is not turned on */ + if (wpa_s->global == NULL) + return; + iface = wpa_s->global->dbus; + if (iface == NULL) + return; + + /* Only send signal if state really changed */ + if (new_state == old_state) + return; + + _signal = dbus_message_new_signal(wpa_s->dbus_path, + WPAS_DBUS_IFACE_INTERFACE, + "StateChange"); + if (_signal == NULL) { + wpa_printf(MSG_ERROR, + "dbus: wpa_supplicant_dbus_notify_state_change: " + "could not create dbus signal; likely out of " + "memory"); + return; + } + + new_state_str = wpa_supplicant_state_txt(new_state); + old_state_str = wpa_supplicant_state_txt(old_state); + if (new_state_str == NULL || old_state_str == NULL) { + wpa_printf(MSG_ERROR, + "dbus: wpa_supplicant_dbus_notify_state_change: " + "Could not convert state strings"); + goto out; + } + + if (!dbus_message_append_args(_signal, + DBUS_TYPE_STRING, &new_state_str, + DBUS_TYPE_STRING, &old_state_str, + DBUS_TYPE_INVALID)) { + wpa_printf(MSG_ERROR, + "dbus: wpa_supplicant_dbus_notify_state_change: " + "Not enough memory to construct state change " + "signal"); + goto out; + } + + dbus_connection_send(iface->con, _signal, NULL); + +out: + dbus_message_unref(_signal); +} + + +/** + * wpa_supplicant_dbus_notify_scanning - send scanning status + * @wpa_s: %wpa_supplicant network interface data + * Returns: 0 on success, -1 on failure + * + * Notify listeners of interface scanning state changes + */ +void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *iface = wpa_s->global->dbus; + DBusMessage *_signal; + dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + _signal = dbus_message_new_signal(wpa_s->dbus_path, + WPAS_DBUS_IFACE_INTERFACE, + "Scanning"); + if (_signal == NULL) { + wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan " + "results signal"); + return; + } + + if (dbus_message_append_args(_signal, + DBUS_TYPE_BOOLEAN, &scanning, + DBUS_TYPE_INVALID)) { + dbus_connection_send(iface->con, _signal, NULL); + } else { + wpa_printf(MSG_ERROR, "dbus: Not enough memory to construct " + "signal"); + } + dbus_message_unref(_signal); +} + + +#ifdef CONFIG_WPS +void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ + struct wpas_dbus_priv *iface; + DBusMessage *_signal = NULL; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s->global == NULL) + return; + iface = wpa_s->global->dbus; + if (iface == NULL) + return; + + _signal = dbus_message_new_signal(wpa_s->dbus_path, + WPAS_DBUS_IFACE_INTERFACE, + "WpsCred"); + if (_signal == NULL) { + wpa_printf(MSG_ERROR, + "dbus: wpa_supplicant_dbus_notify_wps_cred: " + "Could not create dbus signal; likely out of " + "memory"); + return; + } + + if (!dbus_message_append_args(_signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &cred->cred_attr, cred->cred_attr_len, + DBUS_TYPE_INVALID)) { + wpa_printf(MSG_ERROR, + "dbus: wpa_supplicant_dbus_notify_wps_cred: " + "Not enough memory to construct signal"); + goto out; + } + + dbus_connection_send(iface->con, _signal, NULL); + +out: + dbus_message_unref(_signal); +} +#else /* CONFIG_WPS */ +void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ +} +#endif /* CONFIG_WPS */ + + +/** + * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: 0 on success, -1 on failure + * + * Initialize the dbus control interface and start receiving commands from + * external programs over the bus. + */ +int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface) +{ + DBusError error; + int ret = -1; + DBusObjectPathVTable wpas_vtable = { + NULL, &wpas_message_handler, NULL, NULL, NULL, NULL + }; + + /* Register the message handler for the global dbus interface */ + if (!dbus_connection_register_object_path(iface->con, + WPAS_DBUS_PATH, &wpas_vtable, + iface)) { + wpa_printf(MSG_ERROR, "dbus: Could not set up message " + "handler"); + return -1; + } + + /* Register our service with the message bus */ + dbus_error_init(&error); + switch (dbus_bus_request_name(iface->con, WPAS_DBUS_SERVICE, + 0, &error)) { + case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: + ret = 0; + break; + case DBUS_REQUEST_NAME_REPLY_EXISTS: + case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: + case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: + wpa_printf(MSG_ERROR, "dbus: Could not request service name: " + "already registered"); + break; + default: + wpa_printf(MSG_ERROR, "dbus: Could not request service name: " + "%s %s", error.name, error.message); + break; + } + dbus_error_free(&error); + + if (ret != 0) + return -1; + + wpa_printf(MSG_DEBUG, "Providing DBus service '" WPAS_DBUS_SERVICE + "'."); + + return 0; +} + + +/** + * wpas_dbus_register_new_iface - Register a new interface with dbus + * @wpa_s: %wpa_supplicant interface description structure to register + * Returns: 0 on success, -1 on error + * + * Registers a new interface with dbus and assigns it a dbus object path. + */ +int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus; + DBusConnection * con; + u32 next; + DBusObjectPathVTable vtable = { + NULL, &wpas_iface_message_handler, NULL, NULL, NULL, NULL + }; + + /* Do nothing if the control interface is not turned on */ + if (ctrl_iface == NULL) + return 0; + + con = ctrl_iface->con; + next = ctrl_iface->next_objid++; + + /* Create and set the interface's object path */ + wpa_s->dbus_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (wpa_s->dbus_path == NULL) + return -1; + os_snprintf(wpa_s->dbus_path, WPAS_DBUS_OBJECT_PATH_MAX, + WPAS_DBUS_PATH_INTERFACES "/%u", + next); + + /* Register the message handler for the interface functions */ + if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable, + wpa_s)) { + wpa_printf(MSG_ERROR, "dbus: Could not set up message " + "handler for interface %s", wpa_s->ifname); + return -1; + } + + return 0; +} + + +/** + * wpas_dbus_unregister_iface - Unregister an interface from dbus + * @wpa_s: wpa_supplicant interface structure + * Returns: 0 on success, -1 on failure + * + * Unregisters the interface with dbus + */ +int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *ctrl_iface; + DBusConnection *con; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + con = ctrl_iface->con; + if (!dbus_connection_unregister_object_path(con, wpa_s->dbus_path)) + return -1; + + os_free(wpa_s->dbus_path); + wpa_s->dbus_path = NULL; + + return 0; +} + + +/** + * wpa_supplicant_get_iface_by_dbus_path - Get a new network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @path: Pointer to a dbus object path representing an interface + * Returns: Pointer to the interface or %NULL if not found + */ +struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( + struct wpa_global *global, const char *path) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (strcmp(wpa_s->dbus_path, path) == 0) + return wpa_s; + } + return NULL; +} diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h new file mode 100644 index 0000000..a9840c2 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_old.h @@ -0,0 +1,131 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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. + */ + +#ifndef CTRL_IFACE_DBUS_H +#define CTRL_IFACE_DBUS_H + +struct wps_credential; + +#ifdef CONFIG_CTRL_IFACE_DBUS + +#define WPAS_DBUS_OBJECT_PATH_MAX 150 + +#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant" +#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant" +#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant" + +#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces" +#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" + +#define WPAS_DBUS_NETWORKS_PART "Networks" +#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" + +#define WPAS_DBUS_BSSIDS_PART "BSSIDs" +#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID" + + +/* Errors */ +#define WPAS_ERROR_INVALID_NETWORK \ + WPAS_DBUS_IFACE_INTERFACE ".InvalidNetwork" +#define WPAS_ERROR_INVALID_BSSID \ + WPAS_DBUS_IFACE_INTERFACE ".InvalidBSSID" + +#define WPAS_ERROR_INVALID_OPTS \ + WPAS_DBUS_INTERFACE ".InvalidOptions" +#define WPAS_ERROR_INVALID_IFACE \ + WPAS_DBUS_INTERFACE ".InvalidInterface" + +#define WPAS_ERROR_ADD_ERROR \ + WPAS_DBUS_INTERFACE ".AddError" +#define WPAS_ERROR_EXISTS_ERROR \ + WPAS_DBUS_INTERFACE ".ExistsError" +#define WPAS_ERROR_REMOVE_ERROR \ + WPAS_DBUS_INTERFACE ".RemoveError" + +#define WPAS_ERROR_SCAN_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".ScanError" +#define WPAS_ERROR_ADD_NETWORK_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".AddNetworkError" +#define WPAS_ERROR_INTERNAL_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".InternalError" +#define WPAS_ERROR_REMOVE_NETWORK_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".RemoveNetworkError" + +#define WPAS_ERROR_WPS_PBC_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".WpsPbcError" +#define WPAS_ERROR_WPS_PIN_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".WpsPinError" +#define WPAS_ERROR_WPS_REG_ERROR \ + WPAS_DBUS_IFACE_INTERFACE ".WpsRegError" + +#define WPAS_DBUS_BSSID_FORMAT "%02x%02x%02x%02x%02x%02x" + +struct wpa_global; +struct wpa_supplicant; + +int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface); +void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s); +void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s); +void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, + enum wpa_states new_state, + enum wpa_states old_state); +void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred); + +char * wpas_dbus_decompose_object_path(const char *path, char **network, + char **bssid); + +int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s); +int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s); + + +/* Methods internal to the dbus control interface */ +struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( + struct wpa_global *global, const char *path); + +#else /* CONFIG_CTRL_IFACE_DBUS */ + +static inline void +wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) +{ +} + +static inline void +wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) +{ +} + +#define wpa_supplicant_dbus_notify_state_change(w,n,o) do { } while (0) + +static inline void +wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ +} + +static inline int +wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline int +wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +#endif /* CONFIG_CTRL_IFACE_DBUS */ + +#endif /* CTRL_IFACE_DBUS_H */ diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c new file mode 100644 index 0000000..de66140 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_old_handlers.c @@ -0,0 +1,1468 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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. + */ + +#include "includes.h" +#include <dbus/dbus.h> + +#include "common.h" +#include "eap_peer/eap_methods.h" +#include "common/ieee802_11_defs.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "rsn_supp/wpa.h" +#include "../config.h" +#include "../wpa_supplicant_i.h" +#include "../driver_i.h" +#include "../notify.h" +#include "../wpas_glue.h" +#include "../bss.h" +#include "../scan.h" +#include "dbus_old.h" +#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 + * Returns: a dbus error message + * + * Convenience function to create and return an invalid options error + */ +DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, + const char *arg) +{ + DBusMessage *reply; + + reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS, + "Did not receive correct message " + "arguments."); + if (arg != NULL) + dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + + return reply; +} + + +/** + * wpas_dbus_new_success_reply - Return a new success reply message + * @message: Pointer to incoming dbus message this reply refers to + * Returns: a dbus message containing a single UINT32 that indicates + * success (ie, a value of 1) + * + * Convenience function to create and return a success reply message + */ +DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message) +{ + DBusMessage *reply; + unsigned int success = 1; + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success, + DBUS_TYPE_INVALID); + return reply; +} + + +/** + * wpas_dbus_global_add_interface - Request registration of a network interface + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: The object path of the new interface object, + * or a dbus error message with more information + * + * Handler function for "addInterface" method call. Handles requests + * by dbus clients to register a network interface that wpa_supplicant + * will manage. + */ +DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, + struct wpa_global *global) +{ + char *ifname = NULL; + char *driver = NULL; + char *driver_param = NULL; + char *confname = NULL; + char *bridge_ifname = NULL; + DBusMessage *reply = NULL; + DBusMessageIter iter; + + dbus_message_iter_init(message, &iter); + + /* First argument: interface name (DBUS_TYPE_STRING) + * Required; must be non-zero length + */ + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + goto error; + dbus_message_iter_get_basic(&iter, &ifname); + if (!os_strlen(ifname)) + goto error; + + /* Second argument: dict of options */ + if (dbus_message_iter_next(&iter)) { + DBusMessageIter iter_dict; + struct wpa_dbus_dict_entry entry; + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + goto error; + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + if (!strcmp(entry.key, "driver") && + (entry.type == DBUS_TYPE_STRING)) { + 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)) { + 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)) { + 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)) { + bridge_ifname = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + if (bridge_ifname == NULL) + goto error; + } else { + wpa_dbus_dict_entry_clear(&entry); + goto error; + } + } + } + + /* + * Try to get the wpa_supplicant record for this iface, return + * an error if we already control it. + */ + if (wpa_supplicant_get_iface(global, ifname) != NULL) { + reply = dbus_message_new_error(message, + WPAS_ERROR_EXISTS_ERROR, + "wpa_supplicant already " + "controls this interface."); + } else { + struct wpa_supplicant *wpa_s; + struct wpa_interface iface; + os_memset(&iface, 0, sizeof(iface)); + iface.ifname = ifname; + iface.driver = driver; + iface.driver_param = driver_param; + iface.confname = confname; + iface.bridge_ifname = bridge_ifname; + /* Otherwise, have wpa_supplicant attach to it. */ + if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { + const char *path = wpa_s->dbus_path; + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, + &path, DBUS_TYPE_INVALID); + } else { + reply = dbus_message_new_error(message, + WPAS_ERROR_ADD_ERROR, + "wpa_supplicant " + "couldn't grab this " + "interface."); + } + } + +out: + os_free(driver); + os_free(driver_param); + os_free(confname); + os_free(bridge_ifname); + return reply; + +error: + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; +} + + +/** + * wpas_dbus_global_remove_interface - Request deregistration of an interface + * @message: Pointer to incoming dbus message + * @global: wpa_supplicant global data structure + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0), or returns a dbus error message with more information + * + * Handler function for "removeInterface" method call. Handles requests + * by dbus clients to deregister a network interface that wpa_supplicant + * currently manages. + */ +DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, + struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s; + char *path; + DBusMessage *reply = NULL; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path); + if (wpa_s == NULL) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + + if (!wpa_supplicant_remove_iface(global, wpa_s)) { + reply = wpas_dbus_new_success_reply(message); + } else { + reply = dbus_message_new_error(message, + WPAS_ERROR_REMOVE_ERROR, + "wpa_supplicant couldn't " + "remove this interface."); + } + +out: + return reply; +} + + +/** + * wpas_dbus_global_get_interface - Get the object path for an interface name + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: The object path of the interface object, + * or a dbus error message with more information + * + * Handler function for "getInterface" method call. Handles requests + * by dbus clients for the object path of an specific network interface. + */ +DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply = NULL; + const char *ifname; + const char *path; + struct wpa_supplicant *wpa_s; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &ifname, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + wpa_s = wpa_supplicant_get_iface(global, ifname); + if (wpa_s == NULL) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + + path = wpa_s->dbus_path; + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + +out: + return reply; +} + + +/** + * wpas_dbus_global_set_debugparams- Set the debug params + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0), or returns a dbus error message with more information + * + * Handler function for "setDebugParams" method call. Handles requests + * by dbus clients for the object path of an specific network interface. + */ +DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply = NULL; + int debug_level; + dbus_bool_t debug_timestamp; + dbus_bool_t debug_show_keys; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_INT32, &debug_level, + DBUS_TYPE_BOOLEAN, &debug_timestamp, + DBUS_TYPE_BOOLEAN, &debug_show_keys, + DBUS_TYPE_INVALID)) { + return wpas_dbus_new_invalid_opts_error(message, NULL); + } + + if (wpa_supplicant_set_debug_params(global, debug_level, + debug_timestamp ? 1 : 0, + debug_show_keys ? 1 : 0)) { + return wpas_dbus_new_invalid_opts_error(message, NULL); + } + + reply = wpas_dbus_new_success_reply(message); + + return reply; +} + + +/** + * wpas_dbus_iface_scan - Request a wireless scan on an interface + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "scan" method call of a network device. Requests + * that wpa_supplicant perform a wireless scan as soon as possible + * on a particular wireless interface. + */ +DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + wpa_s->scan_req = 2; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_scan_results - Get the results of a recent scan request + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: a dbus message containing a dbus array of objects paths, or returns + * a dbus error message if not scan results could be found + * + * Handler function for "scanResults" method call of a network device. Returns + * a dbus message containing the object paths of wireless networks found. + */ +DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter; + DBusMessageIter sub_iter; + struct wpa_bss *bss; + + /* 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); + + /* 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) { + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX]; + char *path = path_buf; + + /* Construct the object path for this network. Note that ':' + * is not a valid character in dbus object paths. + */ + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%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); + } + + dbus_message_iter_close_container(&iter, &sub_iter); + + return reply; +} + + +/** + * wpas_dbus_bssid_properties - Return the properties of a scanned network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @res: wpa_supplicant scan result for which to get properties + * Returns: a dbus message containing the properties for the requested network + * + * Handler function for "properties" method call of a scanned network. + * Returns a dbus message containing the the properties. + */ +DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + DBusMessage *reply; + DBusMessageIter iter, iter_dict; + const u8 *ie; + + /* Dump the properties into a dbus message */ + reply = dbus_message_new_method_return(message); + + dbus_message_iter_init_append(reply, &iter); + if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) + goto error; + + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", + (const char *) bss->bssid, + ETH_ALEN)) + goto error; + + ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); + if (ie) { + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", + (const char *) (ie + 2), + ie[1])) + goto error; + } + + ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (ie) { + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", + (const char *) ie, + ie[1] + 2)) + goto error; + } + + ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (ie) { + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", + (const char *) ie, + ie[1] + 2)) + goto error; + } + + ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); + if (ie) { + if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", + (const char *) ie, + ie[1] + 2)) + goto error; + } + + if (bss->freq) { + if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency", + bss->freq)) + goto error; + } + if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", + bss->caps)) + goto error; + if (!(bss->flags & WPA_BSS_QUAL_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) + goto error; + if (!(bss->flags & WPA_BSS_NOISE_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) + goto error; + if (!(bss->flags & WPA_BSS_LEVEL_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) + goto error; + if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate", + wpa_bss_get_max_rate(bss) * 500000)) + goto error; + + if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + goto error; + + return reply; + +error: + if (reply) + dbus_message_unref(reply); + return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning " + "BSSID properties."); +} + + +/** + * wpas_dbus_iface_capabilities - Return interface capabilities + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a dict of strings + * + * Handler function for "capabilities" method call of an interface. + */ +DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_driver_capa capa; + int res; + DBusMessageIter iter, iter_dict; + char **eap_methods; + size_t num_items; + dbus_bool_t strict = FALSE; + DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_BOOLEAN, &strict, + DBUS_TYPE_INVALID)) + strict = FALSE; + + reply = dbus_message_new_method_return(message); + + dbus_message_iter_init_append(reply, &iter); + if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) + goto error; + + /* EAP methods */ + eap_methods = eap_get_names_as_string_array(&num_items); + if (eap_methods) { + dbus_bool_t success = FALSE; + size_t i = 0; + + success = wpa_dbus_dict_append_string_array( + &iter_dict, "eap", (const char **) eap_methods, + num_items); + + /* free returned method array */ + while (eap_methods[i]) + os_free(eap_methods[i++]); + os_free(eap_methods); + + if (!success) + goto error; + } + + res = wpa_drv_get_capa(wpa_s, &capa); + + /***** pairwise cipher */ + if (res < 0) { + if (!strict) { + const char *args[] = {"CCMP", "TKIP", "NONE"}; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "pairwise", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "CCMP")) + goto error; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "TKIP")) + goto error; + } + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "NONE")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + /***** group cipher */ + if (res < 0) { + if (!strict) { + const char *args[] = { + "CCMP", "TKIP", "WEP104", "WEP40" + }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "group", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "CCMP")) + goto error; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "TKIP")) + goto error; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WEP104")) + goto error; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WEP40")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + /***** key management */ + if (res < 0) { + if (!strict) { + const char *args[] = { + "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE", + "NONE" + }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "key_mgmt", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + "NONE")) + goto error; + + if (!wpa_dbus_dict_string_array_add_element(&iter_array, + "IEEE8021X")) + goto error; + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-EAP")) + goto error; + } + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-PSK")) + goto error; + } + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-NONE")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + /***** WPA protocol */ + if (res < 0) { + if (!strict) { + const char *args[] = { "RSN", "WPA" }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "proto", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "RSN")) + goto error; + } + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + /***** auth alg */ + if (res < 0) { + if (!strict) { + const char *args[] = { "OPEN", "SHARED", "LEAP" }; + if (!wpa_dbus_dict_append_string_array( + &iter_dict, "auth_alg", args, + sizeof(args) / sizeof(char*))) + goto error; + } + } else { + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + + if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "OPEN")) + goto error; + } + + if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "SHARED")) + goto error; + } + + if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "LEAP")) + goto error; + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto error; + } + + if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + goto error; + + return reply; + +error: + if (reply) + dbus_message_unref(reply); + return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning " + "interface capabilities."); +} + + +/** + * wpas_dbus_iface_add_network - Add a new configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing the object path of the new network + * + * Handler function for "addNetwork" method call of a network interface. + */ +DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_ssid *ssid; + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) { + reply = dbus_message_new_error(message, + WPAS_ERROR_ADD_NETWORK_ERROR, + "wpa_supplicant could not add " + "a network on this interface."); + goto out; + } + wpas_notify_network_added(wpa_s, ssid); + ssid->disabled = 1; + wpa_config_set_network_defaults(ssid); + + /* Construct the object path for this network. */ + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NETWORKS_PART "/%d", + wpa_s->dbus_path, ssid->id); + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, + &path, DBUS_TYPE_INVALID); + +out: + return reply; +} + + +/** + * wpas_dbus_iface_remove_network - Remove a configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "removeNetwork" method call of a network interface. + */ +DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *op; + char *iface = NULL, *net_id = NULL; + int id; + struct wpa_ssid *ssid; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + /* Extract the network ID */ + iface = wpas_dbus_decompose_object_path(op, &net_id, NULL); + if (iface == NULL) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + /* Ensure the network is actually a child of this interface */ + if (os_strcmp(iface, wpa_s->dbus_path) != 0) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + id = strtoul(net_id, NULL, 10); + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + wpas_notify_network_removed(wpa_s, ssid); + + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + reply = dbus_message_new_error(message, + WPAS_ERROR_REMOVE_NETWORK_ERROR, + "error removing the specified " + "on this interface."); + goto out; + } + + if (ssid == wpa_s->current_ssid) + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + reply = wpas_dbus_new_success_reply(message); + +out: + os_free(iface); + os_free(net_id); + return reply; +} + + +static const char *dont_quote[] = { + "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", + "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", + "bssid", NULL +}; + + +static dbus_bool_t should_quote_opt(const char *key) +{ + int i = 0; + while (dont_quote[i] != NULL) { + if (strcmp(key, dont_quote[i]) == 0) + return FALSE; + i++; + } + return TRUE; +} + + +/** + * wpas_dbus_iface_set_network - Set options for a configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "set" method call of a configured network. + */ +DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + DBusMessage *reply = NULL; + struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; + DBusMessageIter iter, iter_dict; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + char *value = NULL; + size_t size = 50; + int ret; + + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { + reply = wpas_dbus_new_invalid_opts_error(message, + NULL); + goto out; + } + + /* Type conversions, since wpa_supplicant wants strings */ + if (entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_BYTE) { + if (entry.array_len <= 0) + goto error; + + size = entry.array_len * 2 + 1; + value = os_zalloc(size); + if (value == NULL) + goto error; + ret = wpa_snprintf_hex(value, size, + (u8 *) entry.bytearray_value, + entry.array_len); + if (ret <= 0) + goto error; + } else if (entry.type == DBUS_TYPE_STRING) { + if (should_quote_opt(entry.key)) { + size = os_strlen(entry.str_value); + /* Zero-length option check */ + if (size <= 0) + goto error; + size += 3; /* For quotes and terminator */ + value = os_zalloc(size); + if (value == NULL) + goto error; + ret = os_snprintf(value, size, "\"%s\"", + entry.str_value); + if (ret < 0 || (size_t) ret != (size - 1)) + goto error; + } else { + value = os_strdup(entry.str_value); + if (value == NULL) + goto error; + } + } else if (entry.type == DBUS_TYPE_UINT32) { + value = os_zalloc(size); + if (value == NULL) + goto error; + ret = os_snprintf(value, size, "%u", + entry.uint32_value); + if (ret <= 0) + goto error; + } else if (entry.type == DBUS_TYPE_INT32) { + value = os_zalloc(size); + if (value == NULL) + goto error; + ret = os_snprintf(value, size, "%d", + entry.int32_value); + if (ret <= 0) + goto error; + } else + goto error; + + if (wpa_config_set(ssid, entry.key, value, 0) < 0) + goto error; + + if ((os_strcmp(entry.key, "psk") == 0 && + value[0] == '"' && ssid->ssid_len) || + (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) + wpa_config_update_psk(ssid); + else if (os_strcmp(entry.key, "priority") == 0) + wpa_config_update_prio_list(wpa_s->conf); + + os_free(value); + wpa_dbus_dict_entry_clear(&entry); + continue; + + error: + os_free(value); + reply = wpas_dbus_new_invalid_opts_error(message, entry.key); + wpa_dbus_dict_entry_clear(&entry); + break; + } + + if (!reply) + reply = wpas_dbus_new_success_reply(message); + +out: + return reply; +} + + +/** + * wpas_dbus_iface_enable_network - Mark a configured network as enabled + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "enable" method call of a configured network. + */ +DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + wpa_supplicant_enable_network(wpa_s, ssid); + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_disable_network - Mark a configured network as disabled + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "disable" method call of a configured network. + */ +DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + wpa_supplicant_disable_network(wpa_s, ssid); + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_select_network - Attempt association with a configured network + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "selectNetwork" method call of network interface. + */ +DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *op; + struct wpa_ssid *ssid; + char *iface_obj_path = NULL; + char *network = NULL; + + if (os_strlen(dbus_message_get_signature(message)) == 0) { + /* Any network */ + ssid = NULL; + } else { + int nid; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, + NULL); + goto out; + } + + /* Extract the network number */ + iface_obj_path = wpas_dbus_decompose_object_path(op, + &network, + NULL); + if (iface_obj_path == NULL) { + reply = wpas_dbus_new_invalid_iface_error(message); + goto out; + } + /* Ensure the object path really points to this interface */ + if (os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + nid = strtoul(network, NULL, 10); + if (errno == EINVAL) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + + ssid = wpa_config_get_network(wpa_s->conf, nid); + if (ssid == NULL) { + reply = wpas_dbus_new_invalid_network_error(message); + goto out; + } + } + + /* Finally, associate with the network */ + wpa_supplicant_select_network(wpa_s, ssid); + + reply = wpas_dbus_new_success_reply(message); + +out: + os_free(iface_obj_path); + os_free(network); + return reply; +} + + +/** + * wpas_dbus_iface_disconnect - Terminate the current connection + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "disconnect" method call of network interface. + */ +DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + wpa_s->disconnected = 1; + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_set_ap_scan - Control roaming mode + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "setAPScan" method call. + */ +DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + dbus_uint32_t ap_scan = 1; + + if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + reply = wpas_dbus_new_success_reply(message); + +out: + return reply; +} + + +/** + * wpas_dbus_iface_set_smartcard_modules - Set smartcard related module paths + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "setSmartcardModules" method call. + */ +DBusMessage * wpas_dbus_iface_set_smartcard_modules( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, iter_dict; + char *opensc_engine_path = NULL; + char *pkcs11_engine_path = NULL; + char *pkcs11_module_path = NULL; + struct wpa_dbus_dict_entry entry; + + if (!dbus_message_iter_init(message, &iter)) + goto error; + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + goto error; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + if (!strcmp(entry.key, "opensc_engine_path") && + (entry.type == DBUS_TYPE_STRING)) { + 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)) { + 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)) { + pkcs11_module_path = os_strdup(entry.str_value); + if (pkcs11_module_path == NULL) + goto error; + } else { + wpa_dbus_dict_entry_clear(&entry); + goto error; + } + wpa_dbus_dict_entry_clear(&entry); + } + + os_free(wpa_s->conf->opensc_engine_path); + wpa_s->conf->opensc_engine_path = opensc_engine_path; + os_free(wpa_s->conf->pkcs11_engine_path); + wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path; + os_free(wpa_s->conf->pkcs11_module_path); + wpa_s->conf->pkcs11_module_path = pkcs11_module_path; + + wpa_sm_set_eapol(wpa_s->wpa, NULL); + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + wpa_supplicant_init_eapol(wpa_s); + wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); + + return wpas_dbus_new_success_reply(message); + +error: + os_free(opensc_engine_path); + os_free(pkcs11_engine_path); + os_free(pkcs11_module_path); + return wpas_dbus_new_invalid_opts_error(message, NULL); +} + + +/** + * wpas_dbus_iface_get_state - Get interface state + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing a STRING representing the current + * interface state + * + * Handler function for "state" method call. + */ +DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *str_state; + + reply = dbus_message_new_method_return(message); + if (reply != NULL) { + str_state = wpa_supplicant_state_txt(wpa_s->wpa_state); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state, + DBUS_TYPE_INVALID); + } + + return reply; +} + + +/** + * wpas_dbus_iface_get_scanning - Get interface scanning state + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing whether the interface is scanning + * + * Handler function for "scanning" method call. + */ +DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; + + reply = dbus_message_new_method_return(message); + if (reply != NULL) { + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning, + DBUS_TYPE_INVALID); + } else { + wpa_printf(MSG_ERROR, "dbus: Not enough memory to return " + "scanning state"); + } + + return reply; +} + + +/** + * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Asks wpa_supplicant to internally store a one or more binary blobs. + */ +DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; + DBusMessageIter iter, iter_dict; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + return wpas_dbus_new_invalid_opts_error(message, NULL); + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + struct wpa_config_blob *blob; + + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { + reply = wpas_dbus_new_invalid_opts_error(message, + NULL); + break; + } + + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) { + reply = wpas_dbus_new_invalid_opts_error( + message, "Byte array expected."); + break; + } + + if ((entry.array_len <= 0) || (entry.array_len > 65536) || + !strlen(entry.key)) { + reply = wpas_dbus_new_invalid_opts_error( + message, "Invalid array size."); + break; + } + + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) { + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_ERROR, + "Not enough memory to add blob."); + break; + } + blob->data = os_zalloc(entry.array_len); + if (blob->data == NULL) { + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_ERROR, + "Not enough memory to add blob data."); + os_free(blob); + break; + } + + blob->name = os_strdup(entry.key); + blob->len = entry.array_len; + os_memcpy(blob->data, (u8 *) entry.bytearray_value, + entry.array_len); + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_ERROR, + "Error adding blob."); + break; + } + + /* Success */ + if (!wpa_config_remove_blob(wpa_s->conf, blob->name)) + wpas_notify_blob_removed(wpa_s, blob->name); + wpa_config_set_blob(wpa_s->conf, blob); + wpas_notify_blob_added(wpa_s, blob->name); + + wpa_dbus_dict_entry_clear(&entry); + } + wpa_dbus_dict_entry_clear(&entry); + + return reply ? reply : wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_remove_blob - Remove named binary blobs + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Asks wpa_supplicant to remove one or more previously stored binary blobs. + */ +DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, array; + char *err_msg = NULL; + + dbus_message_iter_init(message, &iter); + + if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) || + (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)) + return wpas_dbus_new_invalid_opts_error(message, NULL); + + dbus_message_iter_recurse(&iter, &array); + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { + const char *name; + + dbus_message_iter_get_basic(&array, &name); + if (!os_strlen(name)) + err_msg = "Invalid blob name."; + + if (wpa_config_remove_blob(wpa_s->conf, name) != 0) + err_msg = "Error removing blob."; + else + wpas_notify_blob_removed(wpa_s, name); + dbus_message_iter_next(&array); + } + + if (err_msg) + return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR, + err_msg); + + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0), or returns a dbus error message with more information + * + * Handler function for "flush" method call. Handles requests for an + * interface with an optional "age" parameter that specifies the minimum + * age of a BSS to be flushed. + */ +DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + int flush_age = 0; + + if (os_strlen(dbus_message_get_signature(message)) != 0 && + !dbus_message_get_args(message, NULL, + DBUS_TYPE_INT32, &flush_age, + DBUS_TYPE_INVALID)) { + return wpas_dbus_new_invalid_opts_error(message, NULL); + } + + if (flush_age == 0) + wpa_bss_flush(wpa_s); + else + wpa_bss_flush_by_age(wpa_s, flush_age); + + return wpas_dbus_new_success_reply(message); +} diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h new file mode 100644 index 0000000..009e807 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_old_handlers.h @@ -0,0 +1,107 @@ +/* + * WPA Supplicant / dbus-based control interface + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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. + */ + +#ifndef CTRL_IFACE_DBUS_HANDLERS_H +#define CTRL_IFACE_DBUS_HANDLERS_H + +struct wpa_bss; + +DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message); +DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message); + +DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, + struct wpa_global *global); + +DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_bss *bss); + +DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, + struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_set_smartcard_modules( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message); +DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, + const char *arg); + +#endif /* CTRL_IFACE_DBUS_HANDLERS_H */ + diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c new file mode 100644 index 0000000..c04b844 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_old_handlers_wps.c @@ -0,0 +1,163 @@ +/* + * WPA Supplicant / dbus-based control interface (WPS) + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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. + */ + +#include "includes.h" +#include <dbus/dbus.h> + +#include "common.h" +#include "../config.h" +#include "../wpa_supplicant_i.h" +#include "../wps_supplicant.h" +#include "dbus_old.h" +#include "dbus_old_handlers.h" + +/** + * wpas_dbus_iface_wps_pbc - Request credentials using WPS PBC method + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "wpsPbc" method call + */ +DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + char *arg_bssid = NULL; + u8 bssid[ETH_ALEN]; + int ret = 0; + + if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, + DBUS_TYPE_INVALID)) + return wpas_dbus_new_invalid_opts_error(message, NULL); + + if (!os_strcmp(arg_bssid, "any")) + ret = wpas_wps_start_pbc(wpa_s, NULL, 0); + else if (!hwaddr_aton(arg_bssid, bssid)) + ret = wpas_wps_start_pbc(wpa_s, bssid, 0); + else { + return wpas_dbus_new_invalid_opts_error(message, + "Invalid BSSID"); + } + + if (ret < 0) { + return dbus_message_new_error(message, + WPAS_ERROR_WPS_PBC_ERROR, + "Could not start PBC " + "negotiation"); + } + + return wpas_dbus_new_success_reply(message); +} + + +/** + * wpas_dbus_iface_wps_pin - Establish the PIN number of the enrollee + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "wpsPin" method call + */ +DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + char *arg_bssid; + char *pin = NULL; + u8 bssid[ETH_ALEN], *_bssid = NULL; + int ret = 0; + + if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, + DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) + return wpas_dbus_new_invalid_opts_error(message, NULL); + + if (!os_strcmp(arg_bssid, "any")) + _bssid = NULL; + else if (!hwaddr_aton(arg_bssid, bssid)) + _bssid = bssid; + else { + return wpas_dbus_new_invalid_opts_error(message, + "Invalid BSSID"); + } + + if (os_strlen(pin) > 0) + ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0, + DEV_PW_DEFAULT); + else + ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, + DEV_PW_DEFAULT); + + if (ret < 0) { + return dbus_message_new_error(message, + WPAS_ERROR_WPS_PIN_ERROR, + "Could not init PIN"); + } + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return NULL; + + if (ret == 0) { + dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin, + DBUS_TYPE_INVALID); + } else { + char npin[9]; + os_snprintf(npin, sizeof(npin), "%08d", ret); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &npin, + DBUS_TYPE_INVALID); + } + return reply; +} + + +/** + * wpas_dbus_iface_wps_reg - Request credentials using the PIN of the AP + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing a UINT32 indicating success (1) or + * failure (0) + * + * Handler function for "wpsReg" method call + */ +DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + char *arg_bssid; + char *pin = NULL; + u8 bssid[ETH_ALEN]; + int ret = 0; + + if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, + DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) + return wpas_dbus_new_invalid_opts_error(message, NULL); + + if (!os_strcmp(arg_bssid, "any")) + ret = wpas_wps_start_reg(wpa_s, NULL, pin, NULL); + else if (!hwaddr_aton(arg_bssid, bssid)) + ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL); + else { + return wpas_dbus_new_invalid_opts_error(message, + "Invalid BSSID"); + } + + if (ret < 0) { + return dbus_message_new_error(message, + WPAS_ERROR_WPS_PBC_ERROR, + "Could not request credentials"); + } + + return wpas_dbus_new_success_reply(message); +} diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service new file mode 100644 index 0000000..a9ce1ec --- /dev/null +++ b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=fi.epitest.hostap.WPASupplicant +Exec=/sbin/wpa_supplicant -u +User=root diff --git a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service new file mode 100644 index 0000000..df78471 --- /dev/null +++ b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=fi.w1.wpa_supplicant1 +Exec=/sbin/wpa_supplicant -u +User=root diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig new file mode 100644 index 0000000..0c8e5f2 --- /dev/null +++ b/wpa_supplicant/defconfig @@ -0,0 +1,449 @@ +# Example wpa_supplicant build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cases, these lines should use += in order not +# to override previous values of the variables. + + +# Uncomment following two lines and fix the paths if you have installed OpenSSL +# or GnuTLS in non-default location +#CFLAGS += -I/usr/local/openssl/include +#LIBS += -L/usr/local/openssl/lib + +# Some Red Hat versions seem to include kerberos header files from OpenSSL, but +# the kerberos files are not in the default include path. Following line can be +# 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 +# replacement for WEXT and its use allows wpa_supplicant to properly control +# the driver to improve existing functionality like roaming and to support new +# functionality. +CONFIG_DRIVER_WEXT=y + +# Driver interface for Linux drivers using the nl80211 kernel interface +CONFIG_DRIVER_NL80211=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib +#LIBS_p += -L/usr/local/lib +#LIBS_c += -L/usr/local/lib + +# Driver interface for Windows NDIS +#CONFIG_DRIVER_NDIS=y +#CFLAGS += -I/usr/include/w32api/ddk +#LIBS += -L/usr/local/lib +# For native build using mingw +#CONFIG_NATIVE_WINDOWS=y +# Additional directories for cross-compilation on Linux host for mingw target +#CFLAGS += -I/opt/mingw/mingw32/include/ddk +#LIBS += -L/opt/mingw/mingw32/lib +#CC=mingw32-gcc +# By default, driver_ndis uses WinPcap for low-level operations. This can be +# replaced with the following option which replaces WinPcap calls with NDISUIO. +# However, this requires that WZC is disabled (net stop wzcsvc) before starting +# wpa_supplicant. +# CONFIG_USE_NDISUIO=y + +# Driver interface for development testing +#CONFIG_DRIVER_TEST=y + +# Include client MLME (management frame processing) for test driver +# This can be used to test MLME operations in hostapd with the test interface. +# space. +#CONFIG_CLIENT_MLME=y + +# Driver interface for wired Ethernet drivers +CONFIG_DRIVER_WIRED=y + +# Driver interface for the Broadcom RoboSwitch family +#CONFIG_DRIVER_ROBOSWITCH=y + +# Driver interface for no driver (e.g., WPS ER only) +#CONFIG_DRIVER_NONE=y + +# Solaris libraries +#LIBS += -lsocket -ldlpi -lnsl +#LIBS_c += -lsocket + +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is +# included) +CONFIG_IEEE8021X_EAPOL=y + +# EAP-MD5 +CONFIG_EAP_MD5=y + +# EAP-MSCHAPv2 +CONFIG_EAP_MSCHAPV2=y + +# EAP-TLS +CONFIG_EAP_TLS=y + +# EAL-PEAP +CONFIG_EAP_PEAP=y + +# EAP-TTLS +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. +#CONFIG_EAP_FAST=y + +# EAP-GTC +CONFIG_EAP_GTC=y + +# EAP-OTP +CONFIG_EAP_OTP=y + +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) +#CONFIG_EAP_SIM=y + +# EAP-PSK (experimental; this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# EAP-PAX +#CONFIG_EAP_PAX=y + +# LEAP +CONFIG_EAP_LEAP=y + +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) +#CONFIG_EAP_AKA=y + +# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used). +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=y + +# Enable USIM simulator (Milenage) for EAP-AKA +#CONFIG_USIM_SIMULATOR=y + +# EAP-SAKE +#CONFIG_EAP_SAKE=y + +# EAP-GPSK +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + +# EAP-TNC and related Trusted Network Connect support (experimental) +#CONFIG_EAP_TNC=y + +# Wi-Fi Protected Setup (WPS) +#CONFIG_WPS=y +# Enable WSC 2.0 support +#CONFIG_WPS2=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# Smartcard support (i.e., private key on a smartcard), e.g., with openssl +# engine. +CONFIG_SMARTCARD=y + +# PC/SC interface for smartcards (USIM, GSM SIM) +# Enable this if EAP-SIM or EAP-AKA is included +#CONFIG_PCSC=y + +# Development testing +#CONFIG_EAPOL_TEST=y + +# Select control interface backend for external programs, e.g, wpa_cli: +# unix = UNIX domain sockets (default for Linux/*BSD) +# udp = UDP sockets using localhost (127.0.0.1) +# named_pipe = Windows Named Pipe (default for Windows) +# y = use default (backwards compatibility) +# If this option is commented out, control interface is not included in the +# build. +CONFIG_CTRL_IFACE=y + +# Include support for GNU Readline and History Libraries in wpa_cli. +# When building a wpa_cli binary for distribution, please note that these +# libraries are licensed under GPL and as such, BSD license may not apply for +# the resulting binary. +#CONFIG_READLINE=y + +# Include internal line edit mode in wpa_cli. This can be used as a replacement +# for GNU Readline to provide limited command line editing and history support. +#CONFIG_WPA_CLI_EDIT=y + +# Remove debugging code that is printing out debug message to stdout. +# This can be used to reduce the size of the wpa_supplicant considerably +# if debugging code is not needed. The size reduction can be around 35% +# (e.g., 90 kB). +#CONFIG_NO_STDOUT_DEBUG=y + +# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save +# 35-50 kB in code size. +#CONFIG_NO_WPA=y + +# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to +# save about 1 kB in code size when building only WPA-Personal (no EAP support) +# or 6 kB if building for WPA-Enterprise. +#CONFIG_NO_WPA2=y + +# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support +# This option can be used to reduce code size by removing support for +# converting ASCII passphrases into PSK. If this functionality is removed, the +# PSK can only be configured as the 64-octet hexstring (e.g., from +# wpa_passphrase). This saves about 0.5 kB in code size. +#CONFIG_NO_WPA_PASSPHRASE=y + +# Disable scan result processing (ap_mode=1) to save code size by about 1 kB. +# This can be used if ap_scan=1 mode is never enabled. +#CONFIG_NO_SCAN_PROCESSING=y + +# Select configuration backend: +# file = text file (e.g., wpa_supplicant.conf; note: the configuration file +# path is given on command line, not here; this option is just used to +# select the backend that allows configuration files to be used) +# winreg = Windows registry (see win_example.reg for an example) +CONFIG_BACKEND=file + +# Remove configuration write functionality (i.e., to allow the configuration +# file to be updated based on runtime configuration changes). The runtime +# configuration can still be changed, the changes are just not going to be +# persistent over restarts. This option can be used to reduce code size by +# about 3.5 kB. +#CONFIG_NO_CONFIG_WRITE=y + +# Remove support for configuration blobs to reduce code size by about 1.5 kB. +#CONFIG_NO_CONFIG_BLOBS=y + +# Select program entry point implementation: +# main = UNIX/POSIX like main() function (default) +# main_winsvc = Windows service (read parameters from registry) +# main_none = Very basic example (development use only) +#CONFIG_MAIN=main + +# Select wrapper for operatins system and C library specific functions +# unix = UNIX/POSIX like systems (default) +# win32 = Windows systems +# none = Empty template +#CONFIG_OS=unix + +# Select event loop implementation +# eloop = select() loop (default) +# eloop_win = Windows events and WaitForMultipleObject() loop +# eloop_none = Empty template +#CONFIG_ELOOP=eloop + +# Select layer 2 packet implementation +# linux = Linux packet socket (default) +# pcap = libpcap/libdnet/WinPcap +# freebsd = FreeBSD libpcap +# winpcap = WinPcap with receive thread +# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y) +# none = Empty template +#CONFIG_L2_PACKET=linux + +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +CONFIG_PEERKEY=y + +# IEEE 802.11w (management frame protection) +# This version is an experimental implementation based on IEEE 802.11w/D1.0 +# draft and is subject to change since the standard has not yet been finalized. +# Driver support is also needed for IEEE 802.11w. +#CONFIG_IEEE80211W=y + +# Select TLS implementation +# openssl = OpenSSL (default) +# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA) +# internal = Internal TLSv1 implementation (experimental) +# none = Empty template +#CONFIG_TLS=openssl + +# Whether to enable TLS/IA support, which is required for EAP-TTLSv1. +# You need CONFIG_TLS=gnutls for this to have any effect. Please note that +# even though the core GnuTLS library is released under LGPL, this extra +# library uses GPL and as such, the terms of GPL apply to the combination +# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not +# apply for distribution of the resulting binary. +#CONFIG_GNUTLS_EXTRA=y + +# If CONFIG_TLS=internal is used, additional library and include paths are +# needed for LibTomMath. Alternatively, an integrated, minimal version of +# LibTomMath can be used. See beginning of libtommath.c for details on benefits +# and drawbacks of this option. +#CONFIG_INTERNAL_LIBTOMMATH=y +#ifndef CONFIG_INTERNAL_LIBTOMMATH +#LTM_PATH=/usr/src/libtommath-0.39 +#CFLAGS += -I$(LTM_PATH) +#LIBS += -L$(LTM_PATH) +#LIBS_p += -L$(LTM_PATH) +#endif +# At the cost of about 4 kB of additional binary size, the internal LibTomMath +# can be configured to include faster routines for exptmod, sqr, and div to +# speed up DH and RSA calculation considerably +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y + +# Include NDIS event processing through WMI into wpa_supplicant/wpasvc. +# This is only for Windows builds and requires WMI-related header files and +# WbemUuid.Lib from Platform SDK even when building with MinGW. +#CONFIG_NDIS_EVENTS_INTEGRATED=y +#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" + +# Add support for old DBus control interface +# (fi.epitest.hostap.WPASupplicant) +#CONFIG_CTRL_IFACE_DBUS=y + +# Add support for new DBus control interface +# (fi.w1.hostap.wpa_supplicant1) +#CONFIG_CTRL_IFACE_DBUS_NEW=y + +# Add introspection support for new DBus control interface +#CONFIG_CTRL_IFACE_DBUS_INTRO=y + +# Add support for loading EAP methods dynamically as shared libraries. +# When this option is enabled, each EAP method can be either included +# statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn). +# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to +# be loaded in the beginning of the wpa_supplicant configuration file +# (see load_dynamic_eap parameter in the example file) before being used in +# the network blocks. +# +# Note that some shared parts of EAP methods are included in the main program +# and in order to be able to use dynamic EAP methods using these parts, the +# main program must have been build with the EAP method enabled (=y or =dyn). +# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries +# unless at least one of them was included in the main build to force inclusion +# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included +# in the main build to be able to load these methods dynamically. +# +# Please also note that using dynamic libraries will increase the total binary +# size. Thus, it may not be the best option for targets that have limited +# amount of memory/flash. +#CONFIG_DYNAMIC_EAP_METHODS=y + +# IEEE Std 802.11r-2008 (Fast BSS Transition) +#CONFIG_IEEE80211R=y + +# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) +#CONFIG_DEBUG_FILE=y + +# Send debug messages to syslog instead of stdout +#CONFIG_DEBUG_SYSLOG=y +# Set syslog facility for debug messages +#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON + +# Enable privilege separation (see README 'Privilege separation' for details) +#CONFIG_PRIVSEP=y + +# Enable mitigation against certain attacks against TKIP by delaying Michael +# MIC error reports by a random amount of time between 0 and 60 seconds +#CONFIG_DELAYED_MIC_ERROR_REPORT=y + +# Enable tracing code for developer debugging +# This tracks use of memory allocations and other registrations and reports +# incorrect use with a backtrace of call (or allocation) location. +#CONFIG_WPA_TRACE=y +# For BSD, comment out these. +#LIBS += -lexecinfo +#LIBS_p += -lexecinfo +#LIBS_c += -lexecinfo + +# Use libbfd to get more details for developer debugging +# This enables use of libbfd to get more detailed symbols for the backtraces +# generated by CONFIG_WPA_TRACE=y. +#CONFIG_WPA_TRACE_BFD=y +# For BSD, comment out these. +#LIBS += -lbfd -liberty -lz +#LIBS_p += -lbfd -liberty -lz +#LIBS_c += -lbfd -liberty -lz + +# wpa_supplicant depends on strong random number generation being available +# from the operating system. os_get_random() function is used to fetch random +# data when needed, e.g., for key generation. On Linux and BSD systems, this +# works by reading /dev/urandom. It should be noted that the OS entropy pool +# needs to be properly initialized before wpa_supplicant is started. This is +# important especially on embedded devices that do not have a hardware random +# number generator and may by default start up with minimal entropy available +# for random number generation. +# +# As a safety net, wpa_supplicant is by default trying to internally collect +# additional entropy for generating random data to mix in with the data fetched +# from the OS. This by itself is not considered to be very strong, but it may +# help in cases where the system pool is not initialized properly. However, it +# is very strongly recommended that the system pool is initialized with enough +# entropy either by using hardware assisted random number generatior or by +# storing state over device reboots. +# +# If the os_get_random() is known to provide strong ramdom data (e.g., on +# Linux/BSD, the board in question is known to have reliable source of random +# data from /dev/urandom), the internal wpa_supplicant random pool can be +# disabled. This will save some in binary size and CPU use. However, this +# should only be considered for builds that are known to be used on devices +# that meet the requirements described above. +#CONFIG_NO_RANDOM_POOL=y diff --git a/wpa_supplicant/doc/docbook/.gitignore b/wpa_supplicant/doc/docbook/.gitignore new file mode 100644 index 0000000..8c3945c --- /dev/null +++ b/wpa_supplicant/doc/docbook/.gitignore @@ -0,0 +1,6 @@ +manpage.links +manpage.refs +*.8 +*.5 +*.html +*.pdf diff --git a/wpa_supplicant/doc/docbook/Makefile b/wpa_supplicant/doc/docbook/Makefile new file mode 100644 index 0000000..aaeee2e --- /dev/null +++ b/wpa_supplicant/doc/docbook/Makefile @@ -0,0 +1,27 @@ +all: man html pdf + +FILES += wpa_background +FILES += wpa_cli +FILES += wpa_gui +FILES += wpa_passphrase +FILES += wpa_priv +FILES += wpa_supplicant.conf +FILES += wpa_supplicant + +man: + for i in $(FILES); do docbook2man $$i.sgml; done + +html: + for i in $(FILES); do docbook2html $$i.sgml && \ + mv index.html $$i.html; done + +pdf: + for i in $(FILES); do docbook2pdf $$i.sgml; done + + +clean: + rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 + rm -f wpa_supplicant.conf.5 + rm -f manpage.links manpage.refs + rm -f $(FILES:%=%.pdf) + rm -f $(FILES:%=%.html) diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml new file mode 100644 index 0000000..f47235b --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_background.sgml @@ -0,0 +1,101 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_background</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_background</refname> + <refpurpose>Background information on Wi-Fi Protected Access and IEEE 802.11i</refpurpose> + </refnamediv> + <refsect1> + <title>WPA</title> + + <para>The original security mechanism of IEEE 802.11 standard was + not designed to be strong and has proven to be insufficient for + most networks that require some kind of security. Task group I + (Security) of IEEE 802.11 working group + (http://www.ieee802.org/11/) has worked to address the flaws of + the base standard and has in practice completed its work in May + 2004. The IEEE 802.11i amendment to the IEEE 802.11 standard was + approved in June 2004 and published in July 2004.</para> + + <para>Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version + of the IEEE 802.11i work (draft 3.0) to define a subset of the + security enhancements that can be implemented with existing wlan + hardware. This is called Wi-Fi Protected Access<TM> (WPA). This + has now become a mandatory component of interoperability testing + and certification done by Wi-Fi Alliance. Wi-Fi provides + information about WPA at its web site + (http://www.wi-fi.org/OpenSection/protected_access.asp).</para> + + <para>IEEE 802.11 standard defined wired equivalent privacy (WEP) + algorithm for protecting wireless networks. WEP uses RC4 with + 40-bit keys, 24-bit initialization vector (IV), and CRC32 to + protect against packet forgery. All these choices have proven to + be insufficient: key space is too small against current attacks, + RC4 key scheduling is insufficient (beginning of the pseudorandom + stream should be skipped), IV space is too small and IV reuse + makes attacks easier, there is no replay protection, and non-keyed + authentication does not protect against bit flipping packet + data.</para> + + <para>WPA is an intermediate solution for the security issues. It + uses Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP + is a compromise on strong security and possibility to use existing + hardware. It still uses RC4 for the encryption like WEP, but with + per-packet RC4 keys. In addition, it implements replay protection, + keyed packet authentication mechanism (Michael MIC).</para> + + <para>Keys can be managed using two different mechanisms. WPA can + either use an external authentication server (e.g., RADIUS) and + EAP just like IEEE 802.1X is using or pre-shared keys without need + for additional servers. Wi-Fi calls these "WPA-Enterprise" and + "WPA-Personal", respectively. Both mechanisms will generate a + master session key for the Authenticator (AP) and Supplicant + (client station).</para> + + <para>WPA implements a new key handshake (4-Way Handshake and + Group Key Handshake) for generating and exchanging data encryption + keys between the Authenticator and Supplicant. This handshake is + also used to verify that both Authenticator and Supplicant know + the master session key. These handshakes are identical regardless + of the selected key management mechanism (only the method for + generating master session key changes).</para> + </refsect1> + + <refsect1> + <title>IEEE 802.11i / WPA2</title> + + <para>The design for parts of IEEE 802.11i that were not included + in WPA has finished (May 2004) and this amendment to IEEE 802.11 + was approved in June 2004. Wi-Fi Alliance is using the final IEEE + 802.11i as a new version of WPA called WPA2. This includes, e.g., + support for more robust encryption algorithm (CCMP: AES in Counter + mode with CBC-MAC) to replace TKIP and optimizations for handoff + (reduced number of messages in initial key handshake, + pre-authentication, and PMKSA caching).</para> + </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-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml new file mode 100644 index 0000000..1fe98f4 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml @@ -0,0 +1,339 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_cli</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_cli</refname> + + <refpurpose>WPA command line client</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_cli</command> + <arg>-p <replaceable>path to ctrl sockets</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><replaceable>command ...</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para>wpa_cli is a text-based frontend program for interacting + with wpa_supplicant. It is used to query current status, change + configuration, trigger events, and request interactive user + input.</para> + + <para>wpa_cli can show the current authentication status, selected + security mode, dot11 and dot1x MIBs, etc. In addition, it can + configure some variables like EAPOL state machine parameters and + trigger events like reassociation and IEEE 802.1X + logoff/logon. wpa_cli provides a user interface to request + authentication information, like username and password, if these + are not included in the configuration. This can be used to + implement, e.g., one-time-passwords or generic token card + authentication where the authentication is based on a + challenge-response that uses an external device for generating the + response.</para> + + <para>The control interface of wpa_supplicant can be configured to + allow non-root user access (ctrl_interface GROUP= parameter in the + configuration file). This makes it possible to run wpa_cli with a + normal user account.</para> + + <para>wpa_cli supports two modes: interactive and command + line. Both modes share the same command set and the main + difference is in interactive mode providing access to unsolicited + messages (event messages, username/password requests).</para> + + <para>Interactive mode is started when wpa_cli is executed without + including the command as a command line parameter. Commands are + then entered on the wpa_cli prompt. In command line mode, the same + commands are entered as command line arguments for wpa_cli.</para> + </refsect1> + <refsect1> + <title>Interactive authentication parameters request</title> + + <para>When wpa_supplicant need authentication parameters, like + username and password, which are not present in the configuration + file, it sends a request message to all attached frontend programs, + e.g., wpa_cli in interactive mode. wpa_cli shows these requests + with "CTRL-REQ-<type>-<id>:<text>" + prefix. <type> is IDENTITY, PASSWORD, or OTP + (one-time-password). <id> is a unique identifier for the + current network. <text> is description of the request. In + case of OTP request, it includes the challenge from the + authentication server.</para> + + <para>The reply to these requests can be given with + <emphasis>identity</emphasis>, <emphasis>password</emphasis>, and + <emphasis>otp</emphasis> commands. <id> needs to be copied from + the matching request. <emphasis>password</emphasis> and + <emphasis>otp</emphasis> commands can be used regardless of whether + the request was for PASSWORD or OTP. The main difference between these + two commands is that values given with <emphasis>password</emphasis> are + remembered as long as wpa_supplicant is running whereas values given + with <emphasis>otp</emphasis> are used only once and then forgotten, + i.e., wpa_supplicant will ask frontend for a new value for every use. + This can be used to implement one-time-password lists and generic token + card -based authentication.</para> + + <para>Example request for password and a matching reply:</para> + +<blockquote><programlisting> +CTRL-REQ-PASSWORD-1:Password needed for SSID foobar +> password 1 mysecretpassword +</programlisting></blockquote> + + <para>Example request for generic token card challenge-response:</para> + +<blockquote><programlisting> +CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar +> otp 2 9876 +</programlisting></blockquote> + + </refsect1> + <refsect1> + <title>Command Arguments</title> + <variablelist> + <varlistentry> + <term>-p path</term> + + <listitem><para>Change the path where control sockets should + be found.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-i ifname</term> + + <listitem><para>Specify the interface that is being + configured. By default, choose the first interface found with + a control socket in the socket path.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-h</term> + <listitem><para>Help. Show a usage message.</para></listitem> + </varlistentry> + + + <varlistentry> + <term>-v</term> + <listitem><para>Show version information.</para></listitem> + </varlistentry> + + + <varlistentry> + <term>-B</term> + <listitem><para>Run as a daemon in the background.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-a file</term> + + <listitem><para>Run in daemon mode executing the action file + based on events from wpa_supplicant. The specified file will + be executed with the first argument set to interface name and + second to "CONNECTED" or "DISCONNECTED" depending on the event. + This can be used to execute networking tools required to configure + the interface.</para> + + <para>Additionally, three environmental variables are available to + the file: WPA_CTRL_DIR, WPA_ID, and WPA_ID_STR. WPA_CTRL_DIR + contains the absolute path to the ctrl_interface socket. WPA_ID + contains the unique network_id identifier assigned to the active + network, and WPA_ID_STR contains the content of the id_str option. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-P file</term> + + <listitem><para>Set the location of the PID + file.</para></listitem> + </varlistentry> + + <varlistentry> + <term>command</term> + + <listitem><para>Run a command. The available commands are + listed in the next section.</para></listitem> + + </varlistentry> + </variablelist> + </refsect1> + <refsect1> + <title>Commands</title> + <para>The following commands are available:</para> + + <variablelist> + <varlistentry> + <term>status</term> + <listitem> + <para>get current WPA/EAPOL/EAP status</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>mib</term> + <listitem> + <para>get MIB variables (dot1x, dot11)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>help</term> + <listitem> + <para>show this usage help</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>interface [ifname]</term> + <listitem> + <para>show interfaces/select interface</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>level <debug level></term> + <listitem> + <para>change debug level</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>license</term> + <listitem> + <para>show full wpa_cli license</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>logoff</term> + <listitem> + <para>IEEE 802.1X EAPOL state machine logoff</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>logon</term> + <listitem> + <para>IEEE 802.1X EAPOL state machine logon</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>set</term> + <listitem> + <para>set variables (shows list of variables when run without arguments)</para> + </listitem> + </varlistentry> + <varlistentry> + <term>pmksa</term> + <listitem> + <para>show PMKSA cache</para> + </listitem> + </varlistentry> + <varlistentry> + <term>reassociate</term> + <listitem> + <para>force reassociation</para> + </listitem> + </varlistentry> + <varlistentry> + <term>reconfigure</term> + <listitem> + <para>force wpa_supplicant to re-read its configuration file</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>preauthenticate <BSSID></term> + <listitem> + <para>force preauthentication</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>identity <network id> <identity></term> + <listitem> + <para>configure identity for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>password <network id> <password></term> + <listitem> + <para>configure password for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>pin <network id> <pin></term> + <listitem> + <para>configure pin for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>otp <network id> <password></term> + <listitem> + <para>configure one-time-password for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>bssid <network id> <BSSID></term> + <listitem> + <para>set preferred BSSID for an SSID</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>list_networks</term> + <listitem> + <para>list configured networks</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>terminate</term> + <listitem> + <para>terminate <command>wpa_supplicant</command></para> + </listitem> + </varlistentry> + + <varlistentry> + <term>quit</term> + <listitem><para>exit wpa_cli</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-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml new file mode 100644 index 0000000..41b5849 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml @@ -0,0 +1,85 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_gui</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_gui</refname> + + <refpurpose>WPA Graphical User Interface</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_gui</command> + <arg>-p <replaceable>path to ctrl sockets</replaceable></arg> + <arg>-i <replaceable>ifname</replaceable></arg> + <arg>-t</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para>wpa_gui is a QT graphical frontend program for interacting + with wpa_supplicant. It is used to query current status, change + configuration and request interactive user input.</para> + + <para>wpa_gui supports (almost) all of the interactive status and + configuration features of the command line client, wpa_cli. Refer + to the wpa_cli manpage for a comprehensive list of the + interactive mode features.</para> + </refsect1> + <refsect1> + <title>Command Arguments</title> + <variablelist> + <varlistentry> + <term>-p path</term> + + <listitem><para>Change the path where control sockets should + be found.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-i ifname</term> + + <listitem><para>Specify the interface that is being + configured. By default, choose the first interface found with + a control socket in the socket path.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-t</term> + + <listitem><para>Start program in the system tray only (if the window + manager supports it). By default the main status window is + shown.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_cli</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml new file mode 100644 index 0000000..402ea09 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml @@ -0,0 +1,73 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_passphrase</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_passphrase</refname> + <refpurpose>Generate a WPA PSK from an ASCII passphrase for a SSID</refpurpose> + </refnamediv> + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_passphrase</command> + <arg><replaceable>ssid</replaceable></arg> + <arg><replaceable>passphrase</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para><command>wpa_passphrase</command> pre-computes PSK entries for + network configuration blocks of a + <filename>wpa_supplicant.conf</filename> file. An ASCII passphrase + and SSID are used to generate a 256-bit PSK.</para> + </refsect1> + + <refsect1> + <title>Options</title> + <variablelist> + <varlistentry> + <term>ssid</term> + <listitem> + <para>The SSID whose passphrase should be derived.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>passphrase</term> + <listitem> + <para>The passphrase to use. If not included on the command line, + passphrase will be read from standard input.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml new file mode 100644 index 0000000..89b8a92 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml @@ -0,0 +1,148 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_priv</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_priv</refname> + + <refpurpose>wpa_supplicant privilege separation helper</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_priv</command> + <arg>-c <replaceable>ctrl path</replaceable></arg> + <arg>-Bdd</arg> + <arg>-P <replaceable>pid file</replaceable></arg> + <arg>driver:ifname <replaceable>[driver:ifname ...]</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para><command>wpa_priv</command> is a privilege separation helper that + minimizes the size of <command>wpa_supplicant</command> code that needs + to be run with root privileges.</para> + + <para>If enabled, privileged operations are done in the wpa_priv process + while leaving rest of the code (e.g., EAP authentication and WPA + handshakes) to operate in an unprivileged process (wpa_supplicant) that + can be run as non-root user. Privilege separation restricts the effects + of potential software errors by containing the majority of the code in an + unprivileged process to avoid the possibility of a full system + compromise.</para> + + <para><command>wpa_priv</command> needs to be run with network admin + privileges (usually, root user). It opens a UNIX domain socket for each + interface that is included on the command line; any other interface will + be off limits for <command>wpa_supplicant</command> in this kind of + configuration. After this, <command>wpa_supplicant</command> can be run as + a non-root user (e.g., all standard users on a laptop or as a special + non-privileged user account created just for this purpose to limit access + to user files even further).</para> + </refsect1> + <refsect1> + <title>Example configuration</title> + + <para>The following steps are an example of how to configure + <command>wpa_priv</command> to allow users in the + <emphasis>wpapriv</emphasis> group to communicate with + <command>wpa_supplicant</command> with privilege separation:</para> + + <para>Create user group (e.g., wpapriv) and assign users that + should be able to use wpa_supplicant into that group.</para> + + <para>Create /var/run/wpa_priv directory for UNIX domain sockets and + control user access by setting it accessible only for the wpapriv + group:</para> + +<blockquote><programlisting> +mkdir /var/run/wpa_priv +chown root:wpapriv /var/run/wpa_priv +chmod 0750 /var/run/wpa_priv +</programlisting></blockquote> + + <para>Start <command>wpa_priv</command> as root (e.g., from system + startup scripts) with the enabled interfaces configured on the + command line:</para> + +<blockquote><programlisting> +wpa_priv -B -c /var/run/wpa_priv -P /var/run/wpa_priv.pid wext:wlan0 +</programlisting></blockquote> + + <para>Run <command>wpa_supplicant</command> as non-root with a user + that is in the wpapriv group:</para> + +<blockquote><programlisting> +wpa_supplicant -i ath0 -c wpa_supplicant.conf +</programlisting></blockquote> + + </refsect1> + <refsect1> + <title>Command Arguments</title> + <variablelist> + <varlistentry> + <term>-c ctrl path</term> + + <listitem><para>Specify the path to wpa_priv control directory + (Default: /var/run/wpa_priv/).</para></listitem> + </varlistentry> + + <varlistentry> + <term>-B</term> + <listitem><para>Run as a daemon in the background.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-P file</term> + + <listitem><para>Set the location of the PID + file.</para></listitem> + </varlistentry> + + <varlistentry> + <term>driver:ifname [driver:ifname ...]</term> + + <listitem><para>The <driver> string dictates which of the + supported <command>wpa_supplicant</command> driver backends is to be + used. To get a list of supported driver types see wpa_supplicant help + (e.g, wpa_supplicant -h). The driver backend supported by most good + drivers is <emphasis>wext</emphasis>.</para> + + <para>The <ifname> string specifies which network + interface is to be managed by <command>wpa_supplicant</command> + (e.g., wlan0 or ath0).</para> + + <para><command>wpa_priv</command> does not use the network interface + before <command>wpa_supplicant</command> is started, so it is fine to + include network interfaces that are not available at the time wpa_priv + is started. wpa_priv can control multiple interfaces with one process, + but it is also possible to run multiple <command>wpa_priv</command> + processes at the same time, if desired.</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-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml new file mode 100644 index 0000000..462039d --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml @@ -0,0 +1,239 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> +<refentry> + <refmeta> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_supplicant.conf</refname> + <refpurpose>configuration file for wpa_supplicant</refpurpose> + </refnamediv> + <refsect1> + <title>Overview</title> + + <para><command>wpa_supplicant</command> is configured using a text + file that lists all accepted networks and security policies, + including pre-shared keys. See the example configuration file, + probably in <command>/usr/share/doc/wpa_supplicant/</command>, for + detailed information about the configuration format and supported + fields.</para> + + <para>All file paths in this configuration file should use full + (absolute, not relative to working directory) path in order to allow + working directory to be changed. This can happen if wpa_supplicant is + run in the background.</para> + + <para>Changes to configuration file can be reloaded be sending + SIGHUP signal to <command>wpa_supplicant</command> ('killall -HUP + wpa_supplicant'). Similarly, reloading can be triggered with + the <emphasis>wpa_cli reconfigure</emphasis> command.</para> + + <para>Configuration file can include one or more network blocks, + e.g., one for each used SSID. wpa_supplicant will automatically + select the best network based on the order of network blocks in + the configuration file, network security level (WPA/WPA2 is + preferred), and signal strength.</para> + </refsect1> + + <refsect1> + <title>Quick Examples</title> + + <orderedlist> + <listitem> + + <para>WPA-Personal (PSK) as home network and WPA-Enterprise with + EAP-TLS as work network.</para> + +<blockquote><programlisting> +# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +# +# home network; allow all valid ciphers +network={ + ssid="home" + scan_ssid=1 + key_mgmt=WPA-PSK + psk="very secret passphrase" +} +# +# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers +network={ + ssid="work" + scan_ssid=1 + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" +} +</programlisting></blockquote> + </listitem> + + <listitem> + <para>WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that + use old peaplabel (e.g., Funk Odyssey and SBR, Meetinghouse + Aegis, Interlink RAD-Series)</para> + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=0" + phase2="auth=MSCHAPV2" +} +</programlisting></blockquote> + </listitem> + + <listitem> + <para>EAP-TTLS/EAP-MD5-Challenge configuration with anonymous + identity for the unencrypted use. Real identity is sent only + within an encrypted TLS tunnel.</para> + + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MD5" +} +</programlisting></blockquote> + + </listitem> + + <listitem> + <para>IEEE 802.1X (i.e., no WPA) with dynamic WEP keys + (require both unicast and broadcast); use EAP-TLS for + authentication</para> + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="1x-test" + scan_ssid=1 + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} +</programlisting></blockquote> + </listitem> + + + <listitem> + <para>Catch all example that allows more or less all + configuration modes. The configuration options are used based + on what security policy is used in the selected SSID. This is + mostly for testing and is not recommended for normal + use.</para> + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" +} +</programlisting></blockquote> + </listitem> + + <listitem> + <para>Authentication for wired Ethernet. This can be used with + <emphasis>wired</emphasis> or <emphasis>roboswitch</emphasis> interface + (-Dwired or -Droboswitch on command line).</para> + +<blockquote><programlisting> +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel +ap_scan=0 +network={ + key_mgmt=IEEE8021X + eap=MD5 + identity="user" + password="password" + eapol_flags=0 +} +</programlisting></blockquote> + </listitem> + </orderedlist> + + + + + + </refsect1> + <refsect1> + <title>Certificates</title> + + <para>Some EAP authentication methods require use of + certificates. EAP-TLS uses both server side and client + certificates whereas EAP-PEAP and EAP-TTLS only require the server + side certificate. When client certificate is used, a matching + private key file has to also be included in configuration. If the + private key uses a passphrase, this has to be configured in + wpa_supplicant.conf ("private_key_passwd").</para> + + <para>wpa_supplicant supports X.509 certificates in PEM and DER + formats. User certificate and private key can be included in the + same file.</para> + + <para>If the user certificate and private key is received in + PKCS#12/PFX format, they need to be converted to suitable PEM/DER + format for wpa_supplicant. This can be done, e.g., with following + commands:</para> +<blockquote><programlisting> +# convert client certificate and private key to PEM format +openssl pkcs12 -in example.pfx -out user.pem -clcerts +# convert CA certificate (if included in PFX file) to PEM format +openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys +</programlisting></blockquote> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>openssl</refentrytitle> + <manvolnum>1</manvolnum> + </citerefentry> + </para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml new file mode 100644 index 0000000..0ab4e15 --- /dev/null +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -0,0 +1,799 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>wpa_supplicant</refname> + <refpurpose>Wi-Fi Protected Access client and IEEE 802.1X supplicant</refpurpose> + </refnamediv> + <refsynopsisdiv> + <cmdsynopsis> + <command>wpa_supplicant</command> + <arg>-BddfhKLqqtuvW</arg> + <arg>-i<replaceable>ifname</replaceable></arg> + <arg>-c<replaceable>config file</replaceable></arg> + <arg>-D<replaceable>driver</replaceable></arg> + <arg>-P<replaceable>PID_file</replaceable></arg> + <arg>-f<replaceable>output file</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + <refsect1> + <title>Overview</title> + + <para> + Wireless networks do not require physical access to the network equipment + in the same way as wired networks. This makes it easier for unauthorized + users to passively monitor a network and capture all transmitted frames. + In addition, unauthorized use of the network is much easier. In many cases, + this can happen even without user's explicit knowledge since the wireless + LAN adapter may have been configured to automatically join any available + network. + </para> + + <para> + Link-layer encryption can be used to provide a layer of security for + wireless networks. The original wireless LAN standard, IEEE 802.11, + included a simple encryption mechanism, WEP. However, that proved to + be flawed in many areas and network protected with WEP cannot be consider + secure. IEEE 802.1X authentication and frequently changed dynamic WEP keys + can be used to improve the network security, but even that has inherited + security issues due to the use of WEP for encryption. Wi-Fi Protected + Access and IEEE 802.11i amendment to the wireless LAN standard introduce + a much improvement mechanism for securing wireless networks. IEEE 802.11i + enabled networks that are using CCMP (encryption mechanism based on strong + cryptographic algorithm AES) can finally be called secure used for + applications which require efficient protection against unauthorized + access. + </para> + + <para><command>wpa_supplicant</command> is an implementation of + the WPA Supplicant component, i.e., the part that runs in the + client stations. It implements WPA key negotiation with a WPA + Authenticator and EAP authentication with Authentication + Server. In addition, it controls the roaming and IEEE 802.11 + authentication/association of the wireless LAN driver.</para> + + <para><command>wpa_supplicant</command> is designed to be a + "daemon" program that runs in the background and acts as the + backend component controlling the wireless + connection. <command>wpa_supplicant</command> supports separate + frontend programs and an example text-based frontend, + <command>wpa_cli</command>, is included with + wpa_supplicant.</para> + + <para>Before wpa_supplicant can do its work, the network interface + must be available. That means that the physical device must be + present and enabled, and the driver for the device must be + loaded. The daemon will exit immediately if the device is not already + available.</para> + + <para>After <command>wpa_supplicant</command> has configured the + network device, higher level configuration such as DHCP may + proceed. There are a variety of ways to integrate wpa_supplicant + into a machine's networking scripts, a few of which are described + in sections below.</para> + + <para>The following steps are used when associating with an AP + using WPA:</para> + + <itemizedlist> + <listitem> + <para><command>wpa_supplicant</command> requests the kernel + driver to scan neighboring BSSes</para> + </listitem> + + <listitem> + <para><command>wpa_supplicant</command> selects a BSS based on + its configuration</para> + </listitem> + + <listitem> + <para><command>wpa_supplicant</command> requests the kernel + driver to associate with the chosen BSS</para> + </listitem> + + <listitem> + <para>If WPA-EAP: integrated IEEE 802.1X Supplicant + completes EAP authentication with the + authentication server (proxied by the Authenticator in the + AP)</para> + </listitem> + + <listitem> + <para>If WPA-EAP: master key is received from the IEEE 802.1X + Supplicant</para> + </listitem> + + <listitem> + <para>If WPA-PSK: <command>wpa_supplicant</command> uses PSK + as the master session key</para> + </listitem> + + <listitem> + <para><command>wpa_supplicant</command> completes WPA 4-Way + Handshake and Group Key Handshake with the Authenticator + (AP)</para> + </listitem> + + <listitem> + <para><command>wpa_supplicant</command> configures encryption + keys for unicast and broadcast</para> + </listitem> + + <listitem> + <para>normal data packets can be transmitted and received</para> + </listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Supported Features</title> + <para>Supported WPA/IEEE 802.11i features:</para> + <itemizedlist> + <listitem> + <para>WPA-PSK ("WPA-Personal")</para> + </listitem> + + <listitem> + <para>WPA with EAP (e.g., with RADIUS authentication server) + ("WPA-Enterprise") Following authentication methods are + supported with an integrate IEEE 802.1X Supplicant:</para> + + <itemizedlist> + <listitem> + <para>EAP-TLS</para> + </listitem> + </itemizedlist> + + <itemizedlist> + <listitem> + <para>EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)</para> + </listitem> + + + <listitem> + <para>EAP-PEAP/TLS (both PEAPv0 and PEAPv1)</para> + </listitem> + + <listitem> + <para>EAP-PEAP/GTC (both PEAPv0 and PEAPv1)</para> + </listitem> + + <listitem> + <para>EAP-PEAP/OTP (both PEAPv0 and PEAPv1)</para> + </listitem> + + <listitem> + <para>EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)</para> + </listitem> + + <listitem> + <para>EAP-TTLS/EAP-MD5-Challenge</para> + </listitem> + + <listitem> + <para>EAP-TTLS/EAP-GTC</para> + </listitem> + + <listitem><para>EAP-TTLS/EAP-OTP</para></listitem> + + <listitem><para>EAP-TTLS/EAP-MSCHAPv2</para></listitem> + + <listitem><para>EAP-TTLS/EAP-TLS</para></listitem> + + <listitem><para>EAP-TTLS/MSCHAPv2</para></listitem> + + <listitem><para>EAP-TTLS/MSCHAP</para></listitem> + + <listitem><para>EAP-TTLS/PAP</para></listitem> + + <listitem><para>EAP-TTLS/CHAP</para></listitem> + + <listitem><para>EAP-SIM</para></listitem> + + <listitem><para>EAP-AKA</para></listitem> + + <listitem><para>EAP-PSK</para></listitem> + + <listitem><para>EAP-PAX</para></listitem> + + <listitem><para>LEAP (note: requires special support from + the driver for IEEE 802.11 authentication)</para></listitem> + + <listitem><para>(following methods are supported, but since + they do not generate keying material, they cannot be used + with WPA or IEEE 802.1X WEP keying)</para></listitem> + + <listitem><para>EAP-MD5-Challenge </para></listitem> + + <listitem><para>EAP-MSCHAPv2</para></listitem> + + <listitem><para>EAP-GTC</para></listitem> + + <listitem><para>EAP-OTP</para></listitem> + </itemizedlist> + </listitem> + + <listitem> + <para>key management for CCMP, TKIP, WEP104, WEP40</para> + </listitem> + + <listitem> + <para>RSN/WPA2 (IEEE 802.11i)</para> + <itemizedlist> + <listitem> + <para>pre-authentication</para> + </listitem> + + <listitem> + <para>PMKSA caching</para> + </listitem> + </itemizedlist> + </listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Available Drivers</title> + <para>A summary of available driver backends is below. Support for each + of the driver backends is chosen at wpa_supplicant compile time. For a + list of supported driver backends that may be used with the -D option on + your system, refer to the help output of wpa_supplicant + (<emphasis>wpa_supplicant -h</emphasis>).</para> + + <variablelist> + <varlistentry> + <term>hostap</term> + <listitem> + <para>(default) Host AP driver (Intersil Prism2/2.5/3). + (this can also be used with Linuxant DriverLoader).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>hermes</term> + <listitem> + <para>Agere Systems Inc. driver (Hermes-I/Hermes-II).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>madwifi</term> + <listitem> + <para>MADWIFI 802.11 support (Atheros, etc.).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>wext</term> + <listitem> + <para>Linux wireless extensions (generic).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>broadcom</term> + <listitem> + <para>Broadcom wl.o driver.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>wired</term> + <listitem> + <para>wpa_supplicant wired Ethernet driver</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>roboswitch</term> + <listitem> + <para>wpa_supplicant Broadcom switch driver</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>bsd</term> + <listitem> + <para>BSD 802.11 support (Atheros, etc.).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>ndis</term> + <listitem> + <para>Windows NDIS driver.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Command Line Options</title> + <para>Most command line options have global scope. Some are given per + interface, and are only valid if at least one <option>-i</option> option + is specified, otherwise they're ignored. Option groups for different + interfaces must be separated by <option>-N</option> option.</para> + <variablelist> + <varlistentry> + <term>-b br_ifname</term> + <listitem> + <para>Optional bridge interface name. (Per interface)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-B</term> + <listitem> + <para>Run daemon in the background.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-c filename</term> + <listitem> + <para>Path to configuration file. (Per interface)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-C ctrl_interface</term> + <listitem> + <para>Path to ctrl_interface socket (Per interface. Only used if + <option>-c</option> is not).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-i ifname</term> + <listitem> + <para>Interface to listen on. Multiple instances of this option can + be present, one per interface, separated by <option>-N</option> + option (see below).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-d</term> + <listitem> + <para>Increase debugging verbosity (<option>-dd</option> even + more).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-D driver</term> + <listitem> + <para>Driver to use (can be multiple drivers: nl80211,wext). + (Per interface, see the available options below.)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-f output file</term> + <listitem> + <para>Log output to specified file instead of stdout.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-g global ctrl_interface</term> + <listitem> + <para>Path to global ctrl_interface socket. If specified, interface + definitions may be omitted.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-K</term> + <listitem> + <para>Include keys (passwords, etc.) in debug output.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-t</term> + <listitem> + <para>Include timestamp in debug messages.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-h</term> + <listitem> + <para>Help. Show a usage message.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-L</term> + <listitem> + <para>Show license (GPL and BSD).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-p</term> + <listitem> + <para>Driver parameters. (Per interface)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-P PID_file</term> + <listitem> + <para>Path to PID file.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-q</term> + <listitem> + <para>Decrease debugging verbosity (<option>-qq</option> even + less).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-u</term> + <listitem> + <para>Enabled DBus control interface. If enabled, interface + definitions may be omitted.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-v</term> + <listitem> + <para>Show version.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-W</term> + <listitem> + <para>Wait for a control interface monitor before starting.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-N</term> + <listitem> + <para>Start describing new interface.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para>In most common cases, <command>wpa_supplicant</command> is + started with:</para> + +<blockquote><programlisting> +wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0 +</programlisting></blockquote> + + <para>This makes the process fork into background.</para> + + <para>The easiest way to debug problems, and to get debug log for + bug reports, is to start <command>wpa_supplicant</command> on + foreground with debugging enabled:</para> + +<blockquote><programlisting> +wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d +</programlisting></blockquote> + + <para>If the specific driver wrapper is not known beforehand, it is + possible to specify multiple comma separated driver wrappers on the command + line. <command>wpa_supplicant</command> will use the first driver + wrapper that is able to initialize the interface.</para> + +<blockquote><programlisting> +wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0 +</programlisting></blockquote> + + <para><command>wpa_supplicant</command> can control multiple + interfaces (radios) either by running one process for each + interface separately or by running just one process and list of + options at command line. Each interface is separated with -N + argument. As an example, following command would start + wpa_supplicant for two interfaces:</para> + +<blockquote><programlisting> +wpa_supplicant \ + -c wpa1.conf -i wlan0 -D hostap -N \ + -c wpa2.conf -i ath0 -D madwifi +</programlisting></blockquote> + </refsect1> + + <refsect1> + <title>OS Requirements</title> + <para>Current hardware/software requirements:</para> + + <itemizedlist> + <listitem> + <para>Linux kernel 2.4.x or 2.6.x with Linux Wireless + Extensions v15 or newer</para> + </listitem> + + + <listitem> + <para>FreeBSD 6-CURRENT</para> + </listitem> + + <listitem> + <para>Microsoft Windows with WinPcap (at least WinXP, may work + with other versions)</para> + </listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Supported Drivers</title> + <variablelist> + <varlistentry> + <term>Host AP driver for Prism2/2.5/3 (development + snapshot/v0.2.x)</term> + <listitem> + <para> (http://hostap.epitest.fi/) Driver needs to be set in + Managed mode (<emphasis>iwconfig wlan0 mode managed</emphasis>). + Please note that station firmware version needs to be 1.7.0 or + newer to work in WPA mode.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Linuxant DriverLoader</term> + <listitem> + <para>(http://www.linuxant.com/driverloader/) + with Windows NDIS driver for your wlan card supporting WPA.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Agere Systems Inc. Linux Driver</term> + <listitem> + <para> (http://www.agere.com/support/drivers/) Please note + that the driver interface file (driver_hermes.c) and hardware + specific include files are not included in the wpa_supplicant + distribution. You will need to copy these from the source + package of the Agere driver.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>madwifi driver for cards based on Atheros chip set (ar521x)</term> + <listitem> + <para> (http://sourceforge.net/projects/madwifi/) Please + note that you will need to modify the wpa_supplicant .config + file to use the correct path for the madwifi driver root + directory (CFLAGS += -I../madwifi/wpa line in example + defconfig).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Linux ndiswrapper</term> + <listitem> + <para> (http://ndiswrapper.sourceforge.net/) with Windows + NDIS driver.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Broadcom wl.o driver</term> + <listitem> + <para> This is a generic Linux driver for Broadcom IEEE + 802.11a/g cards. However, it is proprietary driver that is + not publicly available except for couple of exceptions, mainly + Broadcom-based APs/wireless routers that use Linux. The driver + binary can be downloaded, e.g., from Linksys support site + (http://www.linksys.com/support/gpl.asp) for Linksys + WRT54G. The GPL tarball includes cross-compiler and the needed + header file, wlioctl.h, for compiling wpa_supplicant. This + driver support in wpa_supplicant is expected to work also with + other devices based on Broadcom driver (assuming the driver + includes client mode support).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term> Intel ipw2100 driver</term> + <listitem> + <para> (http://sourceforge.net/projects/ipw2100/)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Intel ipw2200 driver</term> + <listitem> + <para> (http://sourceforge.net/projects/ipw2200/)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Linux wireless extensions</term> + <listitem> + <para>In theory, any driver that supports Linux wireless + extensions can be used with IEEE 802.1X (i.e., not WPA) when + using ap_scan=0 option in configuration file.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Wired Ethernet drivers</term> + <listitem> + <para>Use ap_scan=0.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>BSD net80211 layer (e.g., Atheros driver)</term> + <listitem> + <para>At the moment, this is for FreeBSD 6-CURRENT branch.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Windows NDIS</term> + <listitem> + <para>The current Windows port requires WinPcap + (http://winpcap.polito.it/). See README-Windows.txt for more + information.</para> + </listitem> + </varlistentry> + </variablelist> + + + <para>wpa_supplicant was designed to be portable for different + drivers and operating systems. Hopefully, support for more wlan + cards and OSes will be added in the future. See developer.txt for + more information about the design of wpa_supplicant and porting to + other drivers. One main goal is to add full WPA/WPA2 support to + Linux wireless extensions to allow new drivers to be supported + without having to implement new driver-specific interface code in + wpa_supplicant.</para> + </refsect1> + + <refsect1> + <title>Architecture</title> <para>The + <command>wpa_supplicant</command> system consists of the following + components:</para> + + <variablelist> + <varlistentry> + <term><filename>wpa_supplicant.conf</filename> </term> + <listitem> + <para>the configuration file describing all networks that the + user wants the computer to connect to. </para> + </listitem> + </varlistentry> + <varlistentry> + <term><command>wpa_supplicant</command></term> + <listitem><para>the program that directly interacts with the + network interface. </para></listitem> + </varlistentry> + <varlistentry> + <term><command>wpa_cli</command></term> <listitem><para> the + client program that provides a high-level interface to the + functionality of the daemon. </para></listitem> + </varlistentry> + <varlistentry> + <term><command>wpa_passphrase</command></term> + <listitem><para>a utility needed to construct + <filename>wpa_supplicant.conf</filename> files that include + encrypted passwords.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Quick Start</title> + + <para>First, make a configuration file, e.g. + <filename>/etc/wpa_supplicant.conf</filename>, that describes the networks + you are interested in. See <citerefentry> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + for details.</para> + + <para>Once the configuration is ready, you can test whether the + configuration works by running <command>wpa_supplicant</command> + with following command to start it on foreground with debugging + enabled:</para> + + <blockquote><programlisting> +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d + </programlisting></blockquote> + + <para>Assuming everything goes fine, you can start using following + command to start <command>wpa_supplicant</command> on background + without debugging:</para> + + <blockquote><programlisting> +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B + </programlisting></blockquote> + + <para>Please note that if you included more than one driver + interface in the build time configuration (.config), you may need + to specify which interface to use by including -D<driver + name> option on the command line.</para> + + <!-- XXX at this point, the page could include a little script + based on wpa_cli to wait for a connection and then run + dhclient --> + + </refsect1> + + <refsect1> + <title>Interface to pcmcia-cs/cardmrg</title> + + <para>For example, following small changes to pcmcia-cs scripts + can be used to enable WPA support:</para> + + <para>Add MODE="Managed" and WPA="y" to the network scheme in + <filename>/etc/pcmcia/wireless.opts</filename>.</para> + + <para>Add the following block to the end of <emphasis>start</emphasis> + action handler in <filename>/etc/pcmcia/wireless</filename>:</para> + + <blockquote><programlisting> +if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf -i$DEVICE +fi + </programlisting></blockquote> + + + <para>Add the following block to the end of <emphasis>stop</emphasis> + action handler (may need to be separated from other actions) in + <filename>/etc/pcmcia/wireless</filename>:</para> + + <blockquote><programlisting> +if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + killall wpa_supplicant +fi + </programlisting></blockquote> + + <para>This will make <command>cardmgr</command> start + <command>wpa_supplicant</command> when the card is plugged + in.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_background</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_cli</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + <citerefentry> + <refentrytitle>wpa_passphrase</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2007, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is dual-licensed under both the GPL version 2 + and BSD license. Either license may be used at your option.</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h new file mode 100644 index 0000000..0d436dd --- /dev/null +++ b/wpa_supplicant/driver_i.h @@ -0,0 +1,707 @@ +/* + * wpa_supplicant - Internal driver interface wrappers + * Copyright (c) 2003-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. + */ + +#ifndef DRIVER_I_H +#define DRIVER_I_H + +#include "drivers/driver.h" + +/* driver_ops */ +static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s, + const char *ifname) +{ + if (wpa_s->driver->init2) + return wpa_s->driver->init2(wpa_s, ifname, + wpa_s->global_drv_priv); + if (wpa_s->driver->init) { + return wpa_s->driver->init(wpa_s, ifname); + } + return NULL; +} + +static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->deinit) + wpa_s->driver->deinit(wpa_s->drv_priv); +} + +static inline int wpa_drv_set_param(struct wpa_supplicant *wpa_s, + const char *param) +{ + if (wpa_s->driver->set_param) + return wpa_s->driver->set_param(wpa_s->drv_priv, param); + return 0; +} + +static inline int wpa_drv_set_countermeasures(struct wpa_supplicant *wpa_s, + int enabled) +{ + if (wpa_s->driver->set_countermeasures) { + return wpa_s->driver->set_countermeasures(wpa_s->drv_priv, + enabled); + } + return -1; +} + +static inline int wpa_drv_authenticate(struct wpa_supplicant *wpa_s, + struct wpa_driver_auth_params *params) +{ + if (wpa_s->driver->authenticate) + return wpa_s->driver->authenticate(wpa_s->drv_priv, params); + return -1; +} + +static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params) +{ + if (wpa_s->driver->associate) { + return wpa_s->driver->associate(wpa_s->drv_priv, params); + } + return -1; +} + +static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + if (wpa_s->driver->scan2) + return wpa_s->driver->scan2(wpa_s->drv_priv, params); + return -1; +} + +static inline struct wpa_scan_results * wpa_drv_get_scan_results2( + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->get_scan_results2) + return wpa_s->driver->get_scan_results2(wpa_s->drv_priv); + return NULL; +} + +static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid) +{ + if (wpa_s->driver->get_bssid) { + return wpa_s->driver->get_bssid(wpa_s->drv_priv, bssid); + } + return -1; +} + +static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid) +{ + if (wpa_s->driver->get_ssid) { + return wpa_s->driver->get_ssid(wpa_s->drv_priv, ssid); + } + return -1; +} + +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) +{ + 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); + } + return -1; +} + +static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) +{ + if (wpa_s->driver->deauthenticate) { + return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr, + reason_code); + } + return -1; +} + +static inline int wpa_drv_disassociate(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) +{ + if (wpa_s->driver->disassociate) { + return wpa_s->driver->disassociate(wpa_s->drv_priv, addr, + reason_code); + } + return -1; +} + +static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + if (wpa_s->driver->add_pmkid) { + return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid); + } + return -1; +} + +static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + if (wpa_s->driver->remove_pmkid) { + return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid, + pmkid); + } + return -1; +} + +static inline int wpa_drv_flush_pmkid(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->flush_pmkid) { + return wpa_s->driver->flush_pmkid(wpa_s->drv_priv); + } + return -1; +} + +static inline int wpa_drv_get_capa(struct wpa_supplicant *wpa_s, + struct wpa_driver_capa *capa) +{ + if (wpa_s->driver->get_capa) { + return wpa_s->driver->get_capa(wpa_s->drv_priv, capa); + } + return -1; +} + +static inline void wpa_drv_poll(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->poll) { + wpa_s->driver->poll(wpa_s->drv_priv); + } +} + +static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->get_ifname) { + return wpa_s->driver->get_ifname(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) { + return wpa_s->driver->get_mac_addr(wpa_s->drv_priv); + } + return NULL; +} + +static inline int wpa_drv_send_eapol(struct wpa_supplicant *wpa_s, + const u8 *dst, u16 proto, + const u8 *data, size_t data_len) +{ + if (wpa_s->driver->send_eapol) + return wpa_s->driver->send_eapol(wpa_s->drv_priv, dst, proto, + data, data_len); + return -1; +} + +static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s, + int state) +{ + if (wpa_s->driver->set_operstate) + return wpa_s->driver->set_operstate(wpa_s->drv_priv, state); + return 0; +} + +static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s, + const u8 *addr, int protect_type, + int key_type) +{ + if (wpa_s->driver->mlme_setprotection) + return wpa_s->driver->mlme_setprotection(wpa_s->drv_priv, addr, + protect_type, + key_type); + return 0; +} + +static inline struct hostapd_hw_modes * +wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes, + u16 *flags) +{ + if (wpa_s->driver->get_hw_feature_data) + return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv, + num_modes, flags); + return NULL; +} + +static inline int wpa_drv_set_channel(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode phymode, int chan, + int freq) +{ + if (wpa_s->driver->set_channel) + return wpa_s->driver->set_channel(wpa_s->drv_priv, phymode, + chan, freq); + return -1; +} + +static inline int wpa_drv_set_ssid(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len) +{ + if (wpa_s->driver->set_ssid) { + return wpa_s->driver->set_ssid(wpa_s->drv_priv, ssid, + ssid_len); + } + return -1; +} + +static inline int wpa_drv_set_bssid(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + if (wpa_s->driver->set_bssid) { + return wpa_s->driver->set_bssid(wpa_s->drv_priv, bssid); + } + return -1; +} + +static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s, + const char *alpha2) +{ + if (wpa_s->driver->set_country) + return wpa_s->driver->set_country(wpa_s->drv_priv, alpha2); + return 0; +} + +static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, + const u8 *data, size_t data_len) +{ + if (wpa_s->driver->send_mlme) + return wpa_s->driver->send_mlme(wpa_s->drv_priv, + data, data_len); + return -1; +} + +static inline int wpa_drv_mlme_add_sta(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *supp_rates, + size_t supp_rates_len) +{ + if (wpa_s->driver->mlme_add_sta) + return wpa_s->driver->mlme_add_sta(wpa_s->drv_priv, addr, + supp_rates, supp_rates_len); + return -1; +} + +static inline int wpa_drv_mlme_remove_sta(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (wpa_s->driver->mlme_remove_sta) + return wpa_s->driver->mlme_remove_sta(wpa_s->drv_priv, addr); + return -1; +} + +static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s, + const u8 *md, + const u8 *ies, size_t ies_len) +{ + if (wpa_s->driver->update_ft_ies) + return wpa_s->driver->update_ft_ies(wpa_s->drv_priv, md, + ies, ies_len); + return -1; +} + +static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s, + u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + if (wpa_s->driver->send_ft_action) + return wpa_s->driver->send_ft_action(wpa_s->drv_priv, action, + target_ap, ies, ies_len); + return -1; +} + +static inline int wpa_drv_set_beacon(struct wpa_supplicant *wpa_s, + const u8 *head, size_t head_len, + const u8 *tail, size_t tail_len, + int dtim_period, int beacon_int) +{ + if (wpa_s->driver->set_beacon) + return wpa_s->driver->set_beacon(wpa_s->drv_priv, head, + head_len, tail, tail_len, + dtim_period, beacon_int); + return -1; +} + +static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s, + struct hostapd_sta_add_params *params) +{ + if (wpa_s->driver->sta_add) + return wpa_s->driver->sta_add(wpa_s->drv_priv, params); + return -1; +} + +static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (wpa_s->driver->sta_remove) + return wpa_s->driver->sta_remove(wpa_s->drv_priv, addr); + return -1; +} + +static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + const u8 *own_addr, u32 flags) +{ + if (wpa_s->driver->hapd_send_eapol) + return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr, + data, data_len, encrypt, + own_addr, flags); + return -1; +} + +static inline int wpa_drv_sta_set_flags(struct wpa_supplicant *wpa_s, + const u8 *addr, int total_flags, + int flags_or, int flags_and) +{ + if (wpa_s->driver->sta_set_flags) + return wpa_s->driver->sta_set_flags(wpa_s->drv_priv, addr, + total_flags, flags_or, + flags_and); + return -1; +} + +static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s, + int authorized) +{ + if (wpa_s->driver->set_supp_port) { + return wpa_s->driver->set_supp_port(wpa_s->drv_priv, + authorized); + } + return 0; +} + +static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s, + unsigned int freq, + unsigned int wait, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len) +{ + if (wpa_s->driver->send_action) + return wpa_s->driver->send_action(wpa_s->drv_priv, freq, + wait, dst, src, bssid, + data, data_len); + return -1; +} + +static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->send_action_cancel_wait) + wpa_s->driver->send_action_cancel_wait(wpa_s->drv_priv); +} + +static inline int wpa_drv_set_freq(struct wpa_supplicant *wpa_s, + struct hostapd_freq_params *freq) +{ + if (wpa_s->driver->set_freq) + return wpa_s->driver->set_freq(wpa_s->drv_priv, freq); + return -1; +} + +static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, + void *bss_ctx, char *force_ifname, + u8 *if_addr, const char *bridge) +{ + 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); + return -1; +} + +static inline int wpa_drv_if_remove(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type, + const char *ifname) +{ + if (wpa_s->driver->if_remove) + return wpa_s->driver->if_remove(wpa_s->drv_priv, type, ifname); + return -1; +} + +static inline int wpa_drv_set_intra_bss(struct wpa_supplicant *wpa_s, + int enabled) +{ + if (wpa_s->driver->set_intra_bss) + return wpa_s->driver->set_intra_bss(wpa_s->drv_priv, enabled); + return -1; +} + +static inline int wpa_drv_remain_on_channel(struct wpa_supplicant *wpa_s, + unsigned int freq, + unsigned int duration) +{ + if (wpa_s->driver->remain_on_channel) + return wpa_s->driver->remain_on_channel(wpa_s->drv_priv, freq, + duration); + return -1; +} + +static inline int wpa_drv_cancel_remain_on_channel( + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->cancel_remain_on_channel) + return wpa_s->driver->cancel_remain_on_channel( + wpa_s->drv_priv); + return -1; +} + +static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s, + int report) +{ + if (wpa_s->driver->probe_req_report) + return wpa_s->driver->probe_req_report(wpa_s->drv_priv, + report); + return -1; +} + +static inline int wpa_drv_disable_11b_rates(struct wpa_supplicant *wpa_s, + int disabled) +{ + if (wpa_s->driver->disable_11b_rates) + return wpa_s->driver->disable_11b_rates(wpa_s->drv_priv, + disabled); + return -1; +} + +static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->deinit_ap) + return wpa_s->driver->deinit_ap(wpa_s->drv_priv); + return 0; +} + +static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->suspend) + wpa_s->driver->suspend(wpa_s->drv_priv); +} + +static inline void wpa_drv_resume(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->resume) + wpa_s->driver->resume(wpa_s->drv_priv); +} + +static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s, + int threshold, int hysteresis) +{ + if (wpa_s->driver->signal_monitor) + return wpa_s->driver->signal_monitor(wpa_s->drv_priv, + threshold, hysteresis); + return -1; +} + +static inline int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s, + struct wpa_signal_info *si) +{ + if (wpa_s->driver->signal_poll) + return wpa_s->driver->signal_poll(wpa_s->drv_priv, si); + return -1; +} + +static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s, + const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + if (!wpa_s->driver->set_ap_wps_ie) + return -1; + return wpa_s->driver->set_ap_wps_ie(wpa_s->drv_priv, beacon, + proberesp, assocresp); +} + +static inline int wpa_drv_shared_freq(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->shared_freq) + return -1; + return wpa_s->driver->shared_freq(wpa_s->drv_priv); +} + +static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s, + u8 *buf, size_t buf_len) +{ + if (!wpa_s->driver->get_noa) + return -1; + return wpa_s->driver->get_noa(wpa_s->drv_priv, buf, buf_len); +} + +static inline int wpa_drv_set_p2p_powersave(struct wpa_supplicant *wpa_s, + int legacy_ps, int opp_ps, + int ctwindow) +{ + if (!wpa_s->driver->set_p2p_powersave) + return -1; + return wpa_s->driver->set_p2p_powersave(wpa_s->drv_priv, legacy_ps, + opp_ps, ctwindow); +} + +static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu) +{ + if (!wpa_s->driver->ampdu) + return -1; + 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) +{ + 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); +} + +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) +{ + 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); + } + return -1; +} + +static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s, + enum tdls_oper oper, const u8 *peer) +{ + if (!wpa_s->driver->tdls_oper) + return -1; + return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer); +} + +#endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c new file mode 100644 index 0000000..e5f43aa --- /dev/null +++ b/wpa_supplicant/eap_register.c @@ -0,0 +1,244 @@ +/* + * EAP method registration + * Copyright (c) 2004-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. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_methods.h" +#include "eap_server/eap_methods.h" + + +/** + * eap_register_methods - Register statically linked EAP methods + * Returns: 0 on success, -1 or -2 on failure + * + * This function is called at program initialization to register all EAP + * methods that were linked in statically. + */ +int eap_register_methods(void) +{ + int ret = 0; + +#ifdef EAP_MD5 + if (ret == 0) + ret = eap_peer_md5_register(); +#endif /* EAP_MD5 */ + +#ifdef EAP_TLS + if (ret == 0) + ret = eap_peer_tls_register(); +#endif /* EAP_TLS */ + +#ifdef EAP_MSCHAPv2 + if (ret == 0) + ret = eap_peer_mschapv2_register(); +#endif /* EAP_MSCHAPv2 */ + +#ifdef EAP_PEAP + if (ret == 0) + ret = eap_peer_peap_register(); +#endif /* EAP_PEAP */ + +#ifdef EAP_TTLS + if (ret == 0) + ret = eap_peer_ttls_register(); +#endif /* EAP_TTLS */ + +#ifdef EAP_GTC + if (ret == 0) + ret = eap_peer_gtc_register(); +#endif /* EAP_GTC */ + +#ifdef EAP_OTP + if (ret == 0) + ret = eap_peer_otp_register(); +#endif /* EAP_OTP */ + +#ifdef EAP_SIM + if (ret == 0) + ret = eap_peer_sim_register(); +#endif /* EAP_SIM */ + +#ifdef EAP_LEAP + if (ret == 0) + ret = eap_peer_leap_register(); +#endif /* EAP_LEAP */ + +#ifdef EAP_PSK + if (ret == 0) + ret = eap_peer_psk_register(); +#endif /* EAP_PSK */ + +#ifdef EAP_AKA + if (ret == 0) + ret = eap_peer_aka_register(); +#endif /* EAP_AKA */ + +#ifdef EAP_AKA_PRIME + if (ret == 0) + ret = eap_peer_aka_prime_register(); +#endif /* EAP_AKA_PRIME */ + +#ifdef EAP_FAST + if (ret == 0) + ret = eap_peer_fast_register(); +#endif /* EAP_FAST */ + +#ifdef EAP_PAX + if (ret == 0) + ret = eap_peer_pax_register(); +#endif /* EAP_PAX */ + +#ifdef EAP_SAKE + if (ret == 0) + ret = eap_peer_sake_register(); +#endif /* EAP_SAKE */ + +#ifdef EAP_GPSK + if (ret == 0) + ret = eap_peer_gpsk_register(); +#endif /* EAP_GPSK */ + +#ifdef EAP_WSC + if (ret == 0) + ret = eap_peer_wsc_register(); +#endif /* EAP_WSC */ + +#ifdef EAP_IKEV2 + if (ret == 0) + ret = eap_peer_ikev2_register(); +#endif /* EAP_IKEV2 */ + +#ifdef EAP_VENDOR_TEST + if (ret == 0) + ret = eap_peer_vendor_test_register(); +#endif /* EAP_VENDOR_TEST */ + +#ifdef EAP_TNC + if (ret == 0) + ret = eap_peer_tnc_register(); +#endif /* EAP_TNC */ + +#ifdef EAP_PWD + if (ret == 0) + ret = eap_peer_pwd_register(); +#endif /* EAP_PWD */ + +#ifdef EAP_SERVER_IDENTITY + if (ret == 0) + ret = eap_server_identity_register(); +#endif /* EAP_SERVER_IDENTITY */ + +#ifdef EAP_SERVER_MD5 + if (ret == 0) + ret = eap_server_md5_register(); +#endif /* EAP_SERVER_MD5 */ + +#ifdef EAP_SERVER_TLS + if (ret == 0) + ret = eap_server_tls_register(); +#endif /* EAP_SERVER_TLS */ + +#ifdef EAP_SERVER_MSCHAPV2 + if (ret == 0) + ret = eap_server_mschapv2_register(); +#endif /* EAP_SERVER_MSCHAPV2 */ + +#ifdef EAP_SERVER_PEAP + if (ret == 0) + ret = eap_server_peap_register(); +#endif /* EAP_SERVER_PEAP */ + +#ifdef EAP_SERVER_TLV + if (ret == 0) + ret = eap_server_tlv_register(); +#endif /* EAP_SERVER_TLV */ + +#ifdef EAP_SERVER_GTC + if (ret == 0) + ret = eap_server_gtc_register(); +#endif /* EAP_SERVER_GTC */ + +#ifdef EAP_SERVER_TTLS + if (ret == 0) + ret = eap_server_ttls_register(); +#endif /* EAP_SERVER_TTLS */ + +#ifdef EAP_SERVER_SIM + if (ret == 0) + ret = eap_server_sim_register(); +#endif /* EAP_SERVER_SIM */ + +#ifdef EAP_SERVER_AKA + if (ret == 0) + ret = eap_server_aka_register(); +#endif /* EAP_SERVER_AKA */ + +#ifdef EAP_SERVER_AKA_PRIME + if (ret == 0) + ret = eap_server_aka_prime_register(); +#endif /* EAP_SERVER_AKA_PRIME */ + +#ifdef EAP_SERVER_PAX + if (ret == 0) + ret = eap_server_pax_register(); +#endif /* EAP_SERVER_PAX */ + +#ifdef EAP_SERVER_PSK + if (ret == 0) + ret = eap_server_psk_register(); +#endif /* EAP_SERVER_PSK */ + +#ifdef EAP_SERVER_SAKE + if (ret == 0) + ret = eap_server_sake_register(); +#endif /* EAP_SERVER_SAKE */ + +#ifdef EAP_SERVER_GPSK + if (ret == 0) + ret = eap_server_gpsk_register(); +#endif /* EAP_SERVER_GPSK */ + +#ifdef EAP_SERVER_VENDOR_TEST + if (ret == 0) + ret = eap_server_vendor_test_register(); +#endif /* EAP_SERVER_VENDOR_TEST */ + +#ifdef EAP_SERVER_FAST + if (ret == 0) + ret = eap_server_fast_register(); +#endif /* EAP_SERVER_FAST */ + +#ifdef EAP_SERVER_WSC + if (ret == 0) + ret = eap_server_wsc_register(); +#endif /* EAP_SERVER_WSC */ + +#ifdef EAP_SERVER_IKEV2 + if (ret == 0) + ret = eap_server_ikev2_register(); +#endif /* EAP_SERVER_IKEV2 */ + +#ifdef EAP_SERVER_TNC + if (ret == 0) + ret = eap_server_tnc_register(); +#endif /* EAP_SERVER_TNC */ + +#ifdef EAP_SERVER_PWD + if (ret == 0) + ret = eap_server_pwd_register(); +#endif /* EAP_SERVER_PWD */ + + return ret; +} diff --git a/wpa_supplicant/eap_testing.txt b/wpa_supplicant/eap_testing.txt new file mode 100644 index 0000000..8d13222 --- /dev/null +++ b/wpa_supplicant/eap_testing.txt @@ -0,0 +1,392 @@ +Automatic regression and interoperability testing of wpa_supplicant's +IEEE 802.1X/EAPOL authentication + +Test program: +- Linked some parts of IEEE 802.1X Authenticator implementation from + hostapd (RADIUS client and RADIUS processing, EAP<->RADIUS + encapsulation/decapsulation) into wpa_supplicant. +- Replaced wpa_supplicant.c and wpa.c with test code that trigger + IEEE 802.1X authentication automatically without need for wireless + client card or AP. +- For EAP methods that generate keying material, the key derived by the + Supplicant is verified to match with the one received by the (now + integrated) Authenticator. + +The full automated test suite can now be run in couple of seconds, but +I'm more than willing to add new RADIUS authentication servers to make +this take a bit more time.. ;-) As an extra bonus, this can also be +seen as automatic regression/interoperability testing for the RADIUS +server, too. + +In order for me to be able to use a new authentication server, the +server need to be available from Internet (at least from one static IP +address) and I will need to get suitable user name/password pairs, +certificates, and private keys for testing use. Other alternative +would be to get an evaluation version of the server so that I can +install it on my own test setup. If you are interested in providing +either server access or evaluation version, please contact me +(j@w1.fi). + + +Test matrix + ++) tested successfully +F) failed +-) server did not support +?) not tested + +Cisco ACS ----------------------------------------------------------. +hostapd --------------------------------------------------------. | +Cisco Aironet 1200 AP (local RADIUS server) ----------------. | | +Periodik Labs Elektron ---------------------------------. | | | +Lucent NavisRadius ---------------------------------. | | | | +Interlink RAD-Series ---------------------------. | | | | | +Radiator -----------------------------------. | | | | | | +Meetinghouse Aegis ---------------------. | | | | | | | +Funk Steel-Belted ------------------. | | | | | | | | +Funk Odyssey -------------------. | | | | | | | | | +Microsoft IAS --------------. | | | | | | | | | | +FreeRADIUS -------------. | | | | | | | | | | | + | | | | | | | | | | | | + +EAP-MD5 + - - + + + + + - - + + +EAP-GTC + - - ? + + + + - - + - +EAP-OTP - - - - - + - - - - - - +EAP-MSCHAPv2 + - - + + + + + - - + - +EAP-TLS + + + + + + + + - - + + +EAP-PEAPv0/MSCHAPv2 + + + + + + + + + - + + +EAP-PEAPv0/GTC + - + - + + + + - - + + +EAP-PEAPv0/OTP - - - - - + - - - - - - +EAP-PEAPv0/MD5 + - - + + + + + - - + - +EAP-PEAPv0/TLS + + - + + + F + - - + + +EAP-PEAPv0/SIM - - - - - - - - - - + - +EAP-PEAPv0/AKA - - - - - - - - - - + - +EAP-PEAPv0/PSK - - - - - - - - - - + - +EAP-PEAPv0/PAX - - - - - - - - - - + - +EAP-PEAPv0/SAKE - - - - - - - - - - + - +EAP-PEAPv0/GPSK - - - - - - - - - - + - +EAP-PEAPv1/MSCHAPv2 - - + + + +1 + +5 +8 - + + +EAP-PEAPv1/GTC - - + + + +1 + +5 +8 - + + +EAP-PEAPv1/OTP - - - - - +1 - - - - - - +EAP-PEAPv1/MD5 - - - + + +1 + +5 - - + - +EAP-PEAPv1/TLS - - - + + +1 F +5 - - + + +EAP-PEAPv1/SIM - - - - - - - - - - + - +EAP-PEAPv1/AKA - - - - - - - - - - + - +EAP-PEAPv1/PSK - - - - - - - - - - + - +EAP-PEAPv1/PAX - - - - - - - - - - + - +EAP-PEAPv1/SAKE - - - - - - - - - - + - +EAP-PEAPv1/GPSK - - - - - - - - - - + - +EAP-TTLS/CHAP + - +2 + + + + + + - + - +EAP-TTLS/MSCHAP + - + + + + + + + - + - +EAP-TTLS/MSCHAPv2 + - + + + + + + + - + - +EAP-TTLS/PAP + - + + + + + + + - + - +EAP-TTLS/EAP-MD5 + - +2 + + + + + + - + - +EAP-TTLS/EAP-GTC + - +2 ? + + + + - - + - +EAP-TTLS/EAP-OTP - - - - - + - - - - - - +EAP-TTLS/EAP-MSCHAPv2 + - +2 + + + + + + - + - +EAP-TTLS/EAP-TLS + - +2 + F + + + - - + - +EAP-TTLS/EAP-SIM - - - - - - - - - - + - +EAP-TTLS/EAP-AKA - - - - - - - - - - + - +EAP-TTLS/EAP-PSK - - - - - - - - - - + - +EAP-TTLS/EAP-PAX - - - - - - - - - - + - +EAP-TTLS/EAP-SAKE - - - - - - - - - - + - +EAP-TTLS/EAP-GPSK - - - - - - - - - - + - +EAP-TTLS + TNC - - - - - + - - - - + - +EAP-SIM + - - ? - + - ? - - + - +EAP-AKA - - - - - + - - - - + - +EAP-AKA' - - - - - - - - - - + - +EAP-PSK +7 - - - - + - - - - + - +EAP-PAX - - - - - + - - - - + - +EAP-SAKE - - - - - - - - - - + - +EAP-GPSK - - - - - - - - - - + - +EAP-FAST/MSCHAPv2(prov) - - - + - + - - - + + + +EAP-FAST/GTC(auth) - - - + - + - - - + + + +EAP-FAST/MSCHAPv2(aprov)- - - - - + - - - - + + +EAP-FAST/GTC(aprov) - - - - - + - - - - + + +EAP-FAST/MD5(aprov) - - - - - + - - - - + - +EAP-FAST/TLS(aprov) - - - - - - - - - - + + +EAP-FAST/SIM(aprov) - - - - - - - - - - + - +EAP-FAST/AKA(aprov) - - - - - - - - - - + - +EAP-FAST/MSCHAPv2(auth) - - - - - + - - - - + + +EAP-FAST/MD5(auth) - - - - - + - - - - + - +EAP-FAST/TLS(auth) - - - - - - - - - - + + +EAP-FAST/SIM(auth) - - - - - - - - - - + - +EAP-FAST/AKA(auth) - - - - - - - - - - + - +EAP-FAST + TNC - - - - - - - - - - + - +LEAP + - + + + + F +6 - + - + +EAP-TNC +9 - - - - + - - - - + - +EAP-IKEv2 +10 - - - - - - - - - + - + +1) PEAPv1 required new label, "client PEAP encryption" instead of "client EAP + encryption", during key derivation (requires phase1="peaplabel=1" in the + network configuration in wpa_supplicant.conf) +2) used FreeRADIUS as inner auth server +5) PEAPv1 required termination of negotiation on tunneled EAP-Success and new + label in key deriviation + (phase1="peap_outer_success=0 peaplabel=1") (in "IETF Draft 5" mode) +6) Authenticator simulator required patching for handling Access-Accept within + negotiation (for the first EAP-Success of LEAP) +7) tested only with an older (incompatible) draft of EAP-PSK; FreeRADIUS does + not support the current EAP-PSK (RFC) specification +8) PEAPv1 used non-standard version negotiation (client had to force v1 even + though server reported v0 as the highest supported version) +9) only EAP-TTLS/EAP-TNC tested, i.e., test did not include proper sequence of + client authentication followed by TNC inside the tunnel +10) worked only with special compatibility code to match the IKEv2 server + implementation + + +Automated tests: + +FreeRADIUS (2.0-beta/CVS snapshot) +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / TLS +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS +- EAP-TTLS / CHAP +- EAP-TTLS / PAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / EAP-TNC (partial support; no authentication sequence) +- EAP-SIM +- LEAP + +Microsoft Windows Server 2003 / IAS +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / TLS +- EAP-MD5 +* IAS does not seem to support other EAP methods + +Funk Odyssey 2.01.00.653 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP (using FreeRADIUS as inner auth srv) +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-GTC (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-MSCHAPv2 (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-TLS (using FreeRADIUS as inner auth srv) +* not supported in Odyssey: + - EAP-MD5-Challenge + - EAP-GTC + - EAP-MSCHAPv2 + - EAP-PEAP / MD5-Challenge + - EAP-PEAP / TLS + +Funk Steel-Belted Radius Enterprise Edition v4.71.739 +- EAP-MD5-Challenge +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / MD5 +- EAP-PEAPv0 / TLS +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / MD5 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / TLS + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS + +Meetinghouse Aegis 1.1.4 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / TLS +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / TLS +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +* did not work + - EAP-TTLS / EAP-TLS + (Server rejects authentication without any reason in debug log. It + looks like the inner TLS negotiation starts properly and the last + packet from Supplicant looks like the one sent in the Phase 1. The + server generates a valid looking reply in the same way as in Phase + 1, but then ends up sending Access-Reject. Maybe an issue with TTLS + fragmentation in the Aegis server(?) The packet seems to include + 1328 bytes of EAP-Message and this may go beyond the fragmentation + limit with AVP encapsulation and TLS tunneling. Note: EAP-PEAP/TLS + did work, so this issue seems to be with something TTLS specific.) + +Radiator 3.17.1 (eval, with all patches up to and including 2007-05-25) +- EAP-MD5-Challenge +- EAP-GTC +- EAP-OTP +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / OTP +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / TLS + Note: Needed to use unknown identity in outer auth and some times the server + seems to get confused and fails to send proper Phase 2 data. +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / OTP +- EAP-PEAPv1 / MD5-Challenge +- EAP-PEAPv1 / TLS + Note: This has some additional requirements for EAPTLS_MaxFragmentSize. + Using 1300 for outer auth and 500 for inner auth seemed to work. + Note: Needed to use unknown identity in outer auth and some times the server + seems to get confused and fails to send proper Phase 2 data. +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-OTP +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS + Note: This has some additional requirements for EAPTLS_MaxFragmentSize. + Using 1300 for outer auth and 500 for inner auth seemed to work. +- EAP-SIM +- EAP-AKA +- EAP-PSK +- EAP-PAX +- EAP-TNC + +Interlink Networks RAD-Series 6.1.2.7 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS +* did not work + - EAP-PEAPv0 / TLS + - EAP-PEAPv1 / TLS + (Failed to decrypt Phase 2 data) + +Lucent NavisRadius 4.4.0 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / TLS +- EAP-PEAPv1 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / TLS + "IETF Draft 5" mode requires phase1="peap_outer_success=0 peaplabel=1" + 'Cisco ACU 5.05' mode works without phase1 configuration +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-TLS + +Note: user certificate from NavisRadius had private key in a format +that wpa_supplicant could not use. Converting this to PKCS#12 and then +back to PEM allowed wpa_supplicant to use the key. + + +hostapd v0.3.3 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-SIM +- EAP-PAX + +PEAPv1: + +Funk Odyssey 2.01.00.653: +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- (peap_outer_success 1 and 2 work) + +Funk Steel-Belted Radius Enterprise Edition v4.71.739 +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- (peap_outer_success 1 and 2 work) + +Radiator 3.9: +- uses TLV Success and Reply, sends MPPE keys with outer EAP-Success message + after this +- uses label "client PEAP encryption" + +Lucent NavisRadius 4.4.0 (in "IETF Draft 5" mode): +- sends tunneled EAP-Success with MPPE keys and expects the authentication to + terminate at this point (gets somewhat confused with reply to this) +- uses label "client PEAP encryption" +- phase1="peap_outer_success=0 peaplabel=1" + +Lucent NavisRadius 4.4.0 (in "Cisco ACU 5.05" mode): +- sends tunneled EAP-Success with MPPE keys and expects to receive TLS ACK + as a reply +- uses label "client EAP encryption" + +Meetinghouse Aegis 1.1.4 +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- peap_outer_success 1 and 2 work diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c new file mode 100644 index 0000000..42a7c70 --- /dev/null +++ b/wpa_supplicant/eapol_test.c @@ -0,0 +1,1213 @@ +/* + * WPA Supplicant - test code + * Copyright (c) 2003-2007, 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. + * + * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. + * Not used in production version. + */ + +#include "includes.h" +#include <assert.h> + +#include "common.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "eap_peer/eap.h" +#include "eap_server/eap_methods.h" +#include "eloop.h" +#include "rsn_supp/wpa.h" +#include "eap_peer/eap_i.h" +#include "wpa_supplicant_i.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; + +struct wpa_driver_ops *wpa_drivers[] = { NULL }; + + +struct extra_radius_attr { + u8 type; + char syntax; + char *data; + struct extra_radius_attr *next; +}; + +struct eapol_test_data { + struct wpa_supplicant *wpa_s; + + int eapol_test_num_reauths; + int no_mppe_keys; + int num_mppe_ok, num_mppe_mismatch; + + u8 radius_identifier; + struct radius_msg *last_recv_radius; + struct in_addr own_ip_addr; + struct radius_client_data *radius; + struct hostapd_radius_servers *radius_conf; + + u8 *last_eap_radius; /* last received EAP Response from Authentication + * Server */ + size_t last_eap_radius_len; + + u8 authenticator_pmk[PMK_LEN]; + size_t authenticator_pmk_len; + int radius_access_accept_received; + int radius_access_reject_received; + int auth_timed_out; + + u8 *eap_identity; + size_t eap_identity_len; + + char *connect_info; + u8 own_addr[ETH_ALEN]; + struct extra_radius_attr *extra_attrs; +}; + +static struct eapol_test_data eapol_test; + + +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx); + + +static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, + int level, const char *txt, size_t len) +{ + if (addr) + wpa_printf(MSG_DEBUG, "STA " MACSTR ": %s\n", + MAC2STR(addr), txt); + else + wpa_printf(MSG_DEBUG, "%s", txt); +} + + +static int add_extra_attr(struct radius_msg *msg, + struct extra_radius_attr *attr) +{ + size_t len; + char *pos; + u32 val; + char buf[128]; + + switch (attr->syntax) { + case 's': + os_snprintf(buf, sizeof(buf), "%s", attr->data); + len = os_strlen(buf); + break; + case 'n': + buf[0] = '\0'; + len = 1; + break; + case 'x': + pos = attr->data; + if (pos[0] == '0' && pos[1] == 'x') + pos += 2; + len = os_strlen(pos); + if ((len & 1) || (len / 2) > sizeof(buf)) { + printf("Invalid extra attribute hexstring\n"); + return -1; + } + len /= 2; + if (hexstr2bin(pos, (u8 *) buf, len) < 0) { + printf("Invalid extra attribute hexstring\n"); + return -1; + } + break; + case 'd': + val = htonl(atoi(attr->data)); + os_memcpy(buf, &val, 4); + len = 4; + break; + default: + printf("Incorrect extra attribute syntax specification\n"); + return -1; + } + + if (!radius_msg_add_attr(msg, attr->type, (u8 *) buf, len)) { + printf("Could not add attribute %d\n", attr->type); + return -1; + } + + return 0; +} + + +static int add_extra_attrs(struct radius_msg *msg, + struct extra_radius_attr *attrs) +{ + struct extra_radius_attr *p; + for (p = attrs; p; p = p->next) { + if (add_extra_attr(msg, p) < 0) + return -1; + } + return 0; +} + + +static struct extra_radius_attr * +find_extra_attr(struct extra_radius_attr *attrs, u8 type) +{ + struct extra_radius_attr *p; + for (p = attrs; p; p = p->next) { + if (p->type == type) + return p; + } + return NULL; +} + + +static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, + const u8 *eap, size_t len) +{ + struct radius_msg *msg; + char buf[128]; + const struct eap_hdr *hdr; + const u8 *pos; + + wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " + "packet"); + + e->radius_identifier = radius_client_get_id(e->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, + e->radius_identifier); + if (msg == NULL) { + printf("Could not create net RADIUS packet\n"); + return; + } + + radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e)); + + hdr = (const struct eap_hdr *) eap; + pos = (const u8 *) (hdr + 1); + if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE && + pos[0] == EAP_TYPE_IDENTITY) { + pos++; + os_free(e->eap_identity); + e->eap_identity_len = len - sizeof(*hdr) - 1; + e->eap_identity = os_malloc(e->eap_identity_len); + if (e->eap_identity) { + os_memcpy(e->eap_identity, pos, e->eap_identity_len); + wpa_hexdump(MSG_DEBUG, "Learned identity from " + "EAP-Response-Identity", + e->eap_identity, e->eap_identity_len); + } + } + + if (e->eap_identity && + !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, + e->eap_identity, e->eap_identity_len)) { + printf("Could not add User-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)) { + printf("Could not add NAS-IP-Address\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(e->wpa_s->own_addr)); + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CALLING_STATION_ID) + && + !radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Calling-Station-Id\n"); + goto fail; + } + + /* TODO: should probably check MTU from driver config; 2304 is max for + * IEEE 802.11, but use 1400 to avoid problems with too large packets + */ + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_FRAMED_MTU) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + printf("Could not add Framed-MTU\n"); + goto fail; + } + + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_PORT_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + printf("Could not add NAS-Port-Type\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), "%s", e->connect_info); + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Connect-Info\n"); + goto fail; + } + + if (add_extra_attrs(msg, e->extra_attrs) < 0) + goto fail; + + if (eap && !radius_msg_add_eap(msg, eap, len)) { + printf("Could not add EAP-Message\n"); + goto fail; + } + + /* State attribute must be copied if and only if this packet is + * Access-Request reply to the previous Access-Challenge */ + if (e->last_recv_radius && + radius_msg_get_hdr(e->last_recv_radius)->code == + RADIUS_CODE_ACCESS_CHALLENGE) { + int res = radius_msg_copy_attr(msg, e->last_recv_radius, + RADIUS_ATTR_STATE); + if (res < 0) { + printf("Could not copy State attribute from previous " + "Access-Challenge\n"); + goto fail; + } + if (res > 0) { + wpa_printf(MSG_DEBUG, " Copied RADIUS State " + "Attribute"); + } + } + + radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr); + return; + + fail: + radius_msg_free(msg); +} + + +static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf, + size_t len) +{ + /* struct wpa_supplicant *wpa_s = ctx; */ + printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n", + type, (unsigned long) len); + if (type == IEEE802_1X_TYPE_EAP_PACKET) { + wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len); + ieee802_1x_encapsulate_radius(&eapol_test, buf, len); + } + return 0; +} + + +static void eapol_test_set_config_blob(void *ctx, + struct wpa_config_blob *blob) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_config_set_blob(wpa_s->conf, blob); +} + + +static const struct wpa_config_blob * +eapol_test_get_config_blob(void *ctx, const char *name) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_config_get_blob(wpa_s->conf, name); +} + + +static void eapol_test_eapol_done_cb(void *ctx) +{ + printf("WPA: EAPOL processing complete\n"); +} + + +static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_test_data *e = eloop_ctx; + printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n"); + e->radius_access_accept_received = 0; + send_eap_request_identity(e->wpa_s, NULL); +} + + +static int eapol_test_compare_pmk(struct eapol_test_data *e) +{ + u8 pmk[PMK_LEN]; + int ret = 1; + + if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) { + wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN); + if (os_memcmp(pmk, e->authenticator_pmk, PMK_LEN) != 0) { + printf("WARNING: PMK mismatch\n"); + wpa_hexdump(MSG_DEBUG, "PMK from AS", + e->authenticator_pmk, PMK_LEN); + } else if (e->radius_access_accept_received) + ret = 0; + } else if (e->authenticator_pmk_len == 16 && + eapol_sm_get_key(e->wpa_s->eapol, pmk, 16) == 0) { + wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16); + if (os_memcmp(pmk, e->authenticator_pmk, 16) != 0) { + printf("WARNING: PMK mismatch\n"); + wpa_hexdump(MSG_DEBUG, "PMK from AS", + e->authenticator_pmk, 16); + } else if (e->radius_access_accept_received) + ret = 0; + } else if (e->radius_access_accept_received && e->no_mppe_keys) { + /* No keying material expected */ + ret = 0; + } + + if (ret && !e->no_mppe_keys) + e->num_mppe_mismatch++; + else if (!e->no_mppe_keys) + e->num_mppe_ok++; + + return ret; +} + + +static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) +{ + struct eapol_test_data *e = ctx; + printf("eapol_sm_cb: success=%d\n", success); + e->eapol_test_num_reauths--; + if (e->eapol_test_num_reauths < 0) + eloop_terminate(); + else { + eapol_test_compare_pmk(e); + eloop_register_timeout(0, 100000, eapol_sm_reauth, e, NULL); + } +} + + +static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct eapol_config eapol_conf; + struct eapol_ctx *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("Failed to allocate EAPOL context.\n"); + return -1; + } + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->scard_ctx = wpa_s->scard; + ctx->cb = eapol_sm_cb; + ctx->cb_ctx = e; + ctx->eapol_send_ctx = wpa_s; + ctx->preauth = 0; + ctx->eapol_done_cb = eapol_test_eapol_done_cb; + ctx->eapol_send = eapol_test_eapol_send; + ctx->set_config_blob = eapol_test_set_config_blob; + ctx->get_config_blob = eapol_test_get_config_blob; + 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; + + wpa_s->eapol = eapol_sm_init(ctx); + if (wpa_s->eapol == NULL) { + os_free(ctx); + printf("Failed to initialize EAPOL state machines.\n"); + return -1; + } + + wpa_s->current_ssid = ssid; + os_memset(&eapol_conf, 0, sizeof(eapol_conf)); + eapol_conf.accept_802_1x_keys = 1; + eapol_conf.required_keys = 0; + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + eapol_conf.workaround = ssid->eap_workaround; + eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); + + + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); + + return 0; +} + + +static void test_eapol_clean(struct eapol_test_data *e, + struct wpa_supplicant *wpa_s) +{ + struct extra_radius_attr *p, *prev; + + radius_client_deinit(e->radius); + os_free(e->last_eap_radius); + radius_msg_free(e->last_recv_radius); + e->last_recv_radius = NULL; + os_free(e->eap_identity); + e->eap_identity = NULL; + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + if (e->radius_conf && e->radius_conf->auth_server) { + os_free(e->radius_conf->auth_server->shared_secret); + os_free(e->radius_conf->auth_server); + } + os_free(e->radius_conf); + e->radius_conf = NULL; + scard_deinit(wpa_s->scard); + if (wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + wpa_config_free(wpa_s->conf); + + p = e->extra_attrs; + while (p) { + prev = p; + p = p->next; + os_free(prev); + } +} + + +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + u8 buf[100], *pos; + struct ieee802_1x_hdr *hdr; + struct eap_hdr *eap; + + hdr = (struct ieee802_1x_hdr *) buf; + hdr->version = EAPOL_VERSION; + hdr->type = IEEE802_1X_TYPE_EAP_PACKET; + hdr->length = htons(5); + + eap = (struct eap_hdr *) (hdr + 1); + eap->code = EAP_CODE_REQUEST; + eap->identifier = 0; + eap->length = htons(5); + pos = (u8 *) (eap + 1); + *pos = EAP_TYPE_IDENTITY; + + printf("Sending fake EAP-Request-Identity\n"); + eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf, + sizeof(*hdr) + 5); +} + + +static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_test_data *e = eloop_ctx; + printf("EAPOL test timed out\n"); + e->auth_timed_out = 1; + eloop_terminate(); +} + + +static char *eap_type_text(u8 type) +{ + switch (type) { + case EAP_TYPE_IDENTITY: return "Identity"; + case EAP_TYPE_NOTIFICATION: return "Notification"; + case EAP_TYPE_NAK: return "Nak"; + case EAP_TYPE_TLS: return "TLS"; + case EAP_TYPE_TTLS: return "TTLS"; + case EAP_TYPE_PEAP: return "PEAP"; + case EAP_TYPE_SIM: return "SIM"; + case EAP_TYPE_GTC: return "GTC"; + case EAP_TYPE_MD5: return "MD5"; + case EAP_TYPE_OTP: return "OTP"; + case EAP_TYPE_FAST: return "FAST"; + case EAP_TYPE_SAKE: return "SAKE"; + case EAP_TYPE_PSK: return "PSK"; + default: return "Unknown"; + } +} + + +static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) +{ + u8 *eap; + size_t len; + struct eap_hdr *hdr; + int eap_type = -1; + char buf[64]; + struct radius_msg *msg; + + if (e->last_recv_radius == NULL) + return; + + msg = e->last_recv_radius; + + eap = radius_msg_get_eap(msg, &len); + if (eap == NULL) { + /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: + * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message + * attribute */ + wpa_printf(MSG_DEBUG, "could not extract " + "EAP-Message from RADIUS message"); + os_free(e->last_eap_radius); + e->last_eap_radius = NULL; + e->last_eap_radius_len = 0; + return; + } + + if (len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "too short EAP packet " + "received from authentication server"); + os_free(eap); + return; + } + + if (len > sizeof(*hdr)) + eap_type = eap[sizeof(*hdr)]; + + hdr = (struct eap_hdr *) eap; + switch (hdr->code) { + case EAP_CODE_REQUEST: + os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_RESPONSE: + os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_SUCCESS: + os_strlcpy(buf, "EAP Success", sizeof(buf)); + /* LEAP uses EAP Success within an authentication, so must not + * stop here with eloop_terminate(); */ + break; + case EAP_CODE_FAILURE: + os_strlcpy(buf, "EAP Failure", sizeof(buf)); + eloop_terminate(); + break; + default: + os_strlcpy(buf, "unknown EAP code", sizeof(buf)); + wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len); + break; + } + wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d " + "id=%d len=%d) from RADIUS server: %s", + hdr->code, hdr->identifier, ntohs(hdr->length), buf); + + /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */ + + os_free(e->last_eap_radius); + e->last_eap_radius = eap; + e->last_eap_radius_len = len; + + { + struct ieee802_1x_hdr *dot1x; + dot1x = os_malloc(sizeof(*dot1x) + len); + assert(dot1x != NULL); + dot1x->version = EAPOL_VERSION; + dot1x->type = IEEE802_1X_TYPE_EAP_PACKET; + dot1x->length = htons(len); + os_memcpy((u8 *) (dot1x + 1), eap, len); + eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid, + (u8 *) dot1x, sizeof(*dot1x) + len); + os_free(dot1x); + } +} + + +static void ieee802_1x_get_keys(struct eapol_test_data *e, + struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, + size_t shared_secret_len) +{ + struct radius_ms_mppe_keys *keys; + + keys = radius_msg_get_ms_keys(msg, req, shared_secret, + shared_secret_len); + if (keys && keys->send == NULL && keys->recv == NULL) { + os_free(keys); + keys = radius_msg_get_cisco_keys(msg, req, shared_secret, + shared_secret_len); + } + + if (keys) { + if (keys->send) { + wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)", + keys->send, keys->send_len); + } + if (keys->recv) { + wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)", + keys->recv, keys->recv_len); + e->authenticator_pmk_len = + keys->recv_len > PMK_LEN ? PMK_LEN : + keys->recv_len; + os_memcpy(e->authenticator_pmk, keys->recv, + e->authenticator_pmk_len); + if (e->authenticator_pmk_len == 16 && keys->send && + keys->send_len == 16) { + /* MS-CHAP-v2 derives 16 octet keys */ + wpa_printf(MSG_DEBUG, "Use MS-MPPE-Send-Key " + "to extend PMK to 32 octets"); + os_memcpy(e->authenticator_pmk + + e->authenticator_pmk_len, + keys->send, keys->send_len); + e->authenticator_pmk_len += keys->send_len; + } + } + + os_free(keys->send); + os_free(keys->recv); + os_free(keys); + } +} + + +/* Process the RADIUS frames from Authentication Server */ +static RadiusRxResult +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + struct eapol_test_data *e = data; + struct radius_hdr *hdr = radius_msg_get_hdr(msg); + + /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be + * present when packet contains an EAP-Message attribute */ + if (hdr->code == RADIUS_CODE_ACCESS_REJECT && + radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, + 0) < 0 && + radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "Allowing RADIUS " + "Access-Reject without Message-Authenticator " + "since it does not include EAP-Message\n"); + } 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"); + return RADIUS_RX_UNKNOWN; + } + + 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"); + return RADIUS_RX_UNKNOWN; + } + + e->radius_identifier = -1; + wpa_printf(MSG_DEBUG, "RADIUS packet matching with station"); + + radius_msg_free(e->last_recv_radius); + e->last_recv_radius = msg; + + switch (hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + e->radius_access_accept_received = 1; + ieee802_1x_get_keys(e, msg, req, shared_secret, + shared_secret_len); + break; + case RADIUS_CODE_ACCESS_REJECT: + e->radius_access_reject_received = 1; + break; + } + + ieee802_1x_decapsulate_radius(e); + + if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT && + e->eapol_test_num_reauths < 0) || + hdr->code == RADIUS_CODE_ACCESS_REJECT) { + eloop_terminate(); + } + + return RADIUS_RX_QUEUED; +} + + +static void wpa_init_conf(struct eapol_test_data *e, + struct wpa_supplicant *wpa_s, const char *authsrv, + int port, const char *secret, + const char *cli_addr) +{ + struct hostapd_radius_server *as; + int res; + + wpa_s->bssid[5] = 1; + os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN); + e->own_ip_addr.s_addr = htonl((127 << 24) | 1); + os_strlcpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname)); + + e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers)); + assert(e->radius_conf != NULL); + e->radius_conf->num_auth_servers = 1; + as = os_zalloc(sizeof(struct hostapd_radius_server)); + assert(as != NULL); +#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA) + { + int a[4]; + u8 *pos; + sscanf(authsrv, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); + pos = (u8 *) &as->addr.u.v4; + *pos++ = a[0]; + *pos++ = a[1]; + *pos++ = a[2]; + *pos++ = a[3]; + } +#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + inet_aton(authsrv, &as->addr.u.v4); +#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + as->addr.af = AF_INET; + as->port = port; + as->shared_secret = (u8 *) os_strdup(secret); + as->shared_secret_len = os_strlen(secret); + e->radius_conf->auth_server = as; + e->radius_conf->auth_servers = as; + e->radius_conf->msg_dumps = 1; + if (cli_addr) { + if (hostapd_parse_ip_addr(cli_addr, + &e->radius_conf->client_addr) == 0) + e->radius_conf->force_client_addr = 1; + else { + wpa_printf(MSG_ERROR, "Invalid IP address '%s'", + cli_addr); + assert(0); + } + } + + e->radius = radius_client_init(wpa_s, e->radius_conf); + assert(e->radius != NULL); + + res = radius_client_register(e->radius, RADIUS_AUTH, + ieee802_1x_receive_auth, e); + assert(res == 0); +} + + +static int scard_test(void) +{ + struct scard_data *scard; + size_t len; + char imsi[20]; + unsigned char _rand[16]; +#ifdef PCSC_FUNCS + unsigned char sres[4]; + unsigned char kc[8]; +#endif /* PCSC_FUNCS */ +#define num_triplets 5 + unsigned char rand_[num_triplets][16]; + unsigned char sres_[num_triplets][4]; + unsigned char kc_[num_triplets][8]; + int i, res; + size_t j; + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define IK_LEN 16 +#define CK_LEN 16 + unsigned char aka_rand[AKA_RAND_LEN]; + unsigned char aka_autn[AKA_AUTN_LEN]; + unsigned char aka_auts[AKA_AUTS_LEN]; + unsigned char aka_res[RES_MAX_LEN]; + size_t aka_res_len; + unsigned char aka_ik[IK_LEN]; + unsigned char aka_ck[CK_LEN]; + + scard = scard_init(SCARD_TRY_BOTH); + if (scard == NULL) + return -1; + if (scard_set_pin(scard, "1234")) { + wpa_printf(MSG_WARNING, "PIN validation failed"); + scard_deinit(scard); + return -1; + } + + len = sizeof(imsi); + if (scard_get_imsi(scard, imsi, &len)) + goto failed; + wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len); + /* NOTE: Permanent Username: 1 | IMSI */ + + os_memset(_rand, 0, sizeof(_rand)); + if (scard_gsm_auth(scard, _rand, sres, kc)) + goto failed; + + os_memset(_rand, 0xff, sizeof(_rand)); + if (scard_gsm_auth(scard, _rand, sres, kc)) + goto failed; + + for (i = 0; i < num_triplets; i++) { + os_memset(rand_[i], i, sizeof(rand_[i])); + if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i])) + goto failed; + } + + for (i = 0; i < num_triplets; i++) { + printf("1"); + for (j = 0; j < len; j++) + printf("%c", imsi[j]); + printf(","); + for (j = 0; j < 16; j++) + printf("%02X", rand_[i][j]); + printf(","); + for (j = 0; j < 4; j++) + printf("%02X", sres_[i][j]); + printf(","); + for (j = 0; j < 8; j++) + printf("%02X", kc_[i][j]); + printf("\n"); + } + + wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication"); + + /* seq 39 (0x28) */ + os_memset(aka_rand, 0xaa, 16); + os_memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf" + "\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16); + + res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len, + aka_ik, aka_ck, aka_auts); + if (res == 0) { + wpa_printf(MSG_DEBUG, "UMTS auth completed successfully"); + wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len); + wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN); + wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN); + } else if (res == -2) { + wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization " + "failure"); + wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN); + } else { + wpa_printf(MSG_DEBUG, "UMTS auth failed"); + } + +failed: + scard_deinit(scard); + + return 0; +#undef num_triplets +} + + +static int scard_get_triplets(int argc, char *argv[]) +{ + struct scard_data *scard; + size_t len; + char imsi[20]; + unsigned char _rand[16]; + unsigned char sres[4]; + unsigned char kc[8]; + int num_triplets; + int i; + size_t j; + + if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) { + printf("invalid parameters for sim command\n"); + return -1; + } + + if (argc <= 2 || os_strcmp(argv[2], "debug") != 0) { + /* disable debug output */ + wpa_debug_level = 99; + } + + scard = scard_init(SCARD_GSM_SIM_ONLY); + if (scard == NULL) { + printf("Failed to open smartcard connection\n"); + return -1; + } + if (scard_set_pin(scard, argv[0])) { + wpa_printf(MSG_WARNING, "PIN validation failed"); + scard_deinit(scard); + return -1; + } + + len = sizeof(imsi); + if (scard_get_imsi(scard, imsi, &len)) { + scard_deinit(scard); + return -1; + } + + for (i = 0; i < num_triplets; i++) { + os_memset(_rand, i, sizeof(_rand)); + if (scard_gsm_auth(scard, _rand, sres, kc)) + break; + + /* IMSI:Kc:SRES:RAND */ + for (j = 0; j < len; j++) + printf("%c", imsi[j]); + printf(":"); + for (j = 0; j < 8; j++) + printf("%02X", kc[j]); + printf(":"); + for (j = 0; j < 4; j++) + printf("%02X", sres[j]); + printf(":"); + for (j = 0; j < 16; j++) + printf("%02X", _rand[j]); + printf("\n"); + } + + scard_deinit(scard); + + return 0; +} + + +static void eapol_test_terminate(int sig, void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = signal_ctx; + wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +static void usage(void) +{ + printf("usage:\n" + "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] " + "[-s<AS secret>]\\\n" + " [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n" + " [-M<client MAC address>] \\\n" + " [-N<attr spec>] \\\n" + " [-A<client IP>]\n" + "eapol_test scard\n" + "eapol_test sim <PIN> <num triplets> [debug]\n" + "\n"); + printf("options:\n" + " -c<conf> = configuration file\n" + " -a<AS IP> = IP address of the authentication server, " + "default 127.0.0.1\n" + " -p<AS port> = UDP port of the authentication server, " + "default 1812\n" + " -s<AS secret> = shared secret with the authentication " + "server, default 'radius'\n" + " -A<client IP> = IP address of the client, default: select " + "automatically\n" + " -r<count> = number of re-authentications\n" + " -W = wait for a control interface monitor before starting\n" + " -S = save configuration after authentication\n" + " -n = no MPPE keys expected\n" + " -t<timeout> = sets timeout in seconds (default: 30 s)\n" + " -C<Connect-Info> = RADIUS Connect-Info (default: " + "CONNECT 11Mbps 802.11b)\n" + " -M<client MAC address> = Set own MAC address " + "(Calling-Station-Id,\n" + " default: 02:00:00:00:00:01)\n" + " -N<attr spec> = send arbitrary attribute specified by:\n" + " attr_id:syntax:value or attr_id\n" + " attr_id - number id of the attribute\n" + " syntax - one of: s, d, x\n" + " s = string\n" + " d = integer\n" + " x = octet string\n" + " value - attribute value.\n" + " When only attr_id is specified, NULL will be used as " + "value.\n" + " Multiple attributes can be specified by using the " + "option several times.\n"); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant wpa_s; + int c, ret = 1, wait_for_monitor = 0, save_config = 0; + char *as_addr = "127.0.0.1"; + int as_port = 1812; + char *as_secret = "radius"; + char *cli_addr = NULL; + char *conf = NULL; + int timeout = 30; + char *pos; + struct extra_radius_attr *p = NULL, *p1; + + if (os_program_init()) + return -1; + + hostapd_logger_register_cb(hostapd_logger_cb); + + 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); + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + for (;;) { + c = getopt(argc, argv, "a:A:c:C:M:nN:p:r:s:St:W"); + if (c < 0) + break; + switch (c) { + case 'a': + as_addr = optarg; + break; + case 'A': + cli_addr = optarg; + break; + case 'c': + conf = optarg; + break; + case 'C': + eapol_test.connect_info = optarg; + break; + case 'M': + if (hwaddr_aton(optarg, eapol_test.own_addr)) { + usage(); + return -1; + } + break; + case 'n': + eapol_test.no_mppe_keys++; + break; + case 'p': + as_port = atoi(optarg); + break; + case 'r': + eapol_test.eapol_test_num_reauths = atoi(optarg); + break; + case 's': + as_secret = optarg; + break; + case 'S': + save_config++; + break; + case 't': + timeout = atoi(optarg); + break; + case 'W': + wait_for_monitor++; + break; + case 'N': + p1 = os_zalloc(sizeof(p1)); + if (p1 == NULL) + break; + if (!p) + eapol_test.extra_attrs = p1; + else + p->next = p1; + p = p1; + + p->type = atoi(optarg); + pos = os_strchr(optarg, ':'); + if (pos == NULL) { + p->syntax = 'n'; + p->data = NULL; + break; + } + + pos++; + if (pos[0] == '\0' || pos[1] != ':') { + printf("Incorrect format of attribute " + "specification\n"); + break; + } + + p->syntax = pos[0]; + p->data = pos + 2; + break; + default: + usage(); + return -1; + } + } + + if (argc > optind && os_strcmp(argv[optind], "scard") == 0) { + return scard_test(); + } + + if (argc > optind && os_strcmp(argv[optind], "sim") == 0) { + return scard_get_triplets(argc - optind - 1, + &argv[optind + 1]); + } + + if (conf == NULL) { + usage(); + printf("Configuration file is required.\n"); + return -1; + } + + if (eap_register_methods()) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + return -1; + } + + if (eloop_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + + os_memset(&wpa_s, 0, sizeof(wpa_s)); + eapol_test.wpa_s = &wpa_s; + wpa_s.conf = wpa_config_read(conf); + if (wpa_s.conf == NULL) { + printf("Failed to parse configuration file '%s'.\n", conf); + return -1; + } + if (wpa_s.conf->ssid == NULL) { + printf("No networks defined.\n"); + return -1; + } + + 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); + if (wpa_s.ctrl_iface == NULL) { + printf("Failed to initialize control interface '%s'.\n" + "You may have another eapol_test process already " + "running or the file was\n" + "left by an unclean termination of eapol_test in " + "which case you will need\n" + "to manually remove this file before starting " + "eapol_test again.\n", + wpa_s.conf->ctrl_interface); + return -1; + } + if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) + return -1; + + if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid)) + return -1; + + if (wait_for_monitor) + wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface); + + eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test, + NULL); + eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL); + eloop_register_signal_terminate(eapol_test_terminate, &wpa_s); + eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s); + eloop_run(); + + eloop_cancel_timeout(eapol_test_timeout, &eapol_test, NULL); + eloop_cancel_timeout(eapol_sm_reauth, &eapol_test, NULL); + + if (eapol_test_compare_pmk(&eapol_test) == 0 || + eapol_test.no_mppe_keys) + ret = 0; + if (eapol_test.auth_timed_out) + ret = -2; + if (eapol_test.radius_access_reject_received) + ret = -3; + + if (save_config) + wpa_config_write(conf, wpa_s.conf); + + test_eapol_clean(&eapol_test, &wpa_s); + + eap_peer_unregister_methods(); +#ifdef CONFIG_AP + eap_server_unregister_methods(); +#endif /* CONFIG_AP */ + + eloop_destroy(); + + printf("MPPE keys OK: %d mismatch: %d\n", + eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch); + if (eapol_test.num_mppe_mismatch) + ret = -4; + if (ret) + printf("FAILURE\n"); + else + printf("SUCCESS\n"); + + os_program_deinit(); + + return ret; +} diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c new file mode 100644 index 0000000..a3153aa --- /dev/null +++ b/wpa_supplicant/events.c @@ -0,0 +1,2202 @@ +/* + * WPA Supplicant - Driver event processing + * Copyright (c) 2003-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. + */ + +#include "includes.h" + +#include "common.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "rsn_supp/wpa.h" +#include "eloop.h" +#include "config.h" +#include "l2_packet/l2_packet.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "pcsc_funcs.h" +#include "rsn_supp/preauth.h" +#include "rsn_supp/pmksa_cache.h" +#include "common/wpa_ctrl.h" +#include "eap_peer/eap.h" +#include "ap/hostapd.h" +#include "p2p/p2p.h" +#include "notify.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "crypto/random.h" +#include "blacklist.h" +#include "wpas_glue.h" +#include "wps_supplicant.h" +#include "ibss_rsn.h" +#include "sme.h" +#include "p2p_supplicant.h" +#include "bgscan.h" +#include "ap.h" +#include "bss.h" +#include "mlme.h" +#include "scan.h" + + +static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid, *old_ssid; + + if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) + return 0; + + wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association " + "information"); + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) { + wpa_msg(wpa_s, MSG_INFO, + "No network configuration found for the current AP"); + return -1; + } + + if (ssid->disabled) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled"); + return -1; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the " + "current AP"); + if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_WPA_NONE | + WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256)) { + u8 wpa_ie[80]; + size_t wpa_ie_len = sizeof(wpa_ie); + wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len); + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + } + + if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) + eapol_sm_invalidate_cached_session(wpa_s->eapol); + old_ssid = wpa_s->current_ssid; + wpa_s->current_ssid = ssid; + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); + wpa_supplicant_initiate_eapol(wpa_s); + if (old_ssid != wpa_s->current_ssid) + wpas_notify_network_changed(wpa_s); + + return 0; +} + + +static void wpa_supplicant_stop_countermeasures(void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->countermeasures) { + wpa_s->countermeasures = 0; + wpa_drv_set_countermeasures(wpa_s, 0); + wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped"); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +} + + +void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) +{ + int bssid_changed; + +#ifdef CONFIG_IBSS_RSN + ibss_rsn_deinit(wpa_s->ibss_rsn); + wpa_s->ibss_rsn = NULL; +#endif /* CONFIG_IBSS_RSN */ + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + return; + + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + 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); + wpa_s->current_bss = NULL; + wpa_s->assoc_freq = 0; + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); + + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + wpa_s->ap_ies_from_associnfo = 0; +} + + +static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) +{ + struct wpa_ie_data ie; + int pmksa_set = -1; + size_t i; + + if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 || + ie.pmkid == NULL) + return; + + for (i = 0; i < ie.num_pmkid; i++) { + pmksa_set = pmksa_cache_set_current(wpa_s->wpa, + ie.pmkid + i * PMKID_LEN, + NULL, NULL, 0); + if (pmksa_set == 0) { + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + break; + } + } + + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from " + "PMKSA cache", pmksa_set == 0 ? "" : "not "); +} + + +static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate " + "event"); + return; + } + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR + " index=%d preauth=%d", + MAC2STR(data->pmkid_candidate.bssid), + data->pmkid_candidate.index, + data->pmkid_candidate.preauth); + + pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid, + data->pmkid_candidate.index, + data->pmkid_candidate.preauth); +} + + +static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) + return 0; + +#ifdef IEEE8021X_EAPOL + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA && + wpa_s->current_ssid && + !(wpa_s->current_ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { + /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either + * plaintext or static WEP keys). */ + return 0; + } +#endif /* IEEE8021X_EAPOL */ + + return 1; +} + + +/** + * wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC + * @wpa_s: pointer to wpa_supplicant data + * @ssid: Configuration data for the network + * Returns: 0 on success, -1 on failure + * + * This function is called when starting authentication with a network that is + * configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA). + */ +int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ +#ifdef IEEE8021X_EAPOL + int aka = 0, sim = 0, type; + + if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL) + return 0; + + if (ssid->eap.eap_methods == NULL) { + sim = 1; + aka = 1; + } else { + struct eap_method_type *eap = ssid->eap.eap_methods; + while (eap->vendor != EAP_VENDOR_IETF || + eap->method != EAP_TYPE_NONE) { + if (eap->vendor == EAP_VENDOR_IETF) { + if (eap->method == EAP_TYPE_SIM) + sim = 1; + else if (eap->method == EAP_TYPE_AKA) + aka = 1; + } + eap++; + } + } + + if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL) + sim = 0; + if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL) + aka = 0; + + if (!sim && !aka) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to " + "use SIM, but neither EAP-SIM nor EAP-AKA are " + "enabled"); + return 0; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM " + "(sim=%d aka=%d) - initialize PCSC", sim, aka); + if (sim && aka) + type = SCARD_TRY_BOTH; + else if (aka) + type = SCARD_USIM_ONLY; + else + type = SCARD_GSM_SIM_ONLY; + + wpa_s->scard = scard_init(type); + if (wpa_s->scard == NULL) { + wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM " + "(pcsc-lite)"); + return -1; + } + wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard); + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); +#endif /* IEEE8021X_EAPOL */ + + return 0; +} + + +#ifndef CONFIG_NO_SCAN_PROCESSING +static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, + struct wpa_ssid *ssid) +{ + int i, privacy = 0; + + if (ssid->mixed_cell) + return 1; + +#ifdef CONFIG_WPS + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) + return 1; +#endif /* CONFIG_WPS */ + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) { + privacy = 1; + break; + } + } +#ifdef IEEE8021X_EAPOL + if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && + ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) + privacy = 1; +#endif /* IEEE8021X_EAPOL */ + + if (bss->caps & IEEE80211_CAP_PRIVACY) + return privacy; + return !privacy; +} + + +static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + struct wpa_ie_data ie; + int proto_match = 0; + const u8 *rsn_ie, *wpa_ie; + int ret; + int wep_ok; + + ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss); + if (ret >= 0) + return ret; + + /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */ + wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) && + (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) && + ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) || + (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)); + + rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { + proto_match++; + + if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse " + "failed"); + break; + } + + if (wep_ok && + (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) + { + wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN " + "in RSN IE"); + return 1; + } + + if (!(ie.proto & ssid->proto)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto " + "mismatch"); + break; + } + + if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK " + "cipher mismatch"); + break; + } + + if (!(ie.group_cipher & ssid->group_cipher)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK " + "cipher mismatch"); + break; + } + + if (!(ie.key_mgmt & ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt " + "mismatch"); + break; + } + +#ifdef CONFIG_IEEE80211W + if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && + ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt " + "frame protection"); + break; + } +#endif /* CONFIG_IEEE80211W */ + + wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE"); + return 1; + } + + wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { + proto_match++; + + if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse " + "failed"); + break; + } + + if (wep_ok && + (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) + { + wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN " + "in WPA IE"); + return 1; + } + + if (!(ie.proto & ssid->proto)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto " + "mismatch"); + break; + } + + if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK " + "cipher mismatch"); + break; + } + + if (!(ie.group_cipher & ssid->group_cipher)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK " + "cipher mismatch"); + break; + } + + if (!(ie.key_mgmt & ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt " + "mismatch"); + break; + } + + wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE"); + return 1; + } + + if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) && + wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match"); + return 0; + } + + if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2"); + return 1; + } + + wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with " + "WPA/WPA2"); + + return 0; +} + + +static int freq_allowed(int *freqs, int freq) +{ + int i; + + if (freqs == NULL) + return 1; + + for (i = 0; freqs[i]; i++) + if (freqs[i] == freq) + return 1; + return 0; +} + + +static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, + int i, struct wpa_scan_res *bss, + struct wpa_ssid *group) +{ + const u8 *ssid_; + u8 wpa_ie_len, rsn_ie_len, ssid_len; + int wpa; + struct wpa_blacklist *e; + const u8 *ie; + struct wpa_ssid *ssid; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ssid_ = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; + + ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + wpa_ie_len = ie ? ie[1] : 0; + + ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + rsn_ie_len = ie ? ie[1] : 0; + + wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' " + "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s", + i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len), + wpa_ie_len, rsn_ie_len, bss->caps, bss->level, + wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : ""); + + e = wpa_blacklist_get(wpa_s, bss->bssid); + if (e) { + int limit = 1; + if (wpa_supplicant_enabled_networks(wpa_s->conf) == 1) { + /* + * When only a single network is enabled, we can + * trigger blacklisting on the first failure. This + * should not be done with multiple enabled networks to + * avoid getting forced to move into a worse ESS on + * single error if there are no other BSSes of the + * current ESS. + */ + limit = 0; + } + if (e->count > limit) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " + "(count=%d limit=%d)", e->count, limit); + return NULL; + } + } + + if (ssid_len == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known"); + return NULL; + } + + wpa = wpa_ie_len > 0 || rsn_ie_len > 0; + + for (ssid = group; ssid; ssid = ssid->pnext) { + int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); + + if (ssid->disabled) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); + continue; + } + +#ifdef CONFIG_WPS + if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " + "(WPS)"); + continue; + } + + if (wpa && ssid->ssid_len == 0 && + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) + check_ssid = 0; + + if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + /* Only allow wildcard SSID match if an AP + * advertises active WPS operation that matches + * with our mode. */ + check_ssid = 1; + if (ssid->ssid_len == 0 && + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) + check_ssid = 0; + } +#endif /* CONFIG_WPS */ + + if (check_ssid && + (ssid_len != ssid->ssid_len || + os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch"); + continue; + } + + if (ssid->bssid_set && + os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch"); + continue; + } + + if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) + continue; + + if (!wpa && + !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && + !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && + !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network " + "not allowed"); + continue; + } + + if (!wpa && !wpa_supplicant_match_privacy(bss, ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy " + "mismatch"); + continue; + } + + if (!wpa && (bss->caps & IEEE80211_CAP_IBSS)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS (adhoc) " + "network"); + continue; + } + + if (!freq_allowed(ssid->freq_list, bss->freq)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not " + "allowed"); + continue; + } + +#ifdef CONFIG_P2P + /* + * TODO: skip the AP if its P2P IE has Group Formation + * bit set in the P2P Group Capability Bitmap and we + * are not in Group Formation with that device. + */ +#endif /* CONFIG_P2P */ + + /* Matching configuration found */ + return ssid; + } + + /* No matching configuration found */ + return NULL; +} + + +static struct wpa_bss * +wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct wpa_ssid *group, + struct wpa_ssid **selected_ssid) +{ + size_t i; + + wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d", + group->priority); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + const u8 *ie, *ssid; + u8 ssid_len; + + *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group); + if (!*selected_ssid) + continue; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ssid = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; + + wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR + " ssid='%s'", + MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); + return wpa_bss_get(wpa_s, bss->bssid, ssid, ssid_len); + } + + return NULL; +} + + +static struct wpa_bss * +wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct wpa_ssid **selected_ssid) +{ + struct wpa_bss *selected = NULL; + int prio; + + while (selected == NULL) { + for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { + selected = wpa_supplicant_select_bss( + wpa_s, scan_res, wpa_s->conf->pssid[prio], + selected_ssid); + if (selected) + break; + } + + if (selected == NULL && wpa_s->blacklist) { + wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear " + "blacklist and try again"); + wpa_blacklist_clear(wpa_s); + wpa_s->blacklist_cleared++; + } else if (selected == NULL) + break; + } + + return selected; +} + + +static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, + int timeout_sec, int timeout_usec) +{ + if (!wpa_supplicant_enabled_networks(wpa_s->conf)) { + /* + * No networks are enabled; short-circuit request so + * we don't wait timeout seconds before transitioning + * to INACTIVE state. + */ + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + return; + } + wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec); +} + + +void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid) +{ + if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP + "PBC session overlap"); +#ifdef CONFIG_P2P + if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1) + return; +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WPS + wpas_wps_cancel(wpa_s); +#endif /* CONFIG_WPS */ + return; + } + + /* + * Do not trigger new association unless the BSSID has changed or if + * reassociation is requested. If we are in process of associating with + * the selected BSSID, do not trigger new attempt. + */ + if (wpa_s->reassociate || + (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && + ((wpa_s->wpa_state != WPA_ASSOCIATING && + wpa_s->wpa_state != WPA_AUTHENTICATING) || + os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != + 0))) { + if (wpa_supplicant_scard_init(wpa_s, ssid)) { + wpa_supplicant_req_new_scan(wpa_s, 10, 0); + return; + } + wpa_msg(wpa_s, MSG_DEBUG, "Request association: " + "reassociate: %d selected: "MACSTR " bssid: " MACSTR + " pending: " MACSTR " wpa_state: %s", + wpa_s->reassociate, MAC2STR(selected->bssid), + MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), + wpa_supplicant_state_txt(wpa_s->wpa_state)); + wpa_supplicant_associate(wpa_s, selected, ssid); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with the " + "selected AP"); + } +} + + +static struct wpa_ssid * +wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) +{ + int prio; + struct wpa_ssid *ssid; + + for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { + for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext) + { + if (ssid->disabled) + continue; + if (ssid->mode == IEEE80211_MODE_IBSS || + ssid->mode == IEEE80211_MODE_AP) + return ssid; + } + } + return NULL; +} + + +/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based + * on BSS added and BSS changed events */ +static void wpa_supplicant_rsn_preauth_scan_results( + struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) +{ + int i; + + if (rsn_preauth_scan_results(wpa_s->wpa) < 0) + return; + + for (i = scan_res->num - 1; i >= 0; i--) { + const u8 *ssid, *rsn; + struct wpa_scan_res *r; + + r = scan_res->res[i]; + + ssid = wpa_scan_get_ie(r, WLAN_EID_SSID); + if (ssid == NULL) + continue; + + rsn = wpa_scan_get_ie(r, WLAN_EID_RSN); + if (rsn == NULL) + continue; + + rsn_preauth_scan_result(wpa_s->wpa, r->bssid, ssid, rsn); + } + +} + + +static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid, + struct wpa_scan_results *scan_res) +{ + size_t i; + struct wpa_scan_res *current_bss = NULL; + int min_diff; + + if (wpa_s->reassociate) + return 1; /* explicit request to reassociate */ + if (wpa_s->wpa_state < WPA_ASSOCIATED) + return 1; /* we are not associated; continue */ + if (wpa_s->current_ssid == NULL) + return 1; /* unknown current SSID */ + if (wpa_s->current_ssid != ssid) + return 1; /* different network block */ + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *res = scan_res->res[i]; + const u8 *ie; + if (os_memcmp(res->bssid, wpa_s->bssid, ETH_ALEN) != 0) + continue; + + ie = wpa_scan_get_ie(res, WLAN_EID_SSID); + if (ie == NULL) + continue; + if (ie[1] != wpa_s->current_ssid->ssid_len || + os_memcmp(ie + 2, wpa_s->current_ssid->ssid, ie[1]) != 0) + continue; + current_bss = res; + break; + } + + if (!current_bss) + return 1; /* current BSS not seen in scan results */ + + wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation"); + wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d", + MAC2STR(current_bss->bssid), current_bss->level); + wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d", + MAC2STR(selected->bssid), selected->level); + + if (wpa_s->current_ssid->bssid_set && + os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) == + 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS " + "has preferred BSSID"); + return 1; + } + + min_diff = 2; + if (current_bss->level < 0) { + if (current_bss->level < -85) + min_diff = 1; + else if (current_bss->level < -80) + min_diff = 2; + else if (current_bss->level < -75) + min_diff = 3; + else if (current_bss->level < -70) + min_diff = 4; + else + min_diff = 5; + } + if (abs(current_bss->level - selected->level) < min_diff) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference " + "in signal level"); + return 0; + } + + return 1; +} + +/* Return < 0 if no scan results could be fetched. */ +static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + struct wpa_bss *selected; + struct wpa_ssid *ssid = NULL; + struct wpa_scan_results *scan_res; + int ap = 0; + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + ap = 1; +#endif /* CONFIG_AP */ + + wpa_supplicant_notify_scanning(wpa_s, 0); + + scan_res = wpa_supplicant_get_scan_results(wpa_s, + data ? &data->scan_info : + NULL, 1); + if (scan_res == NULL) { + if (wpa_s->conf->ap_scan == 2 || ap) + return -1; + 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; + } + +#ifndef CONFIG_NO_RANDOM_POOL + size_t i, num; + num = scan_res->num; + if (num > 10) + num = 10; + for (i = 0; i < num; i++) { + u8 buf[5]; + struct wpa_scan_res *res = scan_res->res[i]; + buf[0] = res->bssid[5]; + buf[1] = res->qual & 0xff; + buf[2] = res->noise & 0xff; + buf[3] = res->level & 0xff; + buf[4] = res->tsf & 0xff; + random_add_randomness(buf, sizeof(buf)); + } +#endif /* CONFIG_NO_RANDOM_POOL */ + + if (wpa_s->scan_res_handler) { + 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 0; + } + + if (ap) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode"); +#ifdef CONFIG_AP + 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; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available"); + 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->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) { + wpa_scan_results_free(scan_res); + return 0; + } + + if (wpa_s->disconnected) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + wpa_scan_results_free(scan_res); + return 0; + } + + if (bgscan_notify_scan(wpa_s, scan_res) == 1) { + wpa_scan_results_free(scan_res); + return 0; + } + + wpa_supplicant_rsn_preauth_scan_results(wpa_s, scan_res); + + selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid); + + if (selected) { + int skip; + skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid, + scan_res); + wpa_scan_results_free(scan_res); + if (skip) + return 0; + wpa_supplicant_connect(wpa_s, selected, ssid); + } else { + wpa_scan_results_free(scan_res); + wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); + ssid = wpa_supplicant_pick_new_network(wpa_s); + if (ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network"); + wpa_supplicant_associate(wpa_s, NULL, ssid); + } else { + int timeout_sec = wpa_s->scan_interval; + int timeout_usec = 0; +#ifdef CONFIG_P2P + if (wpa_s->p2p_in_provisioning) { + /* + * Use shorter wait during P2P Provisioning + * state to speed up group formation. + */ + timeout_sec = 0; + timeout_usec = 250000; + } +#endif /* CONFIG_P2P */ + wpa_supplicant_req_new_scan(wpa_s, timeout_sec, + timeout_usec); + } + } + return 0; +} + + +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) < 0) { + /* + * If no scan results could be fetched, then no need to + * notify those interfaces that did not actually request + * this scan. + */ + return; + } + + /* + * Check other interfaces to see if they have the same radio-name. 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) { + wpa_printf(MSG_DEBUG, "%s: Updating scan results from " + "sibling", ifs->ifname); + _wpa_supplicant_event_scan_results(ifs, data); + } + } +} + +#endif /* CONFIG_NO_SCAN_PROCESSING */ + + +static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int l, len, found = 0, wpa_found, rsn_found; + const u8 *p; + + wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); + if (data->assoc_info.req_ies) + wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies, + data->assoc_info.req_ies_len); + if (data->assoc_info.resp_ies) { + wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#ifdef CONFIG_TDLS + wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_TDLS */ + } + if (data->assoc_info.beacon_ies) + wpa_hexdump(MSG_DEBUG, "beacon_ies", + data->assoc_info.beacon_ies, + data->assoc_info.beacon_ies_len); + if (data->assoc_info.freq) + wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", + data->assoc_info.freq); + + p = data->assoc_info.req_ies; + l = data->assoc_info.req_ies_len; + + /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */ + while (p && l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", + p, l); + break; + } + if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == WLAN_EID_RSN && p[1] >= 2)) { + if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) + break; + found = 1; + wpa_find_assoc_pmkid(wpa_s); + break; + } + l -= len; + p += len; + } + if (!found && data->assoc_info.req_ies) + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + +#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_SME + if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { + u8 bssid[ETH_ALEN]; + if (wpa_drv_get_bssid(wpa_s, bssid) < 0 || + wpa_ft_validate_reassoc_resp(wpa_s->wpa, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + bssid) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of " + "Reassociation Response failed"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_IE); + return -1; + } + } + + p = data->assoc_info.resp_ies; + l = data->assoc_info.resp_ies_len; + +#ifdef CONFIG_WPS_STRICT + if (p && wpa_s->current_ssid && + wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) { + struct wpabuf *wps; + wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE); + if (wps == NULL) { + wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not " + "include WPS IE in (Re)Association Response"); + return -1; + } + + if (wps_validate_assoc_resp(wps) < 0) { + wpabuf_free(wps); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_IE); + return -1; + } + wpabuf_free(wps); + } +#endif /* CONFIG_WPS_STRICT */ + + /* Go through the IEs and make a copy of the MDIE, if present. */ + while (p && l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", + p, l); + break; + } + if (p[0] == WLAN_EID_MOBILITY_DOMAIN && + p[1] >= MOBILITY_DOMAIN_ID_LEN) { + wpa_s->sme.ft_used = 1; + os_memcpy(wpa_s->sme.mobility_domain, p + 2, + MOBILITY_DOMAIN_ID_LEN); + break; + } + l -= len; + p += len; + } +#endif /* CONFIG_SME */ + + wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_IEEE80211R */ + + /* WPA/RSN IE from Beacon/ProbeResp */ + p = data->assoc_info.beacon_ies; + l = data->assoc_info.beacon_ies_len; + + /* Go through the IEs and make a copy of the WPA/RSN IEs, if present. + */ + wpa_found = rsn_found = 0; + while (p && l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies", + p, l); + break; + } + if (!wpa_found && + p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) { + wpa_found = 1; + wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len); + } + + if (!rsn_found && + p[0] == WLAN_EID_RSN && p[1] >= 2) { + rsn_found = 1; + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len); + } + + l -= len; + p += len; + } + + if (!wpa_found && data->assoc_info.beacon_ies) + wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); + if (!rsn_found && data->assoc_info.beacon_ies) + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); + if (wpa_found || rsn_found) + wpa_s->ap_ies_from_associnfo = 1; + + wpa_s->assoc_freq = data->assoc_info.freq; + + return 0; +} + + +static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + u8 bssid[ETH_ALEN]; + int ft_completed; + int bssid_changed; + struct wpa_driver_capa capa; + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + hostapd_notif_assoc(wpa_s->ap_iface->bss[0], + data->assoc_info.addr, + data->assoc_info.req_ies, + data->assoc_info.req_ies_len, + data->assoc_info.reassoc); + return; + } +#endif /* CONFIG_AP */ + + ft_completed = wpa_ft_is_completed(wpa_s->wpa); + if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) + return; + + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) || + (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && + os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" + MACSTR, MAC2STR(bssid)); + random_add_randomness(bssid, ETH_ALEN); + bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN); + os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); + + if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { + wpa_clear_keys(wpa_s, bssid); + } + if (wpa_supplicant_select_config(wpa_s) < 0) { + wpa_supplicant_disassociate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + return; + } + if (wpa_s->current_ssid) { + 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); + if (bss) + wpa_s->current_bss = bss; + } + } + +#ifdef CONFIG_SME + os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN); + wpa_s->sme.prev_bssid_set = 1; +#endif /* CONFIG_SME */ + + wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid)); + if (wpa_s->current_ssid) { + /* When using scanning (ap_scan=1), SIM PC/SC interface can be + * initialized before association, but for other modes, + * initialize PC/SC here, if the current configuration needs + * smartcard or SIM/USIM. */ + wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid); + } + wpa_sm_notify_assoc(wpa_s->wpa, bssid); + if (wpa_s->l2) + l2_packet_notify_auth_start(wpa_s->l2); + + /* + * Set portEnabled first to FALSE in order to get EAP state machine out + * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE + * state machine may transit to AUTHENTICATING state based on obsolete + * eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to + * AUTHENTICATED without ever giving chance to EAP state machine to + * reset the state. + */ + if (!ft_completed) { + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + } + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); + wpa_s->eapol_received = 0; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE || + (wpa_s->current_ssid && + wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) { + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + } else if (!ft_completed) { + /* Timeout for receiving the first EAPOL packet */ + wpa_supplicant_req_auth_timeout(wpa_s, 10, 0); + } + wpa_supplicant_cancel_scan(wpa_s); + + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && + wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + /* + * We are done; the driver will take care of RSN 4-way + * handshake. + */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && + wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + /* + * The driver will take care of RSN 4-way handshake, so we need + * to allow EAPOL supplicant to complete its work without + * waiting for WPA supplicant. + */ + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + } else if (ft_completed) { + /* + * FT protocol completed - make sure EAPOL state machine ends + * up in authenticated. + */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + } + + 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); + if (age.sec == 0 && age.usec < 100000 && + os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == + 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL " + "frame that was received just before " + "association notification"); + wpa_supplicant_rx_eapol( + wpa_s, wpa_s->pending_eapol_rx_src, + wpabuf_head(wpa_s->pending_eapol_rx), + wpabuf_len(wpa_s->pending_eapol_rx)); + } + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = NULL; + } + + if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) && + wpa_s->current_ssid && wpa_drv_get_capa(wpa_s, &capa) == 0 && + capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE) { + /* Set static WEP keys again */ + wpa_set_wep_keys(wpa_s, wpa_s->current_ssid); + } + +#ifdef CONFIG_IBSS_RSN + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_IBSS && + wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && + wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE && + wpa_s->ibss_rsn == NULL) { + wpa_s->ibss_rsn = ibss_rsn_init(wpa_s); + if (!wpa_s->ibss_rsn) { + wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + return; + } + + ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk); + } +#endif /* CONFIG_IBSS_RSN */ +} + + +static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, + u16 reason_code) +{ + const u8 *bssid; + int authenticating; + u8 prev_pending_bssid[ETH_ALEN]; + + authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; + os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* + * At least Host AP driver and a Prism3 card seemed to be + * generating streams of disconnected events when configuring + * IBSS for WPA-None. Ignore them for now. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in " + "IBSS/WPA-None mode"); + return; + } + + if (wpa_s->wpa_state == WPA_4WAY_HANDSHAKE && + wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " + "pre-shared key may be incorrect"); + } + if (!wpa_s->auto_reconnect_disabled || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect enabled: try to " + "reconnect (wps=%d)", + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS); + if (wpa_s->wpa_state >= WPA_ASSOCIATING) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect disabled: do not " + "try to re-connect"); + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + } + bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + wpas_connection_failed(wpa_s, bssid); + wpa_sm_notify_disassoc(wpa_s->wpa); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR + " reason=%d", + MAC2STR(bssid), reason_code); + 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); + } + wpa_supplicant_mark_disassoc(wpa_s); + + if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid); +} + + +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +static void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->pending_mic_error_report) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report"); + wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise); + wpa_s->pending_mic_error_report = 0; +} +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + + +static void +wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int pairwise; + struct os_time 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) || + wpa_s->pending_mic_error_report) { + if (wpa_s->pending_mic_error_report) { + /* + * Send the pending MIC error report immediately since + * we are going to start countermeasures and AP better + * do the same. + */ + wpa_sm_key_request(wpa_s->wpa, 1, + wpa_s->pending_mic_error_pairwise); + } + + /* Send the new MIC error report immediately since we are going + * to start countermeasures and AP better do the same. + */ + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); + + /* initialize countermeasures */ + wpa_s->countermeasures = 1; + wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started"); + + /* + * Need to wait for completion of request frame. We do not get + * any callback for the message completion, so just wait a + * short while and hope for the best. */ + os_sleep(0, 10000); + + wpa_drv_set_countermeasures(wpa_s, 1); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_MICHAEL_MIC_FAILURE); + eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, + wpa_s, NULL); + eloop_register_timeout(60, 0, + wpa_supplicant_stop_countermeasures, + wpa_s, NULL); + /* TODO: mark the AP rejected for 60 second. STA is + * allowed to associate with another AP.. */ + } else { +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT + if (wpa_s->mic_errors_seen) { + /* + * Reduce the effectiveness of Michael MIC error + * reports as a means for attacking against TKIP if + * more than one MIC failure is noticed with the same + * PTK. We delay the transmission of the reports by a + * random time between 0 and 60 seconds in order to + * force the attacker wait 60 seconds before getting + * the information on whether a frame resulted in a MIC + * failure. + */ + u8 rval[4]; + int sec; + + if (os_get_random(rval, sizeof(rval)) < 0) + sec = os_random() % 60; + else + sec = WPA_GET_BE32(rval) % 60; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error " + "report %d seconds", sec); + wpa_s->pending_mic_error_report = 1; + wpa_s->pending_mic_error_pairwise = pairwise; + eloop_cancel_timeout( + wpa_supplicant_delayed_mic_error_report, + wpa_s, NULL); + eloop_register_timeout( + sec, os_random() % 1000000, + wpa_supplicant_delayed_mic_error_report, + wpa_s, NULL); + } else { + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); + } +#else /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + 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->mic_errors_seen++; +} + + +#ifdef CONFIG_TERMINATE_ONLASTIF +static int any_interfaces(struct wpa_supplicant *head) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next) + if (!wpa_s->interface_removed) + return 1; + return 0; +} +#endif /* CONFIG_TERMINATE_ONLASTIF */ + + +static void +wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0) + return; + + switch (data->interface_status.ievent) { + case EVENT_INTERFACE_ADDED: + if (!wpa_s->interface_removed) + break; + wpa_s->interface_removed = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added"); + if (wpa_supplicant_driver_init(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the " + "driver after interface was added"); + } + break; + case EVENT_INTERFACE_REMOVED: + wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed"); + wpa_s->interface_removed = 1; + wpa_supplicant_mark_disassoc(wpa_s); + 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)) + eloop_terminate(); +#endif /* CONFIG_TERMINATE_ONLASTIF */ + break; + } +} + + +#ifdef CONFIG_PEERKEY +static void +wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer); +} +#endif /* CONFIG_PEERKEY */ + + +#ifdef CONFIG_TDLS +static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + switch (data->tdls.oper) { + case TDLS_REQUEST_SETUP: + wpa_tdls_start(wpa_s->wpa, data->tdls.peer); + break; + case TDLS_REQUEST_TEARDOWN: + /* request from driver to add FTIE */ + wpa_tdls_recv_teardown_notify(wpa_s->wpa, data->tdls.peer, + data->tdls.reason_code); + break; + } +} +#endif /* CONFIG_TDLS */ + + +#ifdef CONFIG_IEEE80211R +static void +wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + + if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies, + data->ft_ies.ies_len, + data->ft_ies.ft_action, + data->ft_ies.target_ap, + data->ft_ies.ric_ies, + data->ft_ies.ric_ies_len) < 0) { + /* TODO: prevent MLME/driver from trying to associate? */ + } +} +#endif /* CONFIG_IEEE80211R */ + + +#ifdef CONFIG_IBSS_RSN +static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + struct wpa_ssid *ssid; + if (wpa_s->wpa_state < WPA_ASSOCIATED) + return; + if (data == NULL) + return; + ssid = wpa_s->current_ssid; + if (ssid == NULL) + return; + if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt)) + return; + + ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer); +} +#endif /* CONFIG_IBSS_RSN */ + + +#ifdef CONFIG_IEEE80211R +static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data, + size_t len) +{ + const u8 *sta_addr, *target_ap_addr; + u16 status; + + wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len); + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + return; /* only SME case supported for now */ + if (len < 1 + 2 * ETH_ALEN + 2) + return; + if (data[0] != 2) + return; /* Only FT Action Response is supported for now */ + sta_addr = data + 1; + target_ap_addr = data + 1 + ETH_ALEN; + status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN); + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA " + MACSTR " TargetAP " MACSTR " status %u", + MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); + + if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR + " in FT Action Response", MAC2STR(sta_addr)); + return; + } + + if (status) { + wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates " + "failure (status code %d)", status); + /* TODO: report error to FT code(?) */ + return; + } + + if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2, + len - (1 + 2 * ETH_ALEN + 2), 1, + target_ap_addr, NULL, 0) < 0) + return; + +#ifdef CONFIG_SME + { + struct wpa_bss *bss; + bss = wpa_bss_get_bssid(wpa_s, target_ap_addr); + if (bss) + wpa_s->sme.freq = bss->freq; + wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT; + sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr, + WLAN_AUTH_FT); + } +#endif /* CONFIG_SME */ +} +#endif /* CONFIG_IEEE80211R */ + + +static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s, + struct unprot_deauth *e) +{ +#ifdef CONFIG_IEEE80211W + wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame " + "dropped: " MACSTR " -> " MACSTR + " (reason code %u)", + MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); + sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s, + struct unprot_disassoc *e) +{ +#ifdef CONFIG_IEEE80211W + wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame " + "dropped: " MACSTR " -> " MACSTR + " (reason code %u)", + MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); + sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); +#endif /* CONFIG_IEEE80211W */ +} + + +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct wpa_supplicant *wpa_s = ctx; + u16 reason_code = 0; + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && + event != EVENT_INTERFACE_ENABLED && + event != EVENT_INTERFACE_STATUS) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore event %d while interface is " + "disabled", event); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "Event %d received on interface %s", + event, wpa_s->ifname); + + switch (event) { + case EVENT_AUTH: + sme_event_auth(wpa_s, data); + break; + case EVENT_ASSOC: + wpa_supplicant_event_assoc(wpa_s, data); + break; + case EVENT_DISASSOC: + wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); + if (data) { + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u", + data->disassoc_info.reason_code); + if (data->disassoc_info.addr) + wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, + MAC2STR(data->disassoc_info.addr)); + } +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data && data->disassoc_info.addr) { + hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], + data->disassoc_info.addr); + break; + } +#endif /* CONFIG_AP */ + if (data) { + reason_code = data->disassoc_info.reason_code; + wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", + data->disassoc_info.ie, + data->disassoc_info.ie_len); +#ifdef CONFIG_P2P + wpas_p2p_disassoc_notif( + wpa_s, data->disassoc_info.addr, reason_code, + data->disassoc_info.ie, + data->disassoc_info.ie_len); +#endif /* CONFIG_P2P */ + } + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_disassoc(wpa_s, data); + /* fall through */ + case EVENT_DEAUTH: + if (event == EVENT_DEAUTH) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Deauthentication notification"); + if (data) { + reason_code = data->deauth_info.reason_code; + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u", + data->deauth_info.reason_code); + if (data->deauth_info.addr) { + wpa_dbg(wpa_s, MSG_DEBUG, " * address " + MACSTR, + MAC2STR(data->deauth_info. + addr)); + } + wpa_hexdump(MSG_DEBUG, + "Deauthentication frame IE(s)", + data->deauth_info.ie, + data->deauth_info.ie_len); +#ifdef CONFIG_P2P + wpas_p2p_deauth_notif( + wpa_s, data->deauth_info.addr, + reason_code, + data->deauth_info.ie, + data->deauth_info.ie_len); +#endif /* CONFIG_P2P */ + } + } +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data && data->deauth_info.addr) { + hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], + data->deauth_info.addr); + break; + } +#endif /* CONFIG_AP */ + wpa_supplicant_event_disassoc(wpa_s, reason_code); + break; + case EVENT_MICHAEL_MIC_FAILURE: + wpa_supplicant_event_michael_mic_failure(wpa_s, data); + break; +#ifndef CONFIG_NO_SCAN_PROCESSING + case EVENT_SCAN_RESULTS: + wpa_supplicant_event_scan_results(wpa_s, data); + break; +#endif /* CONFIG_NO_SCAN_PROCESSING */ + case EVENT_ASSOCINFO: + wpa_supplicant_event_associnfo(wpa_s, data); + break; + case EVENT_INTERFACE_STATUS: + wpa_supplicant_event_interface_status(wpa_s, data); + break; + case EVENT_PMKID_CANDIDATE: + wpa_supplicant_event_pmkid_candidate(wpa_s, data); + break; +#ifdef CONFIG_PEERKEY + case EVENT_STKSTART: + wpa_supplicant_event_stkstart(wpa_s, data); + break; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_TDLS + case EVENT_TDLS: + wpa_supplicant_event_tdls(wpa_s, data); + break; +#endif /* CONFIG_TDLS */ +#ifdef CONFIG_IEEE80211R + case EVENT_FT_RESPONSE: + wpa_supplicant_event_ft_response(wpa_s, data); + break; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IBSS_RSN + case EVENT_IBSS_RSN_START: + wpa_supplicant_event_ibss_rsn_start(wpa_s, data); + break; +#endif /* CONFIG_IBSS_RSN */ + case EVENT_ASSOC_REJECT: + if (data->assoc_reject.bssid) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT + "bssid=" MACSTR " status_code=%u", + MAC2STR(data->assoc_reject.bssid), + data->assoc_reject.status_code); + else + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT + "status_code=%u", + data->assoc_reject.status_code); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_assoc_reject(wpa_s, data); + break; + case EVENT_AUTH_TIMED_OUT: + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_auth_timed_out(wpa_s, data); + break; + case EVENT_ASSOC_TIMED_OUT: + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_assoc_timed_out(wpa_s, data); + break; +#ifdef CONFIG_AP + case EVENT_TX_STATUS: + wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR + " type=%d stype=%d", + MAC2STR(data->tx_status.dst), + data->tx_status.type, data->tx_status.stype); + if (wpa_s->ap_iface == NULL) { +#ifdef CONFIG_P2P + if (data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_ACTION) + wpas_send_action_tx_status( + wpa_s, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack ? + P2P_SEND_ACTION_SUCCESS : + P2P_SEND_ACTION_NO_ACK); +#endif /* CONFIG_P2P */ + break; + } +#ifdef CONFIG_P2P + wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst=" + MACSTR, MAC2STR(wpa_s->parent->pending_action_dst)); + /* + * Catch TX status events for Action frames we sent via group + * interface in GO mode. + */ + if (data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_ACTION && + os_memcmp(wpa_s->parent->pending_action_dst, + data->tx_status.dst, ETH_ALEN) == 0) { + wpas_send_action_tx_status( + wpa_s->parent, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack ? + P2P_SEND_ACTION_SUCCESS : + P2P_SEND_ACTION_NO_ACK); + break; + } +#endif /* CONFIG_P2P */ + switch (data->tx_status.type) { + case WLAN_FC_TYPE_MGMT: + ap_mgmt_tx_cb(wpa_s, data->tx_status.data, + data->tx_status.data_len, + data->tx_status.stype, + data->tx_status.ack); + break; + case WLAN_FC_TYPE_DATA: + ap_tx_status(wpa_s, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack); + break; + } + break; + case EVENT_RX_FROM_UNKNOWN: + if (wpa_s->ap_iface == NULL) + break; + ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.frame, + data->rx_from_unknown.len); + break; + case EVENT_RX_MGMT: + if (wpa_s->ap_iface == NULL) { +#ifdef CONFIG_P2P + u16 fc, stype; + const struct ieee80211_mgmt *mgmt; + mgmt = (const struct ieee80211_mgmt *) + data->rx_mgmt.frame; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + if (stype == WLAN_FC_STYPE_PROBE_REQ && + data->rx_mgmt.frame_len > 24) { + const u8 *src = mgmt->sa; + const u8 *ie = mgmt->u.probe_req.variable; + size_t ie_len = data->rx_mgmt.frame_len - + (mgmt->u.probe_req.variable - + data->rx_mgmt.frame); + wpas_p2p_probe_req_rx(wpa_s, src, ie, ie_len); + break; + } +#endif /* CONFIG_P2P */ + wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " + "management frame in non-AP mode"); + break; + } + ap_mgmt_rx(wpa_s, &data->rx_mgmt); + break; +#endif /* CONFIG_AP */ + 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_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) + break; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + hostapd_probe_req_rx(wpa_s->ap_iface->bss[0], + data->rx_probe_req.sa, + data->rx_probe_req.ie, + data->rx_probe_req.ie_len); + break; + } +#endif /* CONFIG_AP */ +#ifdef CONFIG_P2P + wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa, + data->rx_probe_req.ie, + data->rx_probe_req.ie_len); +#endif /* CONFIG_P2P */ + break; +#ifdef CONFIG_P2P + case EVENT_REMAIN_ON_CHANNEL: + wpas_p2p_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq, + data->remain_on_channel.duration); + break; + case EVENT_CANCEL_REMAIN_ON_CHANNEL: + wpas_p2p_cancel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq); + break; + 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); + 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 */ +#ifdef CONFIG_CLIENT_MLME + case EVENT_MLME_RX: { + struct ieee80211_rx_status rx_status; + os_memset(&rx_status, 0, sizeof(rx_status)); + rx_status.freq = data->mlme_rx.freq; + rx_status.channel = data->mlme_rx.channel; + rx_status.ssi = data->mlme_rx.ssi; + ieee80211_sta_rx(wpa_s, data->mlme_rx.buf, data->mlme_rx.len, + &rx_status); + break; + } +#endif /* CONFIG_CLIENT_MLME */ + 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: + bgscan_notify_signal_change( + wpa_s, data->signal_change.above_threshold, + data->signal_change.current_signal, + data->signal_change.current_noise, + data->signal_change.current_txrate); + break; + case EVENT_INTERFACE_ENABLED: + wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled"); + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { +#ifdef CONFIG_AP + if (!wpa_s->ap_iface) { + wpa_supplicant_set_state(wpa_s, + WPA_DISCONNECTED); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else + wpa_supplicant_set_state(wpa_s, + WPA_COMPLETED); +#else /* CONFIG_AP */ + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + wpa_supplicant_req_scan(wpa_s, 0, 0); +#endif /* CONFIG_AP */ + } + break; + case EVENT_INTERFACE_DISABLED: + wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled"); + wpa_supplicant_mark_disassoc(wpa_s); + 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 */ +#ifdef CONFIG_P2P + wpas_p2p_update_channel_list(wpa_s); +#endif /* CONFIG_P2P */ + 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 " + "(%d %d %d)", + data->best_chan.freq_24, data->best_chan.freq_5, + data->best_chan.freq_overall); + 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, + &data->unprot_deauth); + break; + case EVENT_UNPROT_DISASSOC: + wpa_supplicant_event_unprot_disassoc(wpa_s, + &data->unprot_disassoc); + break; + case EVENT_STATION_LOW_ACK: +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data) + hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0], + data->low_ack.addr); +#endif /* CONFIG_AP */ + break; + case EVENT_IBSS_PEER_LOST: +#ifdef CONFIG_IBSS_RSN + ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer); +#endif /* CONFIG_IBSS_RSN */ + break; + default: + wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); + break; + } +} diff --git a/wpa_supplicant/examples/60_wpa_supplicant b/wpa_supplicant/examples/60_wpa_supplicant new file mode 100755 index 0000000..39bd8e0 --- /dev/null +++ b/wpa_supplicant/examples/60_wpa_supplicant @@ -0,0 +1,19 @@ +#!/bin/sh + +# /etc/pm/sleep.d/60_wpa_supplicant +# Action script to notify wpa_supplicant of pm-action events. + +PATH=/sbin:/usr/sbin:/bin:/usr/bin + +WPACLI=wpa_cli + +case "$1" in + suspend|hibernate) + $WPACLI suspend + ;; + resume|thaw) + $WPACLI resume + ;; +esac + +exit 0 diff --git a/wpa_supplicant/examples/ieee8021x.conf b/wpa_supplicant/examples/ieee8021x.conf new file mode 100644 index 0000000..e8a5503 --- /dev/null +++ b/wpa_supplicant/examples/ieee8021x.conf @@ -0,0 +1,13 @@ +# IEEE 802.1X with dynamic WEP keys using EAP-PEAP/MSCHAPv2 + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example 802.1x network" + key_mgmt=IEEE8021X + eap=PEAP + phase2="auth=MSCHAPV2" + identity="user name" + password="password" + ca_cert="/etc/cert/ca.pem" +} diff --git a/wpa_supplicant/examples/openCryptoki.conf b/wpa_supplicant/examples/openCryptoki.conf new file mode 100644 index 0000000..e2301a6 --- /dev/null +++ b/wpa_supplicant/examples/openCryptoki.conf @@ -0,0 +1,41 @@ +# EAP-TLS using private key and certificates via OpenSSL PKCS#11 engine and +# openCryptoki (e.g., with TPM token) + +# This example uses following PKCS#11 objects: +# $ pkcs11-tool --module /usr/lib/opencryptoki/libopencryptoki.so -O -l +# Please enter User PIN: +# Private Key Object; RSA +# label: rsakey +# ID: 04 +# Usage: decrypt, sign, unwrap +# Certificate Object, type = X.509 cert +# label: ca +# ID: 01 +# Certificate Object, type = X.509 cert +# label: cert +# ID: 04 + +# Configure OpenSSL to load the PKCS#11 engine and openCryptoki module +pkcs11_engine_path=/usr/lib/engines/engine_pkcs11.so +pkcs11_module_path=/usr/lib/opencryptoki/libopencryptoki.so + +network={ + ssid="test network" + key_mgmt=WPA-EAP + eap=TLS + identity="User" + + # use OpenSSL PKCS#11 engine for this network + engine=1 + engine_id="pkcs11" + + # select the private key and certificates based on ID (see pkcs11-tool + # output above) + key_id="4" + cert_id="4" + ca_cert_id="1" + + # set the PIN code; leave this out to configure the PIN to be requested + # interactively when needed (e.g., via wpa_gui or wpa_cli) + pin="123456" +} diff --git a/wpa_supplicant/examples/p2p-action-udhcp.sh b/wpa_supplicant/examples/p2p-action-udhcp.sh new file mode 100755 index 0000000..d7d0e79 --- /dev/null +++ b/wpa_supplicant/examples/p2p-action-udhcp.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +IFNAME=$1 +CMD=$2 + +kill_daemon() { + NAME=$1 + PF=$2 + + if [ ! -r $PF ]; then + return + fi + + PID=`cat $PF` + if [ $PID -gt 0 ]; then + if ps $PID | grep -q $NAME; then + kill $PID + fi + fi + rm $PF +} + +if [ "$CMD" = "P2P-GROUP-STARTED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + ifconfig $GIFNAME 192.168.42.1 up + udhcpd /etc/udhcpd-p2p.conf + fi + if [ "$4" = "client" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid + udhcpc -i $GIFNAME -p /var/run/udhcpc-$GIFNAME.pid \ + -s /etc/udhcpc.script + fi +fi + +if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid + ifconfig $GIFNAME 0.0.0.0 + fi + if [ "$4" = "client" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + ifconfig $GIFNAME 0.0.0.0 + fi +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # enable NAT/masquarade $GIFNAME -> $UPLINK + iptables -P FORWARD DROP + iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE + iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT + sysctl net.ipv4.ip_forward=1 +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # disable NAT/masquarade $GIFNAME -> $UPLINK + sysctl net.ipv4.ip_forward=0 + iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE + iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT +fi diff --git a/wpa_supplicant/examples/p2p-action.sh b/wpa_supplicant/examples/p2p-action.sh new file mode 100755 index 0000000..8759f54 --- /dev/null +++ b/wpa_supplicant/examples/p2p-action.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +IFNAME=$1 +CMD=$2 + +kill_daemon() { + NAME=$1 + PF=$2 + + if [ ! -r $PF ]; then + return + fi + + PID=`cat $PF` + if [ $PID -gt 0 ]; then + if ps $PID | grep -q $NAME; then + kill $PID + fi + fi + rm $PF +} + +if [ "$CMD" = "P2P-GROUP-STARTED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid + rm /var/run/dhclient.leases-$GIFNAME + kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME + ifconfig $GIFNAME 192.168.42.1 up + if ! dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \ + -i $GIFNAME \ + -F192.168.42.11,192.168.42.99; then + # another dnsmasq instance may be running and blocking us; try to + # 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 + 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 + dhclient -pf /var/run/dhclient-$GIFNAME.pid \ + -lf /var/run/dhclient.leases-$GIFNAME \ + -nw \ + $GIFNAME + fi +fi + +if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME + ifconfig $GIFNAME 0.0.0.0 + fi + if [ "$4" = "client" ]; then + kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid + rm /var/run/dhclient.leases-$GIFNAME + ifconfig $GIFNAME 0.0.0.0 + fi +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # enable NAT/masquarade $GIFNAME -> $UPLINK + iptables -P FORWARD DROP + iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE + iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT + sysctl net.ipv4.ip_forward=1 +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # disable NAT/masquarade $GIFNAME -> $UPLINK + sysctl net.ipv4.ip_forward=0 + iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE + iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT +fi diff --git a/wpa_supplicant/examples/plaintext.conf b/wpa_supplicant/examples/plaintext.conf new file mode 100644 index 0000000..542ac1d --- /dev/null +++ b/wpa_supplicant/examples/plaintext.conf @@ -0,0 +1,8 @@ +# Plaintext (no encryption) network + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example open network" + key_mgmt=NONE +} diff --git a/wpa_supplicant/examples/udhcpd-p2p.conf b/wpa_supplicant/examples/udhcpd-p2p.conf new file mode 100644 index 0000000..b94f94f --- /dev/null +++ b/wpa_supplicant/examples/udhcpd-p2p.conf @@ -0,0 +1,120 @@ +# Sample udhcpd configuration file (/etc/udhcpd.conf) + +# The start and end of the IP lease block + +start 192.168.42.20 #default: 192.168.0.20 +end 192.168.42.254 #default: 192.168.0.254 + + +# The interface that udhcpd will use + +interface wlan2 #default: eth0 + + +# The maximim number of leases (includes addressesd reserved +# by OFFER's, DECLINE's, and ARP conficts + +#max_leases 254 #default: 254 + + +# If remaining is true (default), udhcpd will store the time +# remaining for each lease in the udhcpd leases file. This is +# for embedded systems that cannot keep time between reboots. +# If you set remaining to no, the absolute time that the lease +# expires at will be stored in the dhcpd.leases file. + +#remaining yes #default: yes + + +# The time period at which udhcpd will write out a dhcpd.leases +# file. If this is 0, udhcpd will never automatically write a +# lease file. (specified in seconds) + +#auto_time 7200 #default: 7200 (2 hours) + + +# The amount of time that an IP will be reserved (leased) for if a +# DHCP decline message is received (seconds). + +#decline_time 3600 #default: 3600 (1 hour) + + +# The amount of time that an IP will be reserved (leased) for if an +# ARP conflct occurs. (seconds + +#conflict_time 3600 #default: 3600 (1 hour) + + +# How long an offered address is reserved (leased) in seconds + +#offer_time 60 #default: 60 (1 minute) + +# If a lease to be given is below this value, the full lease time is +# instead used (seconds). + +#min_lease 60 #defult: 60 + + +# The location of the leases file + +#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases + +# The location of the pid file +pidfile /var/run/udhcpd-wlan2.pid #default: /var/run/udhcpd.pid + +# Everytime udhcpd writes a leases file, the below script will be called. +# Useful for writing the lease file to flash every few hours. + +#notify_file #default: (no script) + +#notify_file dumpleases # <--- usefull for debugging + +# The following are bootp specific options, setable by udhcpd. + +#siaddr 192.168.0.22 #default: 0.0.0.0 + +#sname zorak #default: (none) + +#boot_file /var/nfs_root #default: (none) + +# The remainer of options are DHCP options and can be specifed with the +# keyword 'opt' or 'option'. If an option can take multiple items, such +# as the dns option, they can be listed on the same line, or multiple +# lines. The only option with a default is 'lease'. + +#Examles +opt dns 192.168.2.1 +option subnet 255.255.255.0 +option domain atherosowl.com +option lease 864000 # 10 days of seconds + + +# Currently supported options, for more info, see options.c +#opt subnet +#opt timezone +#opt router +#opt timesvr +#opt namesvr +#opt dns +#opt logsvr +#opt cookiesvr +#opt lprsvr +#opt bootsize +#opt domain +#opt swapsvr +#opt rootpath +#opt ipttl +#opt mtu +#opt broadcast +#opt wins +#opt lease +#opt ntpsrv +#opt tftp +#opt bootfile + + +# Static leases map +#static_lease 00:60:08:11:CE:4E 192.168.0.54 +#static_lease 00:60:08:11:CE:3E 192.168.0.44 + + diff --git a/wpa_supplicant/examples/wep.conf b/wpa_supplicant/examples/wep.conf new file mode 100644 index 0000000..9c7b55f --- /dev/null +++ b/wpa_supplicant/examples/wep.conf @@ -0,0 +1,11 @@ +# Static WEP keys + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example wep network" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_tx_keyidx=0 +} diff --git a/wpa_supplicant/examples/wpa-psk-tkip.conf b/wpa_supplicant/examples/wpa-psk-tkip.conf new file mode 100644 index 0000000..93d7fc2 --- /dev/null +++ b/wpa_supplicant/examples/wpa-psk-tkip.conf @@ -0,0 +1,12 @@ +# WPA-PSK/TKIP + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example wpa-psk network" + key_mgmt=WPA-PSK + proto=WPA + pairwise=TKIP + group=TKIP + psk="secret passphrase" +} diff --git a/wpa_supplicant/examples/wpa2-eap-ccmp.conf b/wpa_supplicant/examples/wpa2-eap-ccmp.conf new file mode 100644 index 0000000..d7a64d8 --- /dev/null +++ b/wpa_supplicant/examples/wpa2-eap-ccmp.conf @@ -0,0 +1,15 @@ +# WPA2-EAP/CCMP using EAP-TLS + +ctrl_interface=/var/run/wpa_supplicant + +network={ + ssid="example wpa2-eap network" + key_mgmt=WPA-EAP + proto=WPA2 + pairwise=CCMP + group=CCMP + eap=TLS + ca_cert="/etc/cert/ca.pem" + private_key="/etc/cert/user.p12" + private_key_passwd="PKCS#12 passhrase" +} diff --git a/wpa_supplicant/examples/wpas-dbus-new-getall.py b/wpa_supplicant/examples/wpas-dbus-new-getall.py new file mode 100755 index 0000000..03da187 --- /dev/null +++ b/wpa_supplicant/examples/wpas-dbus-new-getall.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +import dbus +import sys, os +import time +import gobject + +def main(): + bus = dbus.SystemBus() + wpas_obj = bus.get_object("fi.w1.wpa_supplicant1", + "/fi/w1/wpa_supplicant1") + props = wpas_obj.GetAll("fi.w1.wpa_supplicant1", + dbus_interface=dbus.PROPERTIES_IFACE) + print "GetAll(fi.w1.wpa_supplicant1, /fi/w1/wpa_supplicant1):" + print props + + if len(sys.argv) != 2: + os._exit(1) + + ifname = sys.argv[1] + + wpas = dbus.Interface(wpas_obj, "fi.w1.wpa_supplicant1") + path = wpas.GetInterface(ifname) + if_obj = bus.get_object("fi.w1.wpa_supplicant1", path) + props = if_obj.GetAll("fi.w1.wpa_supplicant1.Interface", + dbus_interface=dbus.PROPERTIES_IFACE) + print + print "GetAll(fi.w1.wpa_supplicant1.Interface, %s):" % (path) + print props + + props = if_obj.GetAll("fi.w1.wpa_supplicant1.Interface.WPS", + dbus_interface=dbus.PROPERTIES_IFACE) + print + print "GetAll(fi.w1.wpa_supplicant1.Interface.WPS, %s):" % (path) + print props + + res = if_obj.Get("fi.w1.wpa_supplicant1.Interface", 'BSSs', + dbus_interface=dbus.PROPERTIES_IFACE) + if len(res) > 0: + bss_obj = bus.get_object("fi.w1.wpa_supplicant1", res[0]) + props = bss_obj.GetAll("fi.w1.wpa_supplicant1.BSS", + dbus_interface=dbus.PROPERTIES_IFACE) + print + print "GetAll(fi.w1.wpa_supplicant1.BSS, %s):" % (res[0]) + print props + + res = if_obj.Get("fi.w1.wpa_supplicant1.Interface", 'Networks', + dbus_interface=dbus.PROPERTIES_IFACE) + if len(res) > 0: + net_obj = bus.get_object("fi.w1.wpa_supplicant1", res[0]) + props = net_obj.GetAll("fi.w1.wpa_supplicant1.Network", + dbus_interface=dbus.PROPERTIES_IFACE) + print + print "GetAll(fi.w1.wpa_supplicant1.Network, %s):" % (res[0]) + print props + +if __name__ == "__main__": + main() + diff --git a/wpa_supplicant/examples/wpas-dbus-new-signals.py b/wpa_supplicant/examples/wpas-dbus-new-signals.py new file mode 100755 index 0000000..b040e0a --- /dev/null +++ b/wpa_supplicant/examples/wpas-dbus-new-signals.py @@ -0,0 +1,203 @@ +#!/usr/bin/python + +import dbus +import sys, os +import time +import gobject +from dbus.mainloop.glib import DBusGMainLoop + +WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1" + +WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface" +WPAS_DBUS_INTERFACES_OPATH = "/fi/w1/wpa_supplicant1/Interfaces" +WPAS_DBUS_BSS_INTERFACE = "fi.w1.wpa_supplicant1.BSS" +WPAS_DBUS_NETWORK_INTERFACE = "fi.w1.wpa_supplicant1.Network" + +def byte_array_to_string(s): + import urllib + r = "" + for c in s: + if c >= 32 and c < 127: + r += "%c" % c + else: + r += urllib.quote(chr(c)) + return r + +def list_interfaces(wpas_obj): + ifaces = wpas_obj.Get(WPAS_DBUS_INTERFACE, 'Interfaces', + dbus_interface=dbus.PROPERTIES_IFACE) + for path in ifaces: + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + ifname = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'Ifname', + dbus_interface=dbus.PROPERTIES_IFACE) + print ifname + +def interfaceAdded(interface, properties): + print "InterfaceAdded(%s): Ifname=%s" % (interface, properties['Ifname']) + +def interfaceRemoved(interface): + print "InterfaceRemoved(%s)" % (interface) + +def propertiesChanged(properties): + for i in properties: + print "PropertiesChanged: %s=%s" % (i, properties[i]) + +def showBss(bss): + net_obj = bus.get_object(WPAS_DBUS_SERVICE, bss) + net = dbus.Interface(net_obj, WPAS_DBUS_BSS_INTERFACE) + + # Convert the byte-array for SSID and BSSID to printable strings + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'BSSID', + dbus_interface=dbus.PROPERTIES_IFACE) + bssid = "" + for item in val: + bssid = bssid + ":%02x" % item + bssid = bssid[1:] + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'SSID', + dbus_interface=dbus.PROPERTIES_IFACE) + ssid = byte_array_to_string(val) + + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPAIE', + dbus_interface=dbus.PROPERTIES_IFACE) + wpa = "no" + if val != None: + wpa = "yes" + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSNIE', + dbus_interface=dbus.PROPERTIES_IFACE) + wpa2 = "no" + if val != None: + wpa2 = "yes" + freq = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Frequency', + dbus_interface=dbus.PROPERTIES_IFACE) + signal = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Signal', + dbus_interface=dbus.PROPERTIES_IFACE) + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Rates', + dbus_interface=dbus.PROPERTIES_IFACE) + if len(val) > 0: + maxrate = val[0] / 1000000 + else: + maxrate = 0 + + print " %s :: ssid='%s' wpa=%s wpa2=%s signal=%d rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq) + +def scanDone(success): + gobject.MainLoop().quit() + print "Scan done: success=%s" % success + +def scanDone2(success, path=None): + print "Scan done: success=%s [path=%s]" % (success, path) + +def bssAdded(bss, properties): + print "BSS added: %s" % (bss) + showBss(bss) + +def bssRemoved(bss): + print "BSS removed: %s" % (bss) + +def blobAdded(blob): + print "BlobAdded(%s)" % (blob) + +def blobRemoved(blob): + print "BlobRemoved(%s)" % (blob) + +def networkAdded(network, properties): + print "NetworkAdded(%s)" % (network) + +def networkRemoved(network): + print "NetworkRemoved(%s)" % (network) + +def networkSelected(network): + print "NetworkSelected(%s)" % (network) + +def propertiesChangedInterface(properties): + for i in properties: + print "PropertiesChanged(interface): %s=%s" % (i, properties[i]) + +def propertiesChangedBss(properties): + for i in properties: + print "PropertiesChanged(BSS): %s=%s" % (i, properties[i]) + +def propertiesChangedNetwork(properties): + for i in properties: + print "PropertiesChanged(Network): %s=%s" % (i, properties[i]) + +def main(): + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + global bus + bus = dbus.SystemBus() + wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) + + if len(sys.argv) != 2: + list_interfaces(wpas_obj) + os._exit(1) + + wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) + bus.add_signal_receiver(interfaceAdded, + dbus_interface=WPAS_DBUS_INTERFACE, + signal_name="InterfaceAdded") + bus.add_signal_receiver(interfaceRemoved, + dbus_interface=WPAS_DBUS_INTERFACE, + signal_name="InterfaceRemoved") + bus.add_signal_receiver(propertiesChanged, + dbus_interface=WPAS_DBUS_INTERFACE, + signal_name="PropertiesChanged") + + ifname = sys.argv[1] + path = wpas.GetInterface(ifname) + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE) + iface.connect_to_signal("ScanDone", scanDone2, + path_keyword='path') + + bus.add_signal_receiver(scanDone, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="ScanDone", + path=path) + bus.add_signal_receiver(bssAdded, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="BSSAdded", + path=path) + bus.add_signal_receiver(bssRemoved, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="BSSRemoved", + path=path) + bus.add_signal_receiver(blobAdded, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="BlobAdded", + path=path) + bus.add_signal_receiver(blobRemoved, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="BlobRemoved", + path=path) + bus.add_signal_receiver(networkAdded, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="NetworkAdded", + path=path) + bus.add_signal_receiver(networkRemoved, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="NetworkRemoved", + path=path) + bus.add_signal_receiver(networkSelected, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="NetworkSelected", + path=path) + bus.add_signal_receiver(propertiesChangedInterface, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="PropertiesChanged", + path=path) + + bus.add_signal_receiver(propertiesChangedBss, + dbus_interface=WPAS_DBUS_BSS_INTERFACE, + signal_name="PropertiesChanged") + + bus.add_signal_receiver(propertiesChangedNetwork, + dbus_interface=WPAS_DBUS_NETWORK_INTERFACE, + signal_name="PropertiesChanged") + + gobject.MainLoop().run() + +if __name__ == "__main__": + main() + diff --git a/wpa_supplicant/examples/wpas-dbus-new-wps.py b/wpa_supplicant/examples/wpas-dbus-new-wps.py new file mode 100755 index 0000000..b886385 --- /dev/null +++ b/wpa_supplicant/examples/wpas-dbus-new-wps.py @@ -0,0 +1,80 @@ +#!/usr/bin/python + +import dbus +import sys, os +import time +import gobject +from dbus.mainloop.glib import DBusGMainLoop + +WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1" + +WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface" +WPAS_DBUS_WPS_INTERFACE = "fi.w1.wpa_supplicant1.Interface.WPS" + +def propertiesChanged(properties): + if properties.has_key("State"): + print "PropertiesChanged: State: %s" % (properties["State"]) + +def scanDone(success): + print "Scan done: success=%s" % success + +def bssAdded(bss, properties): + print "BSS added: %s" % (bss) + +def bssRemoved(bss): + print "BSS removed: %s" % (bss) + +def wpsEvent(name, args): + print "WPS event: %s" % (name) + print args + +def credentials(cred): + print "WPS credentials: %s" % (cred) + +def main(): + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + global bus + bus = dbus.SystemBus() + wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) + + if len(sys.argv) != 2: + print "Missing ifname argument" + os._exit(1) + + wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) + bus.add_signal_receiver(scanDone, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="ScanDone") + bus.add_signal_receiver(bssAdded, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="BSSAdded") + bus.add_signal_receiver(bssRemoved, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="BSSRemoved") + bus.add_signal_receiver(propertiesChanged, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="PropertiesChanged") + bus.add_signal_receiver(wpsEvent, + dbus_interface=WPAS_DBUS_WPS_INTERFACE, + signal_name="Event") + bus.add_signal_receiver(credentials, + dbus_interface=WPAS_DBUS_WPS_INTERFACE, + signal_name="Credentials") + + ifname = sys.argv[1] + + path = wpas.GetInterface(ifname) + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + if_obj.Set(WPAS_DBUS_WPS_INTERFACE, 'ProcessCredentials', + dbus.Boolean(1), + dbus_interface=dbus.PROPERTIES_IFACE) + wps = dbus.Interface(if_obj, WPAS_DBUS_WPS_INTERFACE) + wps.Start({'Role': 'enrollee', 'Type': 'pbc'}) + + gobject.MainLoop().run() + +if __name__ == "__main__": + main() + diff --git a/wpa_supplicant/examples/wpas-dbus-new.py b/wpa_supplicant/examples/wpas-dbus-new.py new file mode 100755 index 0000000..25072ce --- /dev/null +++ b/wpa_supplicant/examples/wpas-dbus-new.py @@ -0,0 +1,149 @@ +#!/usr/bin/python + +import dbus +import sys, os +import time +import gobject +from dbus.mainloop.glib import DBusGMainLoop + +WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1" + +WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface" +WPAS_DBUS_INTERFACES_OPATH = "/fi/w1/wpa_supplicant1/Interfaces" +WPAS_DBUS_BSS_INTERFACE = "fi.w1.wpa_supplicant1.BSS" + +def byte_array_to_string(s): + import urllib + r = "" + for c in s: + if c >= 32 and c < 127: + r += "%c" % c + else: + r += urllib.quote(chr(c)) + return r + +def list_interfaces(wpas_obj): + ifaces = wpas_obj.Get(WPAS_DBUS_INTERFACE, 'Interfaces', + dbus_interface=dbus.PROPERTIES_IFACE) + for path in ifaces: + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + ifname = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'Ifname', + dbus_interface=dbus.PROPERTIES_IFACE) + print ifname + +def propertiesChanged(properties): + if properties.has_key("State"): + print "PropertiesChanged: State: %s" % (properties["State"]) + +def showBss(bss): + net_obj = bus.get_object(WPAS_DBUS_SERVICE, bss) + net = dbus.Interface(net_obj, WPAS_DBUS_BSS_INTERFACE) + + # Convert the byte-array for SSID and BSSID to printable strings + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'BSSID', + dbus_interface=dbus.PROPERTIES_IFACE) + bssid = "" + for item in val: + bssid = bssid + ":%02x" % item + bssid = bssid[1:] + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'SSID', + dbus_interface=dbus.PROPERTIES_IFACE) + ssid = byte_array_to_string(val) + + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPA', + dbus_interface=dbus.PROPERTIES_IFACE) + wpa = "no" + if len(val["KeyMgmt"]) > 0: + wpa = "yes" + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSN', + dbus_interface=dbus.PROPERTIES_IFACE) + wpa2 = "no" + if len(val["KeyMgmt"]) > 0: + wpa2 = "yes" + freq = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Frequency', + dbus_interface=dbus.PROPERTIES_IFACE) + signal = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Signal', + dbus_interface=dbus.PROPERTIES_IFACE) + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Rates', + dbus_interface=dbus.PROPERTIES_IFACE) + if len(val) > 0: + maxrate = val[0] / 1000000 + else: + maxrate = 0 + + print " %s :: ssid='%s' wpa=%s wpa2=%s signal=%d rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq) + +def scanDone(success): + print "Scan done: success=%s" % success + + res = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'BSSs', + dbus_interface=dbus.PROPERTIES_IFACE) + + print "Scanned wireless networks:" + for opath in res: + print opath + showBss(opath) + +def bssAdded(bss, properties): + print "BSS added: %s" % (bss) + showBss(bss) + +def bssRemoved(bss): + print "BSS removed: %s" % (bss) + +def main(): + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + global bus + bus = dbus.SystemBus() + wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) + + if len(sys.argv) != 2: + list_interfaces(wpas_obj) + os._exit(1) + + wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) + bus.add_signal_receiver(scanDone, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="ScanDone") + bus.add_signal_receiver(bssAdded, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="BSSAdded") + bus.add_signal_receiver(bssRemoved, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="BSSRemoved") + bus.add_signal_receiver(propertiesChanged, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="PropertiesChanged") + + ifname = sys.argv[1] + + # See if wpa_supplicant already knows about this interface + path = None + try: + path = wpas.GetInterface(ifname) + except dbus.DBusException, exc: + if not str(exc).startswith("fi.w1.wpa_supplicant1.InterfaceUnknown:"): + raise exc + try: + path = wpas.CreateInterface({'Ifname': ifname, 'Driver': 'test'}) + time.sleep(1) + + except dbus.DBusException, exc: + if not str(exc).startswith("fi.w1.wpa_supplicant1.InterfaceExists:"): + raise exc + + global if_obj + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + global iface + iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE) + iface.Scan({'Type': 'active'}) + + gobject.MainLoop().run() + + wpas.RemoveInterface(dbus.ObjectPath(path)) + +if __name__ == "__main__": + main() + diff --git a/wpa_supplicant/examples/wpas-test.py b/wpa_supplicant/examples/wpas-test.py new file mode 100755 index 0000000..fd7f73d --- /dev/null +++ b/wpa_supplicant/examples/wpas-test.py @@ -0,0 +1,91 @@ +#!/usr/bin/python + +import dbus +import sys, os +import time + +WPAS_DBUS_SERVICE = "fi.epitest.hostap.WPASupplicant" +WPAS_DBUS_INTERFACE = "fi.epitest.hostap.WPASupplicant" +WPAS_DBUS_OPATH = "/fi/epitest/hostap/WPASupplicant" + +WPAS_DBUS_INTERFACES_INTERFACE = "fi.epitest.hostap.WPASupplicant.Interface" +WPAS_DBUS_INTERFACES_OPATH = "/fi/epitest/hostap/WPASupplicant/Interfaces" +WPAS_DBUS_BSSID_INTERFACE = "fi.epitest.hostap.WPASupplicant.BSSID" + +def byte_array_to_string(s): + import urllib + r = "" + for c in s: + if c >= 32 and c < 127: + r += "%c" % c + else: + r += urllib.quote(chr(c)) + return r + +def main(): + if len(sys.argv) != 2: + print "Usage: wpas-test.py <interface>" + os._exit(1) + + ifname = sys.argv[1] + + bus = dbus.SystemBus() + wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) + wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) + + # See if wpa_supplicant already knows about this interface + path = None + try: + path = wpas.getInterface(ifname) + except dbus.dbus_bindings.DBusException, exc: + if str(exc) != "wpa_supplicant knows nothing about this interface.": + raise exc + try: + path = wpas.addInterface(ifname, {'driver': dbus.Variant('wext')}) + except dbus.dbus_bindings.DBusException, exc: + if str(exc) != "wpa_supplicant already controls this interface.": + raise exc + + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE) + iface.scan() + # Should really wait for the "scanResults" signal instead of sleeping + time.sleep(5) + res = iface.scanResults() + + print "Scanned wireless networks:" + for opath in res: + net_obj = bus.get_object(WPAS_DBUS_SERVICE, opath) + net = dbus.Interface(net_obj, WPAS_DBUS_BSSID_INTERFACE) + props = net.properties() + + # Convert the byte-array for SSID and BSSID to printable strings + bssid = "" + for item in props["bssid"]: + bssid = bssid + ":%02x" % item + bssid = bssid[1:] + ssid = byte_array_to_string(props["ssid"]) + wpa = "no" + if props.has_key("wpaie"): + wpa = "yes" + wpa2 = "no" + if props.has_key("rsnie"): + wpa2 = "yes" + freq = 0 + if props.has_key("frequency"): + freq = props["frequency"] + caps = props["capabilities"] + qual = props["quality"] + level = props["level"] + noise = props["noise"] + maxrate = props["maxrate"] / 1000000 + + print " %s :: ssid='%s' wpa=%s wpa2=%s quality=%d%% rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, qual, maxrate, freq) + + wpas.removeInterface(dbus.ObjectPath(path)) + # Should fail here with unknown interface error + iface.scan() + +if __name__ == "__main__": + main() + diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c new file mode 100644 index 0000000..0d7d568 --- /dev/null +++ b/wpa_supplicant/ibss_rsn.c @@ -0,0 +1,604 @@ +/* + * wpa_supplicant - IBSS RSN + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "l2_packet/l2_packet.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/wpa_ie.h" +#include "ap/wpa_auth.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "ibss_rsn.h" + + +static void ibss_rsn_free(struct ibss_rsn_peer *peer) +{ + wpa_auth_sta_deinit(peer->auth); + wpa_sm_deinit(peer->supp); + os_free(peer); +} + + +static void supp_set_state(void *ctx, enum wpa_states state) +{ + struct ibss_rsn_peer *peer = ctx; + peer->supp_state = state; +} + + +static enum wpa_states supp_get_state(void *ctx) +{ + struct ibss_rsn_peer *peer = ctx; + return peer->supp_state; +} + + +static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, + size_t len) +{ + struct ibss_rsn_peer *peer = ctx; + struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s; + + wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x " + "len=%lu)", + __func__, MAC2STR(dest), proto, (unsigned long) len); + + if (wpa_s->l2) + return l2_packet_send(wpa_s->l2, dest, proto, buf, len); + + return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len); +} + + +static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data, + u16 data_len, size_t *msg_len, void **data_pos) +{ + struct ieee802_1x_hdr *hdr; + + wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)", + __func__, type, data_len); + + *msg_len = sizeof(*hdr) + data_len; + hdr = os_malloc(*msg_len); + if (hdr == NULL) + return NULL; + + hdr->version = 2; + hdr->type = type; + hdr->length = host_to_be16(data_len); + + if (data) + os_memcpy(hdr + 1, data, data_len); + else + os_memset(hdr + 1, 0, data_len); + + if (data_pos) + *data_pos = hdr + 1; + + return (u8 *) hdr; +} + + +static int supp_get_beacon_ie(void *ctx) +{ + struct ibss_rsn_peer *peer = ctx; + + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); + /* TODO: get correct RSN IE */ + return wpa_sm_set_ap_rsn_ie(peer->supp, + (u8 *) "\x30\x14\x01\x00" + "\x00\x0f\xac\x04" + "\x01\x00\x00\x0f\xac\x04" + "\x01\x00\x00\x0f\xac\x02" + "\x00\x00", 22); +} + + +static int supp_set_key(void *ctx, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct ibss_rsn_peer *peer = ctx; + + wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d " + "set_tx=%d)", + __func__, alg, MAC2STR(addr), key_idx, set_tx); + wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len); + wpa_hexdump_key(MSG_DEBUG, "SUPP: set_key - key", key, key_len); + + if (key_idx == 0) { + /* + * In IBSS RSN, the pairwise key from the 4-way handshake + * initiated by the peer with highest MAC address is used. + */ + if (os_memcmp(peer->ibss_rsn->wpa_s->own_addr, peer->addr, + ETH_ALEN) > 0) { + wpa_printf(MSG_DEBUG, "SUPP: Do not use this PTK"); + return 0; + } + } + + if (is_broadcast_ether_addr(addr)) + addr = peer->addr; + return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx, + set_tx, seq, seq_len, key, key_len); +} + + +static void * supp_get_network_ctx(void *ctx) +{ + struct ibss_rsn_peer *peer = ctx; + return wpa_supplicant_get_ssid(peer->ibss_rsn->wpa_s); +} + + +static int supp_mlme_setprotection(void *ctx, const u8 *addr, + int protection_type, int key_type) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d " + "key_type=%d)", + __func__, MAC2STR(addr), protection_type, key_type); + return 0; +} + + +static void supp_cancel_auth_timeout(void *ctx) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); +} + + +static void supp_deauthenticate(void * ctx, int reason_code) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__); +} + + +int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, + const u8 *psk) +{ + struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return -1; + + ctx->ctx = peer; + ctx->msg_ctx = peer->ibss_rsn->wpa_s; + ctx->set_state = supp_set_state; + ctx->get_state = supp_get_state; + ctx->ether_send = supp_ether_send; + ctx->get_beacon_ie = supp_get_beacon_ie; + ctx->alloc_eapol = supp_alloc_eapol; + ctx->set_key = supp_set_key; + ctx->get_network_ctx = supp_get_network_ctx; + ctx->mlme_setprotection = supp_mlme_setprotection; + ctx->cancel_auth_timeout = supp_cancel_auth_timeout; + ctx->deauthenticate = supp_deauthenticate; + peer->supp = wpa_sm_init(ctx); + if (peer->supp == NULL) { + wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); + return -1; + } + + wpa_sm_set_own_addr(peer->supp, own_addr); + wpa_sm_set_param(peer->supp, WPA_PARAM_RSN_ENABLED, 1); + wpa_sm_set_param(peer->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN); + wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); + wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); + wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); + wpa_sm_set_pmk(peer->supp, psk, PMK_LEN); + + peer->supp_ie_len = sizeof(peer->supp_ie); + if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie, + &peer->supp_ie_len) < 0) { + wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()" + " failed"); + return -1; + } + + wpa_sm_notify_assoc(peer->supp, peer->addr); + + return 0; +} + + +static void auth_logger(void *ctx, const u8 *addr, logger_level level, + const char *txt) +{ + if (addr) + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", + MAC2STR(addr), txt); + else + wpa_printf(MSG_DEBUG, "AUTH: %s", txt); +} + + +static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk) +{ + struct ibss_rsn *ibss_rsn = ctx; + wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", + __func__, MAC2STR(addr), prev_psk); + if (prev_psk) + return NULL; + return ibss_rsn->psk; +} + + +static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, + size_t data_len, int encrypt) +{ + struct ibss_rsn *ibss_rsn = ctx; + struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s; + + wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu " + "encrypt=%d)", + __func__, MAC2STR(addr), (unsigned long) data_len, encrypt); + + if (wpa_s->l2) + return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data, + data_len); + + return wpa_drv_send_eapol(wpa_s, addr, ETH_P_EAPOL, data, data_len); +} + + +static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, + const u8 *addr, int idx, u8 *key, size_t key_len) +{ + struct ibss_rsn *ibss_rsn = ctx; + u8 seq[6]; + + os_memset(seq, 0, sizeof(seq)); + + if (addr) { + wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR + " key_idx=%d)", + __func__, alg, MAC2STR(addr), idx); + } else { + wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)", + __func__, alg, idx); + } + wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len); + + if (idx == 0) { + /* + * In IBSS RSN, the pairwise key from the 4-way handshake + * initiated by the peer with highest MAC address is used. + */ + if (addr == NULL || + os_memcmp(ibss_rsn->wpa_s->own_addr, addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "AUTH: Do not use this PTK"); + return 0; + } + } + + return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx, + 1, seq, 6, key, key_len); +} + + +static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm, + void *ctx), + void *cb_ctx) +{ + struct ibss_rsn *ibss_rsn = ctx; + struct ibss_rsn_peer *peer; + + wpa_printf(MSG_DEBUG, "AUTH: for_each_sta"); + + for (peer = ibss_rsn->peers; peer; peer = peer->next) { + if (peer->auth && cb(peer->auth, cb_ctx)) + return 1; + } + + return 0; +} + + +static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, + const u8 *own_addr) +{ + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); + + os_memset(&conf, 0, sizeof(conf)); + conf.wpa = 2; + conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK; + conf.wpa_pairwise = WPA_CIPHER_CCMP; + conf.rsn_pairwise = WPA_CIPHER_CCMP; + conf.wpa_group = WPA_CIPHER_CCMP; + conf.eapol_version = 2; + conf.wpa_group_rekey = 600; + + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = ibss_rsn; + cb.logger = auth_logger; + cb.send_eapol = auth_send_eapol; + cb.get_psk = auth_get_psk; + cb.set_key = auth_set_key; + cb.for_each_sta = auth_for_each_sta; + + ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb); + if (ibss_rsn->auth_group == NULL) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); + return -1; + } + + return 0; +} + + +static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, + struct ibss_rsn_peer *peer) +{ + peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr); + if (peer->auth == NULL) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); + return -1; + } + + /* TODO: get peer RSN IE with Probe Request */ + if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth, + (u8 *) "\x30\x14\x01\x00" + "\x00\x0f\xac\x04" + "\x01\x00\x00\x0f\xac\x04" + "\x01\x00\x00\x0f\xac\x02" + "\x00\x00", 22, NULL, 0) != + WPA_IE_OK) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); + return -1; + } + + if (wpa_auth_sm_event(peer->auth, WPA_ASSOC)) + return -1; + + if (wpa_auth_sta_associated(ibss_rsn->auth_group, peer->auth)) + return -1; + + return 0; +} + + +int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) +{ + struct ibss_rsn_peer *peer; + + if (ibss_rsn == NULL) + return -1; + + for (peer = ibss_rsn->peers; peer; peer = peer->next) { + if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and " + "Supplicant for peer " MACSTR " already " + "running", MAC2STR(addr)); + return 0; + } + } + + wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and " + "Supplicant for peer " MACSTR, MAC2STR(addr)); + + peer = os_zalloc(sizeof(*peer)); + if (peer == NULL) + return -1; + + peer->ibss_rsn = ibss_rsn; + os_memcpy(peer->addr, addr, ETH_ALEN); + + if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, ibss_rsn->psk) + < 0) { + ibss_rsn_free(peer); + return -1; + } + + if (ibss_rsn_auth_init(ibss_rsn, peer) < 0) { + ibss_rsn_free(peer); + return -1; + } + + peer->next = ibss_rsn->peers; + ibss_rsn->peers = peer; + + return 0; +} + + +void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac) +{ + struct ibss_rsn_peer *peer, *prev; + + if (ibss_rsn == NULL) + return; + + if (peermac == NULL) { + /* remove all peers */ + wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__); + peer = ibss_rsn->peers; + while (peer) { + prev = peer; + peer = peer->next; + ibss_rsn_free(prev); + ibss_rsn->peers = peer; + } + } else { + /* remove specific peer */ + wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR, + __func__, MAC2STR(peermac)); + + for (prev = NULL, peer = ibss_rsn->peers; peer != NULL; + prev = peer, peer = peer->next) { + if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) { + if (prev == NULL) + ibss_rsn->peers = peer->next; + else + prev->next = peer->next; + ibss_rsn_free(peer); + wpa_printf(MSG_DEBUG, "%s: Successfully " + "removed a specific peer", + __func__); + break; + } + } + } +} + + +struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s) +{ + struct ibss_rsn *ibss_rsn; + + ibss_rsn = os_zalloc(sizeof(*ibss_rsn)); + if (ibss_rsn == NULL) + return NULL; + ibss_rsn->wpa_s = wpa_s; + + if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr) < 0) { + ibss_rsn_deinit(ibss_rsn); + return NULL; + } + + return ibss_rsn; +} + + +void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn) +{ + struct ibss_rsn_peer *peer, *prev; + + if (ibss_rsn == NULL) + return; + + peer = ibss_rsn->peers; + while (peer) { + prev = peer; + peer = peer->next; + ibss_rsn_free(prev); + } + + wpa_deinit(ibss_rsn->auth_group); + os_free(ibss_rsn); + +} + + +static int ibss_rsn_eapol_dst_supp(const u8 *buf, size_t len) +{ + const struct ieee802_1x_hdr *hdr; + const struct wpa_eapol_key *key; + u16 key_info; + size_t plen; + + /* TODO: Support other EAPOL packets than just EAPOL-Key */ + + if (len < sizeof(*hdr) + sizeof(*key)) + return -1; + + hdr = (const struct ieee802_1x_hdr *) buf; + key = (const struct wpa_eapol_key *) (hdr + 1); + plen = be_to_host16(hdr->length); + + if (hdr->version < EAPOL_VERSION) { + /* TODO: backwards compatibility */ + } + if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { + wpa_printf(MSG_DEBUG, "RSN: EAPOL frame (type %u) discarded, " + "not a Key frame", hdr->type); + return -1; + } + if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { + wpa_printf(MSG_DEBUG, "RSN: EAPOL frame payload size %lu " + "invalid (frame size %lu)", + (unsigned long) plen, (unsigned long) len); + return -1; + } + + if (key->type != EAPOL_KEY_TYPE_RSN) { + wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key type (%d) unknown, " + "discarded", key->type); + return -1; + } + + key_info = WPA_GET_BE16(key->key_info); + + return !!(key_info & WPA_KEY_INFO_ACK); +} + + +static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn, + struct ibss_rsn_peer *peer, + const u8 *buf, size_t len) +{ + int supp; + u8 *tmp; + + supp = ibss_rsn_eapol_dst_supp(buf, len); + if (supp < 0) + return -1; + + tmp = os_malloc(len); + if (tmp == NULL) + return -1; + os_memcpy(tmp, buf, len); + if (supp) { + wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant"); + wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len); + } else { + wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator"); + wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len); + } + os_free(tmp); + + return 1; +} + + +int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct ibss_rsn_peer *peer; + + if (ibss_rsn == NULL) + return -1; + + for (peer = ibss_rsn->peers; peer; peer = peer->next) { + if (os_memcmp(src_addr, peer->addr, ETH_ALEN) == 0) + return ibss_rsn_process_rx_eapol(ibss_rsn, peer, + buf, len); + } + + if (ibss_rsn_eapol_dst_supp(buf, len) > 0) { + /* + * Create new IBSS peer based on an EAPOL message from the peer + * Authenticator. + */ + if (ibss_rsn_start(ibss_rsn, src_addr) < 0) + return -1; + return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers, + buf, len); + } + + return 0; +} + + +void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk) +{ + if (ibss_rsn == NULL) + return; + os_memcpy(ibss_rsn->psk, psk, PMK_LEN); +} diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h new file mode 100644 index 0000000..dbc889f --- /dev/null +++ b/wpa_supplicant/ibss_rsn.h @@ -0,0 +1,50 @@ +/* + * wpa_supplicant - IBSS RSN + * 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. + */ + +#ifndef IBSS_RSN_H +#define IBSS_RSN_H + +struct ibss_rsn; + +struct ibss_rsn_peer { + struct ibss_rsn_peer *next; + struct ibss_rsn *ibss_rsn; + + u8 addr[ETH_ALEN]; + + struct wpa_sm *supp; + enum wpa_states supp_state; + u8 supp_ie[80]; + size_t supp_ie_len; + + struct wpa_state_machine *auth; +}; + +struct ibss_rsn { + struct wpa_supplicant *wpa_s; + struct wpa_authenticator *auth_group; + struct ibss_rsn_peer *peers; + u8 psk[PMK_LEN]; +}; + + +struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s); +void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn); +int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr); +void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac); +int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, + const u8 *buf, size_t len); +void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk); + +#endif /* IBSS_RSN_H */ diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c new file mode 100644 index 0000000..c0aa59c --- /dev/null +++ b/wpa_supplicant/main.c @@ -0,0 +1,285 @@ +/* + * WPA Supplicant / main() function for UNIX like OSes and MinGW + * Copyright (c) 2003-2007, 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. + */ + +#include "includes.h" +#ifdef __linux__ +#include <fcntl.h> +#endif /* __linux__ */ + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "driver_i.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>] " + "[-g<global ctrl>] \\\n" + " -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] " + "[-p<driver_param>] \\\n" + " [-b<br_ifname>] [-f<debug file>] \\\n" + " [-o<override driver>] [-O<override ctrl>] \\\n" + " [-N -i<ifname> -c<conf> [-C<ctrl>] " + "[-D<driver>] \\\n" + " [-p<driver_param>] [-b<br_ifname>] ...]\n" + "\n" + "drivers:\n", + wpa_supplicant_version, wpa_supplicant_license); + + for (i = 0; wpa_drivers[i]; i++) { + printf(" %s = %s\n", + wpa_drivers[i]->name, + wpa_drivers[i]->desc); + } + +#ifndef CONFIG_NO_STDOUT_DEBUG + printf("options:\n" + " -b = optional bridge interface name\n" + " -B = run daemon in the background\n" + " -c = Configuration file\n" + " -C = ctrl_interface parameter (only used if -c is not)\n" + " -i = interface name\n" + " -d = increase debugging verbosity (-dd even more)\n" + " -D = driver name (can be multiple drivers: nl80211,wext)\n"); +#ifdef CONFIG_DEBUG_FILE + printf(" -f = log output to debug file instead of stdout\n"); +#endif /* CONFIG_DEBUG_FILE */ + printf(" -g = global ctrl_interface\n" + " -K = include keys (passwords, etc.) in debug output\n"); +#ifdef CONFIG_DEBUG_SYSLOG + printf(" -s = log output to syslog instead of stdout\n"); +#endif /* CONFIG_DEBUG_SYSLOG */ + printf(" -t = include timestamp in debug messages\n" + " -h = show this help text\n" + " -L = show license (GPL and BSD)\n" + " -o = override driver parameter for new interfaces\n" + " -O = override ctrl_interface parameter for new interfaces\n" + " -p = driver parameters\n" + " -P = PID file\n" + " -q = decrease debugging verbosity (-qq even less)\n"); +#ifdef CONFIG_DBUS + printf(" -u = enable DBus control interface\n"); +#endif /* CONFIG_DBUS */ + printf(" -v = show version\n" + " -W = wait for a control interface monitor before starting\n" + " -N = start describing new interface\n"); + + printf("example:\n" + " wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n", + wpa_drivers[i] ? wpa_drivers[i]->name : "wext"); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static void license(void) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + printf("%s\n\n%s%s%s%s%s\n", + wpa_supplicant_version, + wpa_supplicant_full_license1, + wpa_supplicant_full_license2, + wpa_supplicant_full_license3, + wpa_supplicant_full_license4, + wpa_supplicant_full_license5); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static void wpa_supplicant_fd_workaround(void) +{ +#ifdef __linux__ + int s, i; + /* When started from pcmcia-cs scripts, wpa_supplicant might start with + * fd 0, 1, and 2 closed. This will cause some issues because many + * places in wpa_supplicant are still printing out to stdout. As a + * workaround, make sure that fd's 0, 1, and 2 are not used for other + * sockets. */ + for (i = 0; i < 3; i++) { + s = open("/dev/null", O_RDWR); + if (s > 2) { + close(s); + break; + } + } +#endif /* __linux__ */ +} + + +int main(int argc, char *argv[]) +{ + int c, i; + struct wpa_interface *ifaces, *iface; + int iface_count, exitcode = -1; + struct wpa_params params; + struct wpa_global *global; + + if (os_program_init()) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + iface = ifaces = os_zalloc(sizeof(struct wpa_interface)); + if (ifaces == NULL) + return -1; + iface_count = 1; + + wpa_supplicant_fd_workaround(); + + for (;;) { + c = getopt(argc, argv, "b:Bc:C:D:df:g:hi:KLNo:O:p:P:qstuvW"); + if (c < 0) + break; + switch (c) { + case 'b': + iface->bridge_ifname = optarg; + break; + case 'B': + params.daemonize++; + break; + case 'c': + iface->confname = optarg; + break; + case 'C': + iface->ctrl_interface = optarg; + break; + case 'D': + iface->driver = optarg; + break; + case 'd': +#ifdef CONFIG_NO_STDOUT_DEBUG + printf("Debugging disabled with " + "CONFIG_NO_STDOUT_DEBUG=y build time " + "option.\n"); + goto out; +#else /* CONFIG_NO_STDOUT_DEBUG */ + params.wpa_debug_level--; + break; +#endif /* CONFIG_NO_STDOUT_DEBUG */ +#ifdef CONFIG_DEBUG_FILE + case 'f': + params.wpa_debug_file_path = optarg; + break; +#endif /* CONFIG_DEBUG_FILE */ + case 'g': + params.ctrl_interface = optarg; + break; + case 'h': + usage(); + exitcode = 0; + goto out; + case 'i': + iface->ifname = optarg; + break; + case 'K': + params.wpa_debug_show_keys++; + break; + case 'L': + license(); + exitcode = 0; + goto out; + case 'o': + params.override_driver = optarg; + break; + case 'O': + params.override_ctrl_interface = optarg; + break; + case 'p': + iface->driver_param = optarg; + break; + case 'P': + os_free(params.pid_file); + params.pid_file = os_rel2abs_path(optarg); + break; + case 'q': + params.wpa_debug_level++; + break; +#ifdef CONFIG_DEBUG_SYSLOG + case 's': + params.wpa_debug_syslog++; + break; +#endif /* CONFIG_DEBUG_SYSLOG */ + case 't': + params.wpa_debug_timestamp++; + break; +#ifdef CONFIG_DBUS + case 'u': + params.dbus_ctrl_interface = 1; + break; +#endif /* CONFIG_DBUS */ + case 'v': + printf("%s\n", wpa_supplicant_version); + exitcode = 0; + goto out; + case 'W': + params.wait_for_monitor++; + break; + case 'N': + iface_count++; + iface = os_realloc(ifaces, iface_count * + sizeof(struct wpa_interface)); + if (iface == NULL) + goto out; + ifaces = iface; + iface = &ifaces[iface_count - 1]; + os_memset(iface, 0, sizeof(*iface)); + break; + default: + usage(); + exitcode = 0; + goto out; + } + } + + exitcode = 0; + global = wpa_supplicant_init(¶ms); + if (global == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant"); + exitcode = -1; + goto out; + } + + for (i = 0; exitcode == 0 && i < iface_count; i++) { + if ((ifaces[i].confname == NULL && + ifaces[i].ctrl_interface == NULL) || + ifaces[i].ifname == NULL) { + if (iface_count == 1 && (params.ctrl_interface || + params.dbus_ctrl_interface)) + break; + usage(); + exitcode = -1; + break; + } + if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + exitcode = -1; + } + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + +out: + os_free(ifaces); + os_free(params.pid_file); + + os_program_deinit(); + + return exitcode; +} diff --git a/wpa_supplicant/main_none.c b/wpa_supplicant/main_none.c new file mode 100644 index 0000000..993338a --- /dev/null +++ b/wpa_supplicant/main_none.c @@ -0,0 +1,46 @@ +/* + * WPA Supplicant / Example program entrypoint + * Copyright (c) 2003-2005, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" + +int main(int argc, char *argv[]) +{ + struct wpa_interface iface; + int exitcode = 0; + struct wpa_params params; + struct wpa_global *global; + + memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + global = wpa_supplicant_init(¶ms); + if (global == NULL) + return -1; + + memset(&iface, 0, sizeof(iface)); + /* TODO: set interface parameters */ + + if (wpa_supplicant_add_iface(global, &iface) == NULL) + exitcode = -1; + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + return exitcode; +} diff --git a/wpa_supplicant/main_symbian.cpp b/wpa_supplicant/main_symbian.cpp new file mode 100644 index 0000000..4ff364b --- /dev/null +++ b/wpa_supplicant/main_symbian.cpp @@ -0,0 +1,48 @@ +/* + * WPA Supplicant / Program entrypoint for Symbian + * Copyright (c) 2003-2007, 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. + */ + +#include "includes.h" + +extern "C" { +#include "common.h" +#include "wpa_supplicant_i.h" +} + +GLDEF_C TInt E32Main(void) +{ + struct wpa_interface iface; + int exitcode = 0; + struct wpa_params params; + struct wpa_global *global; + + memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + global = wpa_supplicant_init(¶ms); + if (global == NULL) + return -1; + + memset(&iface, 0, sizeof(iface)); + /* TODO: set interface parameters */ + + if (wpa_supplicant_add_iface(global, &iface) == NULL) + exitcode = -1; + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + return exitcode; +} diff --git a/wpa_supplicant/main_winmain.c b/wpa_supplicant/main_winmain.c new file mode 100644 index 0000000..19d9950 --- /dev/null +++ b/wpa_supplicant/main_winmain.c @@ -0,0 +1,84 @@ +/* + * WPA Supplicant / WinMain() function for Windows-based applications + * Copyright (c) 2006, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" + +#ifdef _WIN32_WCE +#define CMDLINE LPWSTR +#else /* _WIN32_WCE */ +#define CMDLINE LPSTR +#endif /* _WIN32_WCE */ + + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + CMDLINE lpCmdLine, int nShowCmd) +{ + int i; + struct wpa_interface *ifaces, *iface; + int iface_count, exitcode = -1; + struct wpa_params params; + struct wpa_global *global; + + if (os_program_init()) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_MSGDUMP; + params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt"; + params.wpa_debug_show_keys = 1; + + iface = ifaces = os_zalloc(sizeof(struct wpa_interface)); + if (ifaces == NULL) + return -1; + iface_count = 1; + + iface->confname = "default"; + iface->driver = "ndis"; + iface->ifname = ""; + + exitcode = 0; + global = wpa_supplicant_init(¶ms); + if (global == NULL) { + printf("Failed to initialize wpa_supplicant\n"); + exitcode = -1; + } + + for (i = 0; exitcode == 0 && i < iface_count; i++) { + if ((ifaces[i].confname == NULL && + ifaces[i].ctrl_interface == NULL) || + ifaces[i].ifname == NULL) { + if (iface_count == 1 && (params.ctrl_interface || + params.dbus_ctrl_interface)) + break; + exitcode = -1; + break; + } + if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + exitcode = -1; + } + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + os_free(ifaces); + + os_program_deinit(); + + return exitcode; +} diff --git a/wpa_supplicant/main_winsvc.c b/wpa_supplicant/main_winsvc.c new file mode 100644 index 0000000..4a46ed5 --- /dev/null +++ b/wpa_supplicant/main_winsvc.c @@ -0,0 +1,464 @@ +/* + * WPA Supplicant / main() function for Win32 service + * Copyright (c) 2003-2006, 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. + * + * The root of wpa_supplicant configuration in registry is + * HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant. This level includes global + * parameters and a 'interfaces' subkey with all the interface configuration + * (adapter to confname mapping). Each such mapping is a subkey that has + * 'adapter' and 'config' values. + * + * This program can be run either as a normal command line application, e.g., + * for debugging, with 'wpasvc.exe app' or as a Windows service. Service need + * to be registered with 'wpasvc.exe reg <full path to wpasvc.exe>'. After + * this, it can be started like any other Windows service (e.g., 'net start + * wpasvc') or it can be configured to start automatically through the Services + * tool in administrative tasks. The service can be unregistered with + * 'wpasvc.exe unreg'. + */ + +#include "includes.h" +#include <windows.h> + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "eloop.h" + +#ifndef WPASVC_NAME +#define WPASVC_NAME TEXT("wpasvc") +#endif +#ifndef WPASVC_DISPLAY_NAME +#define WPASVC_DISPLAY_NAME TEXT("wpa_supplicant service") +#endif +#ifndef WPASVC_DESCRIPTION +#define WPASVC_DESCRIPTION \ +TEXT("Provides IEEE 802.1X and WPA/WPA2 supplicant functionality") +#endif + +static HANDLE kill_svc; + +static SERVICE_STATUS_HANDLE svc_status_handle; +static SERVICE_STATUS svc_status; + + +#ifndef WPA_KEY_ROOT +#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE +#endif +#ifndef WPA_KEY_PREFIX +#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant") +#endif + +#ifdef UNICODE +#define TSTR "%S" +#else /* UNICODE */ +#define TSTR "%s" +#endif /* UNICODE */ + + +static int read_interface(struct wpa_global *global, HKEY _hk, + const TCHAR *name) +{ + HKEY hk; +#define TBUFLEN 255 + TCHAR adapter[TBUFLEN], config[TBUFLEN], ctrl_interface[TBUFLEN]; + DWORD buflen, val; + LONG ret; + struct wpa_interface iface; + int skip_on_error = 0; + + ret = RegOpenKeyEx(_hk, name, 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant interface key\n"); + return -1; + } + + os_memset(&iface, 0, sizeof(iface)); + iface.driver = "ndis"; + + buflen = sizeof(ctrl_interface); + ret = RegQueryValueEx(hk, TEXT("ctrl_interface"), NULL, NULL, + (LPBYTE) ctrl_interface, &buflen); + if (ret == ERROR_SUCCESS) { + ctrl_interface[TBUFLEN - 1] = TEXT('\0'); + wpa_unicode2ascii_inplace(ctrl_interface); + printf("ctrl_interface[len=%d] '%s'\n", + (int) buflen, (char *) ctrl_interface); + iface.ctrl_interface = (char *) ctrl_interface; + } + + buflen = sizeof(adapter); + ret = RegQueryValueEx(hk, TEXT("adapter"), NULL, NULL, + (LPBYTE) adapter, &buflen); + if (ret == ERROR_SUCCESS) { + adapter[TBUFLEN - 1] = TEXT('\0'); + wpa_unicode2ascii_inplace(adapter); + printf("adapter[len=%d] '%s'\n", + (int) buflen, (char *) adapter); + iface.ifname = (char *) adapter; + } + + buflen = sizeof(config); + ret = RegQueryValueEx(hk, TEXT("config"), NULL, NULL, + (LPBYTE) config, &buflen); + if (ret == ERROR_SUCCESS) { + config[sizeof(config) - 1] = '\0'; + wpa_unicode2ascii_inplace(config); + printf("config[len=%d] '%s'\n", + (int) buflen, (char *) config); + iface.confname = (char *) config; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("skip_on_error"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) + skip_on_error = val; + + RegCloseKey(hk); + + if (wpa_supplicant_add_iface(global, &iface) == NULL) { + if (skip_on_error) + wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to " + "initialization failure", iface.ifname); + else + return -1; + } + + return 0; +} + + +static int wpa_supplicant_thread(void) +{ + int exitcode; + struct wpa_params params; + struct wpa_global *global; + HKEY hk, ihk; + DWORD val, buflen, i; + LONG ret; + + if (os_program_init()) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX, + 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant registry key\n"); + return -1; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_level"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_level = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_show_keys"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_show_keys = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_timestamp"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_timestamp = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_use_file"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val) && val) { + params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt"; + } + + exitcode = 0; + global = wpa_supplicant_init(¶ms); + if (global == NULL) { + printf("Failed to initialize wpa_supplicant\n"); + exitcode = -1; + } + + ret = RegOpenKeyEx(hk, TEXT("interfaces"), 0, KEY_ENUMERATE_SUB_KEYS, + &ihk); + RegCloseKey(hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant interfaces registry " + "key\n"); + return -1; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(ihk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + printf("RegEnumKeyEx failed: 0x%x\n", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + + wpa_printf(MSG_DEBUG, "interface %d: %s\n", (int) i, name); + if (read_interface(global, ihk, name) < 0) + exitcode = -1; + } + + RegCloseKey(ihk); + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + os_program_deinit(); + + return exitcode; +} + + +static DWORD svc_thread(LPDWORD param) +{ + int ret = wpa_supplicant_thread(); + + svc_status.dwCurrentState = SERVICE_STOPPED; + svc_status.dwWaitHint = 0; + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + } + + return ret; +} + + +static int register_service(const TCHAR *exe) +{ + SC_HANDLE svc, scm; + SERVICE_DESCRIPTION sd; + + printf("Registering service: " TSTR "\n", WPASVC_NAME); + + scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + if (!scm) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return -1; + } + + svc = CreateService(scm, WPASVC_NAME, WPASVC_DISPLAY_NAME, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, + exe, NULL, NULL, NULL, NULL, NULL); + + if (!svc) { + printf("CreateService failed: %d\n\n", (int) GetLastError()); + CloseServiceHandle(scm); + return -1; + } + + os_memset(&sd, 0, sizeof(sd)); + sd.lpDescription = WPASVC_DESCRIPTION; + if (!ChangeServiceConfig2(svc, SERVICE_CONFIG_DESCRIPTION, &sd)) { + printf("ChangeServiceConfig2 failed: %d\n", + (int) GetLastError()); + /* This is not a fatal error, so continue anyway. */ + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + printf("Service registered successfully.\n"); + + return 0; +} + + +static int unregister_service(void) +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + + printf("Unregistering service: " TSTR "\n", WPASVC_NAME); + + scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + if (!scm) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return -1; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_ALL_ACCESS | DELETE); + if (!svc) { + printf("OpenService failed: %d\n\n", (int) GetLastError()); + CloseServiceHandle(scm); + return -1; + } + + if (QueryServiceStatus(svc, &status)) { + if (status.dwCurrentState != SERVICE_STOPPED) { + printf("Service currently active - stopping " + "service...\n"); + if (!ControlService(svc, SERVICE_CONTROL_STOP, + &status)) { + printf("ControlService failed: %d\n", + (int) GetLastError()); + } + Sleep(500); + } + } + + if (DeleteService(svc)) { + printf("Service unregistered successfully.\n"); + } else { + printf("DeleteService failed: %d\n", (int) GetLastError()); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + return 0; +} + + +static void WINAPI service_ctrl_handler(DWORD control_code) +{ + switch (control_code) { + case SERVICE_CONTROL_INTERROGATE: + break; + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + svc_status.dwCurrentState = SERVICE_STOP_PENDING; + svc_status.dwWaitHint = 2000; + eloop_terminate(); + SetEvent(kill_svc); + break; + } + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + } +} + + +static void WINAPI service_start(DWORD argc, LPTSTR *argv) +{ + DWORD id; + + svc_status_handle = RegisterServiceCtrlHandler(WPASVC_NAME, + service_ctrl_handler); + if (svc_status_handle == (SERVICE_STATUS_HANDLE) 0) { + printf("RegisterServiceCtrlHandler failed: %d\n", + (int) GetLastError()); + return; + } + + os_memset(&svc_status, 0, sizeof(svc_status)); + svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + svc_status.dwCurrentState = SERVICE_START_PENDING; + svc_status.dwWaitHint = 1000; + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + return; + } + + kill_svc = CreateEvent(0, TRUE, FALSE, 0); + if (!kill_svc) { + printf("CreateEvent failed: %d\n", (int) GetLastError()); + return; + } + + if (CreateThread(0, 0, (LPTHREAD_START_ROUTINE) svc_thread, 0, 0, &id) + == 0) { + printf("CreateThread failed: %d\n", (int) GetLastError()); + return; + } + + if (svc_status.dwCurrentState == SERVICE_START_PENDING) { + svc_status.dwCurrentState = SERVICE_RUNNING; + svc_status.dwWaitHint = 0; + svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_SHUTDOWN; + } + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + return; + } + + /* wait until service gets killed */ + WaitForSingleObject(kill_svc, INFINITE); +} + + +int main(int argc, char *argv[]) +{ + SERVICE_TABLE_ENTRY dt[] = { + { WPASVC_NAME, service_start }, + { NULL, NULL } + }; + + if (argc > 1) { + if (os_strcmp(argv[1], "reg") == 0) { + TCHAR *path; + int ret; + + if (argc < 3) { + path = os_malloc(MAX_PATH * sizeof(TCHAR)); + if (path == NULL) + return -1; + if (!GetModuleFileName(NULL, path, MAX_PATH)) { + printf("GetModuleFileName failed: " + "%d\n", (int) GetLastError()); + os_free(path); + return -1; + } + } else { + path = wpa_strdup_tchar(argv[2]); + if (path == NULL) + return -1; + } + ret = register_service(path); + os_free(path); + return ret; + } else if (os_strcmp(argv[1], "unreg") == 0) { + return unregister_service(); + } else if (os_strcmp(argv[1], "app") == 0) { + return wpa_supplicant_thread(); + } + } + + if (!StartServiceCtrlDispatcher(dt)) { + printf("StartServiceCtrlDispatcher failed: %d\n", + (int) GetLastError()); + } + + return 0; +} diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c new file mode 100644 index 0000000..49713f5 --- /dev/null +++ b/wpa_supplicant/mlme.c @@ -0,0 +1,3181 @@ +/* + * WPA Supplicant - Client mode MLME + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config_ssid.h" +#include "wpa_supplicant_i.h" +#include "notify.h" +#include "driver_i.h" +#include "rsn_supp/wpa.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "mlme.h" + + +/* Timeouts and intervals in milliseconds */ +#define IEEE80211_AUTH_TIMEOUT (200) +#define IEEE80211_AUTH_MAX_TRIES 3 +#define IEEE80211_ASSOC_TIMEOUT (200) +#define IEEE80211_ASSOC_MAX_TRIES 3 +#define IEEE80211_MONITORING_INTERVAL (2000) +#define IEEE80211_PROBE_INTERVAL (60000) +#define IEEE80211_RETRY_AUTH_INTERVAL (1000) +#define IEEE80211_SCAN_INTERVAL (2000) +#define IEEE80211_SCAN_INTERVAL_SLOW (15000) +#define IEEE80211_IBSS_JOIN_TIMEOUT (20000) + +#define IEEE80211_PROBE_DELAY (33) +#define IEEE80211_CHANNEL_TIME (33) +#define IEEE80211_PASSIVE_CHANNEL_TIME (200) +#define IEEE80211_SCAN_RESULT_EXPIRE (10000) +#define IEEE80211_IBSS_MERGE_INTERVAL (30000) +#define IEEE80211_IBSS_INACTIVITY_LIMIT (60000) + +#define IEEE80211_IBSS_MAX_STA_ENTRIES 128 + + +#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) + + +struct ieee80211_sta_bss { + struct ieee80211_sta_bss *next; + struct ieee80211_sta_bss *hnext; + + u8 bssid[ETH_ALEN]; + u8 ssid[MAX_SSID_LEN]; + size_t ssid_len; + u16 capability; /* host byte order */ + int hw_mode; + int channel; + int freq; + int rssi; + u8 *ie; + size_t ie_len; + u8 *wpa_ie; + size_t wpa_ie_len; + u8 *rsn_ie; + size_t rsn_ie_len; + u8 *wmm_ie; + size_t wmm_ie_len; + u8 *mdie; + size_t mdie_len; +#define IEEE80211_MAX_SUPP_RATES 32 + u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; + size_t supp_rates_len; + int beacon_int; + u64 timestamp; + + int probe_resp; + struct os_time last_update; +}; + + +static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s, + const u8 *dst, + const u8 *ssid, size_t ssid_len); +static struct ieee80211_sta_bss * +ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid); +static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s); +static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s); +static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx); +static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx); +static void ieee80211_build_tspec(struct wpabuf *buf); +static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len); + + +static int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode phymode, int chan, + int freq) +{ + size_t i; + struct hostapd_hw_modes *mode; + + for (i = 0; i < wpa_s->mlme.num_modes; i++) { + mode = &wpa_s->mlme.modes[i]; + if (mode->mode == phymode) { + wpa_s->mlme.curr_rates = mode->rates; + wpa_s->mlme.num_curr_rates = mode->num_rates; + break; + } + } + + return wpa_drv_set_channel(wpa_s, phymode, chan, freq); +} + + +static int ecw2cw(int ecw) +{ + int cw = 1; + while (ecw > 0) { + cw <<= 1; + ecw--; + } + return cw - 1; +} + + +static void ieee80211_sta_wmm_params(struct wpa_supplicant *wpa_s, + const u8 *wmm_param, size_t wmm_param_len) +{ + size_t left; + int count; + const u8 *pos; + u8 wmm_acm; + + if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) + return; + count = wmm_param[6] & 0x0f; + if (count == wpa_s->mlme.wmm_last_param_set) + return; + wpa_s->mlme.wmm_last_param_set = count; + + pos = wmm_param + 8; + left = wmm_param_len - 8; + + wmm_acm = 0; + for (; left >= 4; left -= 4, pos += 4) { + int aci = (pos[0] >> 5) & 0x03; + int acm = (pos[0] >> 4) & 0x01; + int aifs, cw_max, cw_min, burst_time; + + switch (aci) { + case 1: /* AC_BK */ + if (acm) + wmm_acm |= BIT(1) | BIT(2); /* BK/- */ + break; + case 2: /* AC_VI */ + if (acm) + wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ + break; + case 3: /* AC_VO */ + if (acm) + wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ + break; + case 0: /* AC_BE */ + default: + if (acm) + wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ + break; + } + + aifs = pos[0] & 0x0f; + cw_max = ecw2cw((pos[1] & 0xf0) >> 4); + cw_min = ecw2cw(pos[1] & 0x0f); + /* TXOP is in units of 32 usec; burst_time in 0.1 ms */ + burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100; + wpa_printf(MSG_DEBUG, "MLME: WMM aci=%d acm=%d aifs=%d " + "cWmin=%d cWmax=%d burst=%d", + aci, acm, aifs, cw_min, cw_max, burst_time); + /* TODO: driver configuration */ + } +} + + +static void ieee80211_set_associated(struct wpa_supplicant *wpa_s, int assoc) +{ + if (wpa_s->mlme.associated == assoc && !assoc) + return; + + wpa_s->mlme.associated = assoc; + + if (assoc) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + wpa_s->mlme.prev_bssid_set = 1; + os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN); + data.assoc_info.req_ies = wpa_s->mlme.assocreq_ies; + data.assoc_info.req_ies_len = wpa_s->mlme.assocreq_ies_len; + data.assoc_info.resp_ies = wpa_s->mlme.assocresp_ies; + data.assoc_info.resp_ies_len = wpa_s->mlme.assocresp_ies_len; + data.assoc_info.freq = wpa_s->mlme.freq; + wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); + } else { + wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL); + } + os_get_time(&wpa_s->mlme.last_probe); +} + + +static int ieee80211_sta_tx(struct wpa_supplicant *wpa_s, const u8 *buf, + size_t len) +{ + return wpa_drv_send_mlme(wpa_s, buf, len); +} + + +static void ieee80211_send_auth(struct wpa_supplicant *wpa_s, + int transaction, const u8 *extra, + size_t extra_len, int encrypt) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + + buf = os_malloc(sizeof(*mgmt) + 6 + extra_len); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "auth frame"); + return; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24 + 6; + os_memset(mgmt, 0, 24 + 6); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + if (encrypt) + mgmt->frame_control |= host_to_le16(WLAN_FC_ISWEP); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->u.auth.auth_alg = host_to_le16(wpa_s->mlme.auth_alg); + mgmt->u.auth.auth_transaction = host_to_le16(transaction); + wpa_s->mlme.auth_transaction = transaction + 1; + mgmt->u.auth.status_code = host_to_le16(0); + if (extra) { + os_memcpy(buf + len, extra, extra_len); + len += extra_len; + } + + ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); +} + + +static void ieee80211_reschedule_timer(struct wpa_supplicant *wpa_s, int ms) +{ + eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL); + eloop_register_timeout(ms / 1000, 1000 * (ms % 1000), + ieee80211_sta_timer, wpa_s, NULL); +} + + +static void ieee80211_authenticate(struct wpa_supplicant *wpa_s) +{ + u8 *extra; + size_t extra_len; + + wpa_s->mlme.auth_tries++; + if (wpa_s->mlme.auth_tries > IEEE80211_AUTH_MAX_TRIES) { + wpa_printf(MSG_DEBUG, "MLME: authentication with AP " MACSTR + " timed out", MAC2STR(wpa_s->bssid)); + return; + } + + wpa_s->mlme.state = IEEE80211_AUTHENTICATE; + wpa_printf(MSG_DEBUG, "MLME: authenticate with AP " MACSTR, + MAC2STR(wpa_s->bssid)); + + extra = NULL; + extra_len = 0; + +#ifdef CONFIG_IEEE80211R + if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || + wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && + wpa_s->mlme.ft_ies) { + struct ieee80211_sta_bss *bss; + struct rsn_mdie *mdie = NULL; + bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); + if (bss && bss->mdie_len >= 2 + sizeof(*mdie)) + mdie = (struct rsn_mdie *) (bss->mdie + 2); + if (mdie && + os_memcmp(mdie->mobility_domain, wpa_s->mlme.current_md, + MOBILITY_DOMAIN_ID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "MLME: Trying to use FT " + "over-the-air"); + wpa_s->mlme.auth_alg = WLAN_AUTH_FT; + extra = wpa_s->mlme.ft_ies; + extra_len = wpa_s->mlme.ft_ies_len; + } + } +#endif /* CONFIG_IEEE80211R */ + + ieee80211_send_auth(wpa_s, 1, extra, extra_len, 0); + + ieee80211_reschedule_timer(wpa_s, IEEE80211_AUTH_TIMEOUT); +} + + +static void ieee80211_send_assoc(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_mgmt *mgmt; + u8 *pos, *ies, *buf; + int i, len; + u16 capab; + struct ieee80211_sta_bss *bss; + int wmm = 0; + size_t blen, buflen; + + if (wpa_s->mlme.curr_rates == NULL) { + wpa_printf(MSG_DEBUG, "MLME: curr_rates not set for assoc"); + return; + } + + buflen = sizeof(*mgmt) + 200 + wpa_s->mlme.extra_ie_len + + wpa_s->mlme.ssid_len; +#ifdef CONFIG_IEEE80211R + if (wpa_s->mlme.ft_ies) + buflen += wpa_s->mlme.ft_ies_len; +#endif /* CONFIG_IEEE80211R */ + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "assoc frame"); + return; + } + blen = 0; + + capab = wpa_s->mlme.capab; + if (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G) { + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME | + WLAN_CAPABILITY_SHORT_PREAMBLE; + } + bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); + if (bss) { + if (bss->capability & WLAN_CAPABILITY_PRIVACY) + capab |= WLAN_CAPABILITY_PRIVACY; + if (bss->wmm_ie) { + wmm = 1; + } + } + + mgmt = (struct ieee80211_mgmt *) buf; + blen += 24; + os_memset(mgmt, 0, 24); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + + if (wpa_s->mlme.prev_bssid_set) { + blen += 10; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_REASSOC_REQ); + mgmt->u.reassoc_req.capab_info = host_to_le16(capab); + mgmt->u.reassoc_req.listen_interval = host_to_le16(1); + os_memcpy(mgmt->u.reassoc_req.current_ap, + wpa_s->mlme.prev_bssid, + ETH_ALEN); + } else { + blen += 4; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ASSOC_REQ); + mgmt->u.assoc_req.capab_info = host_to_le16(capab); + mgmt->u.assoc_req.listen_interval = host_to_le16(1); + } + + /* SSID */ + ies = pos = buf + blen; + blen += 2 + wpa_s->mlme.ssid_len; + *pos++ = WLAN_EID_SSID; + *pos++ = wpa_s->mlme.ssid_len; + os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); + + len = wpa_s->mlme.num_curr_rates; + if (len > 8) + len = 8; + pos = buf + blen; + blen += len + 2; + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = len; + for (i = 0; i < len; i++) + *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5); + + if (wpa_s->mlme.num_curr_rates > len) { + pos = buf + blen; + blen += wpa_s->mlme.num_curr_rates - len + 2; + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = wpa_s->mlme.num_curr_rates - len; + for (i = len; i < wpa_s->mlme.num_curr_rates; i++) + *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5); + } + + if (wpa_s->mlme.extra_ie && wpa_s->mlme.auth_alg != WLAN_AUTH_FT) { + pos = buf + blen; + blen += wpa_s->mlme.extra_ie_len; + os_memcpy(pos, wpa_s->mlme.extra_ie, wpa_s->mlme.extra_ie_len); + } + +#ifdef CONFIG_IEEE80211R + if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || + wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && + wpa_s->mlme.auth_alg != WLAN_AUTH_FT && + bss && bss->mdie && + bss->mdie_len >= 2 + sizeof(struct rsn_mdie) && + bss->mdie[1] >= sizeof(struct rsn_mdie)) { + pos = buf + blen; + blen += 2 + sizeof(struct rsn_mdie); + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = sizeof(struct rsn_mdie); + os_memcpy(pos, bss->mdie + 2, MOBILITY_DOMAIN_ID_LEN); + pos += MOBILITY_DOMAIN_ID_LEN; + *pos++ = 0; /* FIX: copy from the target AP's MDIE */ + } + + if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || + wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && + wpa_s->mlme.auth_alg == WLAN_AUTH_FT && wpa_s->mlme.ft_ies) { + pos = buf + blen; + os_memcpy(pos, wpa_s->mlme.ft_ies, wpa_s->mlme.ft_ies_len); + pos += wpa_s->mlme.ft_ies_len; + blen += wpa_s->mlme.ft_ies_len; + } +#endif /* CONFIG_IEEE80211R */ + + if (wmm && wpa_s->mlme.wmm_enabled) { + pos = buf + blen; + blen += 9; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 7; /* len */ + *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ + *pos++ = 0x50; + *pos++ = 0xf2; + *pos++ = 2; /* WMM */ + *pos++ = 0; /* WMM info */ + *pos++ = 1; /* WMM ver */ + *pos++ = 0; + } + + os_free(wpa_s->mlme.assocreq_ies); + wpa_s->mlme.assocreq_ies_len = (buf + blen) - ies; + wpa_s->mlme.assocreq_ies = os_malloc(wpa_s->mlme.assocreq_ies_len); + if (wpa_s->mlme.assocreq_ies) { + os_memcpy(wpa_s->mlme.assocreq_ies, ies, + wpa_s->mlme.assocreq_ies_len); + } + + ieee80211_sta_tx(wpa_s, buf, blen); + os_free(buf); +} + + +static void ieee80211_send_deauth(struct wpa_supplicant *wpa_s, u16 reason) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + + buf = os_zalloc(sizeof(*mgmt)); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "deauth frame"); + return; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24; + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + len += 2; + mgmt->u.deauth.reason_code = host_to_le16(reason); + + ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); +} + + +static void ieee80211_send_disassoc(struct wpa_supplicant *wpa_s, u16 reason) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + + buf = os_zalloc(sizeof(*mgmt)); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "disassoc frame"); + return; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24; + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + len += 2; + mgmt->u.disassoc.reason_code = host_to_le16(reason); + + ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); +} + + +static int ieee80211_privacy_mismatch(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_sta_bss *bss; + int res = 0; + + if (wpa_s->mlme.mixed_cell || + wpa_s->mlme.key_mgmt != KEY_MGMT_NONE) + return 0; + + bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); + if (bss == NULL) + return 0; + + if (ieee80211_sta_wep_configured(wpa_s) != + !!(bss->capability & WLAN_CAPABILITY_PRIVACY)) + res = 1; + + return res; +} + + +static void ieee80211_associate(struct wpa_supplicant *wpa_s) +{ + wpa_s->mlme.assoc_tries++; + if (wpa_s->mlme.assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { + wpa_printf(MSG_DEBUG, "MLME: association with AP " MACSTR + " timed out", MAC2STR(wpa_s->bssid)); + return; + } + + wpa_s->mlme.state = IEEE80211_ASSOCIATE; + wpa_printf(MSG_DEBUG, "MLME: associate with AP " MACSTR, + MAC2STR(wpa_s->bssid)); + if (ieee80211_privacy_mismatch(wpa_s)) { + wpa_printf(MSG_DEBUG, "MLME: mismatch in privacy " + "configuration and mixed-cell disabled - abort " + "association"); + return; + } + + ieee80211_send_assoc(wpa_s); + + ieee80211_reschedule_timer(wpa_s, IEEE80211_ASSOC_TIMEOUT); +} + + +static void ieee80211_associated(struct wpa_supplicant *wpa_s) +{ + int disassoc; + + /* TODO: start monitoring current AP signal quality and number of + * missed beacons. Scan other channels every now and then and search + * for better APs. */ + /* TODO: remove expired BSSes */ + + wpa_s->mlme.state = IEEE80211_ASSOCIATED; + +#if 0 /* FIX */ + sta = sta_info_get(local, wpa_s->bssid); + if (sta == NULL) { + wpa_printf(MSG_DEBUG "MLME: No STA entry for own AP " MACSTR, + MAC2STR(wpa_s->bssid)); + disassoc = 1; + } else { + disassoc = 0; + if (time_after(jiffies, + sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { + if (wpa_s->mlme.probereq_poll) { + wpa_printf(MSG_DEBUG "MLME: No ProbeResp from " + "current AP " MACSTR " - assume " + "out of range", + MAC2STR(wpa_s->bssid)); + disassoc = 1; + } else { + ieee80211_send_probe_req( + wpa_s->bssid, + wpa_s->mlme.scan_ssid, + wpa_s->mlme.scan_ssid_len); + wpa_s->mlme.probereq_poll = 1; + } + } else { + wpa_s->mlme.probereq_poll = 0; + if (time_after(jiffies, wpa_s->mlme.last_probe + + IEEE80211_PROBE_INTERVAL)) { + wpa_s->mlme.last_probe = jiffies; + ieee80211_send_probe_req(wpa_s->bssid, + wpa_s->mlme.ssid, + wpa_s->mlme.ssid_len); + } + } + sta_info_release(local, sta); + } +#else + disassoc = 0; +#endif + if (disassoc) { + wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL); + ieee80211_reschedule_timer(wpa_s, + IEEE80211_MONITORING_INTERVAL + + 30000); + } else { + ieee80211_reschedule_timer(wpa_s, + IEEE80211_MONITORING_INTERVAL); + } +} + + +static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s, + const u8 *dst, + const u8 *ssid, size_t ssid_len) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + u8 *pos, *supp_rates; + u8 *esupp_rates = NULL; + int i; + + buf = os_malloc(sizeof(*mgmt) + 200 + wpa_s->mlme.extra_probe_ie_len); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " + "probe request"); + return; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24; + os_memset(mgmt, 0, 24); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_PROBE_REQ); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + if (dst) { + os_memcpy(mgmt->da, dst, ETH_ALEN); + os_memcpy(mgmt->bssid, dst, ETH_ALEN); + } else { + os_memset(mgmt->da, 0xff, ETH_ALEN); + os_memset(mgmt->bssid, 0xff, ETH_ALEN); + } + pos = buf + len; + len += 2 + ssid_len; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + os_memcpy(pos, ssid, ssid_len); + + supp_rates = buf + len; + len += 2; + supp_rates[0] = WLAN_EID_SUPP_RATES; + supp_rates[1] = 0; + for (i = 0; i < wpa_s->mlme.num_curr_rates; i++) { + if (esupp_rates) { + pos = buf + len; + len++; + esupp_rates[1]++; + } else if (supp_rates[1] == 8) { + esupp_rates = pos; + esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES; + esupp_rates[1] = 1; + pos = &esupp_rates[2]; + len += 3; + } else { + pos = buf + len; + len++; + supp_rates[1]++; + } + *pos++ = wpa_s->mlme.curr_rates[i] / 5; + } + + if (wpa_s->mlme.extra_probe_ie) { + os_memcpy(pos, wpa_s->mlme.extra_probe_ie, + wpa_s->mlme.extra_probe_ie_len); + len += wpa_s->mlme.extra_probe_ie_len; + } + + ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); +} + + +static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s) +{ +#if 0 /* FIX */ + if (sdata == NULL || sdata->default_key == NULL || + sdata->default_key->alg != ALG_WEP) + return 0; + return 1; +#else + return 0; +#endif +} + + +static void ieee80211_auth_completed(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "MLME: authenticated"); + wpa_s->mlme.authenticated = 1; + ieee80211_associate(wpa_s); +} + + +static void ieee80211_auth_challenge(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + u8 *pos; + struct ieee802_11_elems elems; + + wpa_printf(MSG_DEBUG, "MLME: replying to auth challenge"); + pos = mgmt->u.auth.variable; + if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0) + == ParseFailed) { + wpa_printf(MSG_DEBUG, "MLME: failed to parse Auth(challenge)"); + return; + } + if (elems.challenge == NULL) { + wpa_printf(MSG_DEBUG, "MLME: no challenge IE in shared key " + "auth frame"); + return; + } + ieee80211_send_auth(wpa_s, 3, elems.challenge - 2, + elems.challenge_len + 2, 1); +} + + +static void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + u16 auth_alg, auth_transaction, status_code; + int adhoc; + + adhoc = ssid && ssid->mode == WPAS_MODE_IBSS; + + if (wpa_s->mlme.state != IEEE80211_AUTHENTICATE && !adhoc) { + wpa_printf(MSG_DEBUG, "MLME: authentication frame received " + "from " MACSTR ", but not in authenticate state - " + "ignored", MAC2STR(mgmt->sa)); + return; + } + + if (len < 24 + 6) { + wpa_printf(MSG_DEBUG, "MLME: too short (%lu) authentication " + "frame received from " MACSTR " - ignored", + (unsigned long) len, MAC2STR(mgmt->sa)); + return; + } + + if (!adhoc && os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: authentication frame received " + "from unknown AP (SA=" MACSTR " BSSID=" MACSTR + ") - ignored", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + if (adhoc && os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: authentication frame received " + "from unknown BSSID (SA=" MACSTR " BSSID=" MACSTR + ") - ignored", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + + wpa_printf(MSG_DEBUG, "MLME: RX authentication from " MACSTR + " (alg=%d transaction=%d status=%d)", + MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code); + + if (adhoc) { + /* IEEE 802.11 standard does not require authentication in IBSS + * networks and most implementations do not seem to use it. + * However, try to reply to authentication attempts if someone + * has actually implemented this. + * TODO: Could implement shared key authentication. */ + if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) { + wpa_printf(MSG_DEBUG, "MLME: unexpected IBSS " + "authentication frame (alg=%d " + "transaction=%d)", + auth_alg, auth_transaction); + return; + } + ieee80211_send_auth(wpa_s, 2, NULL, 0, 0); + } + + if (auth_alg != wpa_s->mlme.auth_alg || + auth_transaction != wpa_s->mlme.auth_transaction) { + wpa_printf(MSG_DEBUG, "MLME: unexpected authentication frame " + "(alg=%d transaction=%d)", + auth_alg, auth_transaction); + return; + } + + if (status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "MLME: AP denied authentication " + "(auth_alg=%d code=%d)", wpa_s->mlme.auth_alg, + status_code); + if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { + const int num_algs = 3; + u8 algs[num_algs]; + int i, pos; + algs[0] = algs[1] = algs[2] = 0xff; + if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN) + algs[0] = WLAN_AUTH_OPEN; + if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED) + algs[1] = WLAN_AUTH_SHARED_KEY; + if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP) + algs[2] = WLAN_AUTH_LEAP; + if (wpa_s->mlme.auth_alg == WLAN_AUTH_OPEN) + pos = 0; + else if (wpa_s->mlme.auth_alg == WLAN_AUTH_SHARED_KEY) + pos = 1; + else + pos = 2; + for (i = 0; i < num_algs; i++) { + pos++; + if (pos >= num_algs) + pos = 0; + if (algs[pos] == wpa_s->mlme.auth_alg || + algs[pos] == 0xff) + continue; + if (algs[pos] == WLAN_AUTH_SHARED_KEY && + !ieee80211_sta_wep_configured(wpa_s)) + continue; + wpa_s->mlme.auth_alg = algs[pos]; + wpa_printf(MSG_DEBUG, "MLME: set auth_alg=%d " + "for next try", + wpa_s->mlme.auth_alg); + break; + } + } + return; + } + + switch (wpa_s->mlme.auth_alg) { + case WLAN_AUTH_OPEN: + case WLAN_AUTH_LEAP: + ieee80211_auth_completed(wpa_s); + break; + case WLAN_AUTH_SHARED_KEY: + if (wpa_s->mlme.auth_transaction == 4) + ieee80211_auth_completed(wpa_s); + else + ieee80211_auth_challenge(wpa_s, mgmt, len, + rx_status); + break; +#ifdef CONFIG_IEEE80211R + case WLAN_AUTH_FT: + { + union wpa_event_data data; + struct wpabuf *ric = NULL; + os_memset(&data, 0, sizeof(data)); + data.ft_ies.ies = mgmt->u.auth.variable; + data.ft_ies.ies_len = len - + (mgmt->u.auth.variable - (u8 *) mgmt); + os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN); + if (os_strcmp(wpa_s->driver->name, "test") == 0 && + wpa_s->mlme.wmm_enabled) { + ric = wpabuf_alloc(200); + if (ric) { + /* Build simple RIC-Request: RDIE | TSPEC */ + + /* RIC Data (RDIE) */ + wpabuf_put_u8(ric, WLAN_EID_RIC_DATA); + wpabuf_put_u8(ric, 4); + wpabuf_put_u8(ric, 0); /* RDIE Identifier */ + wpabuf_put_u8(ric, 1); /* Resource Descriptor + * Count */ + wpabuf_put_le16(ric, 0); /* Status Code */ + + /* WMM TSPEC */ + ieee80211_build_tspec(ric); + + data.ft_ies.ric_ies = wpabuf_head(ric); + data.ft_ies.ric_ies_len = wpabuf_len(ric); + } + } + + wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); + wpabuf_free(ric); + ieee80211_auth_completed(wpa_s); + break; + } +#endif /* CONFIG_IEEE80211R */ + } +} + + +static void ieee80211_rx_mgmt_deauth(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + u16 reason_code; + + if (len < 24 + 2) { + wpa_printf(MSG_DEBUG, "MLME: too short (%lu) deauthentication " + "frame received from " MACSTR " - ignored", + (unsigned long) len, MAC2STR(mgmt->sa)); + return; + } + + if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: deauthentication frame received " + "from unknown AP (SA=" MACSTR " BSSID=" MACSTR + ") - ignored", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + wpa_printf(MSG_DEBUG, "MLME: RX deauthentication from " MACSTR + " (reason=%d)", MAC2STR(mgmt->sa), reason_code); + + if (wpa_s->mlme.authenticated) + wpa_printf(MSG_DEBUG, "MLME: deauthenticated"); + + if (wpa_s->mlme.state == IEEE80211_AUTHENTICATE || + wpa_s->mlme.state == IEEE80211_ASSOCIATE || + wpa_s->mlme.state == IEEE80211_ASSOCIATED) { + wpa_s->mlme.state = IEEE80211_AUTHENTICATE; + ieee80211_reschedule_timer(wpa_s, + IEEE80211_RETRY_AUTH_INTERVAL); + } + + ieee80211_set_associated(wpa_s, 0); + wpa_s->mlme.authenticated = 0; +} + + +static void ieee80211_rx_mgmt_disassoc(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + u16 reason_code; + + if (len < 24 + 2) { + wpa_printf(MSG_DEBUG, "MLME: too short (%lu) disassociation " + "frame received from " MACSTR " - ignored", + (unsigned long) len, MAC2STR(mgmt->sa)); + return; + } + + if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: disassociation frame received " + "from unknown AP (SA=" MACSTR " BSSID=" MACSTR + ") - ignored", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + reason_code = le_to_host16(mgmt->u.disassoc.reason_code); + + wpa_printf(MSG_DEBUG, "MLME: RX disassociation from " MACSTR + " (reason=%d)", MAC2STR(mgmt->sa), reason_code); + + if (wpa_s->mlme.associated) + wpa_printf(MSG_DEBUG, "MLME: disassociated"); + + if (wpa_s->mlme.state == IEEE80211_ASSOCIATED) { + wpa_s->mlme.state = IEEE80211_ASSOCIATE; + ieee80211_reschedule_timer(wpa_s, + IEEE80211_RETRY_AUTH_INTERVAL); + } + + ieee80211_set_associated(wpa_s, 0); +} + + +static void ieee80211_build_tspec(struct wpabuf *buf) +{ + struct wmm_tspec_element *tspec; + int tid, up; + + tspec = wpabuf_put(buf, sizeof(*tspec)); + tspec->eid = WLAN_EID_VENDOR_SPECIFIC; + tspec->length = sizeof(*tspec) - 2; + tspec->oui[0] = 0x00; + tspec->oui[1] = 0x50; + tspec->oui[2] = 0xf2; + tspec->oui_type = 2; + tspec->oui_subtype = 2; + tspec->version = 1; + + tid = 1; + up = 6; /* Voice */ + tspec->ts_info[0] = (tid << 1) | + (WMM_TSPEC_DIRECTION_BI_DIRECTIONAL << 5) | + BIT(7); + tspec->ts_info[1] = up << 3; + tspec->nominal_msdu_size = host_to_le16(1530); + tspec->mean_data_rate = host_to_le32(128000); /* bits per second */ + tspec->minimum_phy_rate = host_to_le32(6000000); + tspec->surplus_bandwidth_allowance = host_to_le16(0x3000); /* 150% */ +} + + +static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + struct ieee80211_mgmt *mgmt; + size_t alen; + + wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC"); + mgmt = NULL; + alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt; + + buf = wpabuf_alloc(alen + sizeof(struct wmm_tspec_element)); + if (buf == NULL) + return; + + mgmt = wpabuf_put(buf, alen); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WMM; + mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ; + mgmt->u.action.u.wmm_action.dialog_token = 1; + mgmt->u.action.u.wmm_action.status_code = 0; + + ieee80211_build_tspec(buf); + + ieee80211_sta_tx(wpa_s, wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); +} + + +static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status, + int reassoc) +{ + u8 rates[32]; + size_t rates_len; + u16 capab_info, status_code, aid; + struct ieee802_11_elems elems; + u8 *pos; + + /* AssocResp and ReassocResp have identical structure, so process both + * of them in this function. */ + + if (wpa_s->mlme.state != IEEE80211_ASSOCIATE) { + wpa_printf(MSG_DEBUG, "MLME: association frame received from " + MACSTR ", but not in associate state - ignored", + MAC2STR(mgmt->sa)); + return; + } + + if (len < 24 + 6) { + wpa_printf(MSG_DEBUG, "MLME: too short (%lu) association " + "frame received from " MACSTR " - ignored", + (unsigned long) len, MAC2STR(mgmt->sa)); + return; + } + + if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: association frame received from " + "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - " + "ignored", MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + capab_info = le_to_host16(mgmt->u.assoc_resp.capab_info); + status_code = le_to_host16(mgmt->u.assoc_resp.status_code); + aid = le_to_host16(mgmt->u.assoc_resp.aid); + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) + wpa_printf(MSG_DEBUG, "MLME: invalid aid value %d; bits 15:14 " + "not set", aid); + aid &= ~(BIT(15) | BIT(14)); + + wpa_printf(MSG_DEBUG, "MLME: RX %sssocResp from " MACSTR + " (capab=0x%x status=%d aid=%d)", + reassoc ? "Rea" : "A", MAC2STR(mgmt->sa), + capab_info, status_code, aid); + + pos = mgmt->u.assoc_resp.variable; + if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0) + == ParseFailed) { + wpa_printf(MSG_DEBUG, "MLME: failed to parse AssocResp"); + return; + } + + if (status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "MLME: AP denied association (code=%d)", + status_code); +#ifdef CONFIG_IEEE80211W + if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && + elems.timeout_int && elems.timeout_int_len == 5 && + elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { + u32 tu, ms; + tu = WPA_GET_LE32(elems.timeout_int + 1); + ms = tu * 1024 / 1000; + wpa_printf(MSG_DEBUG, "MLME: AP rejected association " + "temporarily; comeback duration %u TU " + "(%u ms)", tu, ms); + if (ms > IEEE80211_ASSOC_TIMEOUT) { + wpa_printf(MSG_DEBUG, "MLME: Update timer " + "based on comeback duration"); + ieee80211_reschedule_timer(wpa_s, ms); + } + } +#endif /* CONFIG_IEEE80211W */ + return; + } + + if (elems.supp_rates == NULL) { + wpa_printf(MSG_DEBUG, "MLME: no SuppRates element in " + "AssocResp"); + return; + } + + if (wpa_s->mlme.auth_alg == WLAN_AUTH_FT) { + if (!reassoc) { + wpa_printf(MSG_DEBUG, "MLME: AP tried to use " + "association, not reassociation, response " + "with FT"); + return; + } + if (wpa_ft_validate_reassoc_resp( + wpa_s->wpa, pos, len - (pos - (u8 *) mgmt), + mgmt->sa) < 0) { + wpa_printf(MSG_DEBUG, "MLME: FT validation of Reassoc" + "Resp failed"); + return; + } + } else if (wpa_sm_set_ft_params(wpa_s->wpa, pos, + len - (pos - (u8 *) mgmt)) < 0) + return; + + wpa_printf(MSG_DEBUG, "MLME: associated"); + wpa_s->mlme.aid = aid; + wpa_s->mlme.ap_capab = capab_info; + + os_free(wpa_s->mlme.assocresp_ies); + wpa_s->mlme.assocresp_ies_len = len - (pos - (u8 *) mgmt); + wpa_s->mlme.assocresp_ies = os_malloc(wpa_s->mlme.assocresp_ies_len); + if (wpa_s->mlme.assocresp_ies) { + os_memcpy(wpa_s->mlme.assocresp_ies, pos, + wpa_s->mlme.assocresp_ies_len); + } + + ieee80211_set_associated(wpa_s, 1); + + rates_len = elems.supp_rates_len; + if (rates_len > sizeof(rates)) + rates_len = sizeof(rates); + os_memcpy(rates, elems.supp_rates, rates_len); + if (elems.ext_supp_rates) { + size_t _len = elems.ext_supp_rates_len; + if (_len > sizeof(rates) - rates_len) + _len = sizeof(rates) - rates_len; + os_memcpy(rates + rates_len, elems.ext_supp_rates, _len); + rates_len += _len; + } + + if (wpa_drv_set_bssid(wpa_s, wpa_s->bssid) < 0) { + wpa_printf(MSG_DEBUG, "MLME: failed to set BSSID for the " + "netstack"); + } + if (wpa_drv_set_ssid(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) < + 0) { + wpa_printf(MSG_DEBUG, "MLME: failed to set SSID for the " + "netstack"); + } + + /* Remove STA entry before adding a new one just in case to avoid + * problems with existing configuration (e.g., keys). */ + wpa_drv_mlme_remove_sta(wpa_s, wpa_s->bssid); + if (wpa_drv_mlme_add_sta(wpa_s, wpa_s->bssid, rates, rates_len) < 0) { + wpa_printf(MSG_DEBUG, "MLME: failed to add STA entry to the " + "netstack"); + } + + if (elems.wmm && wpa_s->mlme.wmm_enabled) + ieee80211_sta_wmm_params(wpa_s, elems.wmm, elems.wmm_len); + + ieee80211_associated(wpa_s); + + if (wpa_s->mlme.auth_alg != WLAN_AUTH_FT && + os_strcmp(wpa_s->driver->name, "test") == 0 && + elems.wmm && wpa_s->mlme.wmm_enabled) { + /* Test WMM-AC - send ADDTS for WMM TSPEC */ + ieee80211_tx_addts(wpa_s); + } +} + + +/* Caller must hold local->sta_bss_lock */ +static void __ieee80211_bss_hash_add(struct wpa_supplicant *wpa_s, + struct ieee80211_sta_bss *bss) +{ + bss->hnext = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]; + wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] = bss; +} + + +/* Caller must hold local->sta_bss_lock */ +static void __ieee80211_bss_hash_del(struct wpa_supplicant *wpa_s, + struct ieee80211_sta_bss *bss) +{ + struct ieee80211_sta_bss *b, *prev = NULL; + b = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]; + while (b) { + if (b == bss) { + if (prev == NULL) { + wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] + = bss->hnext; + } else { + prev->hnext = bss->hnext; + } + break; + } + prev = b; + b = b->hnext; + } +} + + +static struct ieee80211_sta_bss * +ieee80211_bss_add(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct ieee80211_sta_bss *bss; + + bss = os_zalloc(sizeof(*bss)); + if (bss == NULL) + return NULL; + os_memcpy(bss->bssid, bssid, ETH_ALEN); + + /* TODO: order by RSSI? */ + bss->next = wpa_s->mlme.sta_bss_list; + wpa_s->mlme.sta_bss_list = bss; + __ieee80211_bss_hash_add(wpa_s, bss); + return bss; +} + + +static struct ieee80211_sta_bss * +ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct ieee80211_sta_bss *bss; + + bss = wpa_s->mlme.sta_bss_hash[STA_HASH(bssid)]; + while (bss) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + break; + bss = bss->hnext; + } + return bss; +} + + +static void ieee80211_bss_free(struct wpa_supplicant *wpa_s, + struct ieee80211_sta_bss *bss) +{ + __ieee80211_bss_hash_del(wpa_s, bss); + os_free(bss->ie); + os_free(bss->wpa_ie); + os_free(bss->rsn_ie); + os_free(bss->wmm_ie); + os_free(bss->mdie); + os_free(bss); +} + + +static void ieee80211_bss_list_deinit(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_sta_bss *bss, *prev; + + bss = wpa_s->mlme.sta_bss_list; + wpa_s->mlme.sta_bss_list = NULL; + while (bss) { + prev = bss; + bss = bss->next; + ieee80211_bss_free(wpa_s, prev); + } +} + + +static void ieee80211_bss_info(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status, + int beacon) +{ + struct ieee802_11_elems elems; + size_t baselen; + int channel, invalid = 0, clen; + struct ieee80211_sta_bss *bss; + u64 timestamp; + u8 *pos, *ie_pos; + size_t ie_len; + + if (!beacon && os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN)) + return; /* ignore ProbeResp to foreign address */ + +#if 0 + wpa_printf(MSG_MSGDUMP, "MLME: RX %s from " MACSTR " to " MACSTR, + beacon ? "Beacon" : "Probe Response", + MAC2STR(mgmt->sa), MAC2STR(mgmt->da)); +#endif + + baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; + if (baselen > len) + return; + + pos = mgmt->u.beacon.timestamp; + timestamp = WPA_GET_LE64(pos); + +#if 0 /* FIX */ + if (local->conf.mode == IW_MODE_ADHOC && beacon && + os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) { +#ifdef IEEE80211_IBSS_DEBUG + static unsigned long last_tsf_debug = 0; + u64 tsf; + if (local->hw->get_tsf) + tsf = local->hw->get_tsf(local->mdev); + else + tsf = -1LLU; + if (time_after(jiffies, last_tsf_debug + 5 * HZ)) { + wpa_printf(MSG_DEBUG, "RX beacon SA=" MACSTR " BSSID=" + MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld " + "@%ld", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), + tsf, timestamp, tsf - timestamp, jiffies); + last_tsf_debug = jiffies; + } +#endif /* IEEE80211_IBSS_DEBUG */ + } +#endif + + ie_pos = mgmt->u.beacon.variable; + ie_len = len - baselen; + if (ieee802_11_parse_elems(ie_pos, ie_len, &elems, 0) == ParseFailed) + invalid = 1; + +#if 0 /* FIX */ + if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates && + os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 && + (sta = sta_info_get(local, mgmt->sa))) { + struct ieee80211_rate *rates; + size_t num_rates; + u32 supp_rates, prev_rates; + int i, j, oper_mode; + + rates = local->curr_rates; + num_rates = local->num_curr_rates; + oper_mode = wpa_s->mlme.sta_scanning ? + local->scan_oper_phymode : local->conf.phymode; + for (i = 0; i < local->hw->num_modes; i++) { + struct ieee80211_hw_modes *mode = &local->hw->modes[i]; + if (oper_mode == mode->mode) { + rates = mode->rates; + num_rates = mode->num_rates; + break; + } + } + + supp_rates = 0; + for (i = 0; i < elems.supp_rates_len + + elems.ext_supp_rates_len; i++) { + u8 rate = 0; + int own_rate; + if (i < elems.supp_rates_len) + rate = elems.supp_rates[i]; + else if (elems.ext_supp_rates) + rate = elems.ext_supp_rates + [i - elems.supp_rates_len]; + own_rate = 5 * (rate & 0x7f); + if (oper_mode == MODE_ATHEROS_TURBO) + own_rate *= 2; + for (j = 0; j < num_rates; j++) + if (rates[j].rate == own_rate) + supp_rates |= BIT(j); + } + + prev_rates = sta->supp_rates; + sta->supp_rates &= supp_rates; + if (sta->supp_rates == 0) { + /* No matching rates - this should not really happen. + * Make sure that at least one rate is marked + * supported to avoid issues with TX rate ctrl. */ + sta->supp_rates = wpa_s->mlme.supp_rates_bits; + } + if (sta->supp_rates != prev_rates) { + wpa_printf(MSG_DEBUG, "MLME: updated supp_rates set " + "for " MACSTR " based on beacon info " + "(0x%x & 0x%x -> 0x%x)", + MAC2STR(sta->addr), prev_rates, + supp_rates, sta->supp_rates); + } + sta_info_release(local, sta); + } +#endif + + if (elems.ssid == NULL) + return; + + if (elems.ds_params && elems.ds_params_len == 1) + channel = elems.ds_params[0]; + else + channel = rx_status->channel; + + bss = ieee80211_bss_get(wpa_s, mgmt->bssid); + if (bss == NULL) { + bss = ieee80211_bss_add(wpa_s, mgmt->bssid); + if (bss == NULL) + return; + } else { +#if 0 + /* TODO: order by RSSI? */ + spin_lock_bh(&local->sta_bss_lock); + list_move_tail(&bss->list, &local->sta_bss_list); + spin_unlock_bh(&local->sta_bss_lock); +#endif + } + + if (bss->probe_resp && beacon) { + /* Do not allow beacon to override data from Probe Response. */ + return; + } + + bss->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); + bss->capability = le_to_host16(mgmt->u.beacon.capab_info); + + if (bss->ie == NULL || bss->ie_len < ie_len) { + os_free(bss->ie); + bss->ie = os_malloc(ie_len); + } + if (bss->ie) { + os_memcpy(bss->ie, ie_pos, ie_len); + bss->ie_len = ie_len; + } + + if (elems.ssid && elems.ssid_len <= MAX_SSID_LEN) { + os_memcpy(bss->ssid, elems.ssid, elems.ssid_len); + bss->ssid_len = elems.ssid_len; + } + + bss->supp_rates_len = 0; + if (elems.supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; + if (clen > elems.supp_rates_len) + clen = elems.supp_rates_len; + os_memcpy(&bss->supp_rates[bss->supp_rates_len], + elems.supp_rates, clen); + bss->supp_rates_len += clen; + } + if (elems.ext_supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; + if (clen > elems.ext_supp_rates_len) + clen = elems.ext_supp_rates_len; + os_memcpy(&bss->supp_rates[bss->supp_rates_len], + elems.ext_supp_rates, clen); + bss->supp_rates_len += clen; + } + + if (elems.wpa_ie && + (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_ie_len || + os_memcmp(bss->wpa_ie, elems.wpa_ie, elems.wpa_ie_len))) { + os_free(bss->wpa_ie); + bss->wpa_ie = os_malloc(elems.wpa_ie_len + 2); + if (bss->wpa_ie) { + os_memcpy(bss->wpa_ie, elems.wpa_ie - 2, + elems.wpa_ie_len + 2); + bss->wpa_ie_len = elems.wpa_ie_len + 2; + } else + bss->wpa_ie_len = 0; + } else if (!elems.wpa_ie && bss->wpa_ie) { + os_free(bss->wpa_ie); + bss->wpa_ie = NULL; + bss->wpa_ie_len = 0; + } + + if (elems.rsn_ie && + (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_ie_len || + os_memcmp(bss->rsn_ie, elems.rsn_ie, elems.rsn_ie_len))) { + os_free(bss->rsn_ie); + bss->rsn_ie = os_malloc(elems.rsn_ie_len + 2); + if (bss->rsn_ie) { + os_memcpy(bss->rsn_ie, elems.rsn_ie - 2, + elems.rsn_ie_len + 2); + bss->rsn_ie_len = elems.rsn_ie_len + 2; + } else + bss->rsn_ie_len = 0; + } else if (!elems.rsn_ie && bss->rsn_ie) { + os_free(bss->rsn_ie); + bss->rsn_ie = NULL; + bss->rsn_ie_len = 0; + } + + if (elems.wmm && + (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wmm_len || + os_memcmp(bss->wmm_ie, elems.wmm, elems.wmm_len))) { + os_free(bss->wmm_ie); + bss->wmm_ie = os_malloc(elems.wmm_len + 2); + if (bss->wmm_ie) { + os_memcpy(bss->wmm_ie, elems.wmm - 2, + elems.wmm_len + 2); + bss->wmm_ie_len = elems.wmm_len + 2; + } else + bss->wmm_ie_len = 0; + } else if (!elems.wmm && bss->wmm_ie) { + os_free(bss->wmm_ie); + bss->wmm_ie = NULL; + bss->wmm_ie_len = 0; + } + +#ifdef CONFIG_IEEE80211R + if (elems.mdie && + (bss->mdie == NULL || bss->mdie_len != elems.mdie_len || + os_memcmp(bss->mdie, elems.mdie, elems.mdie_len))) { + os_free(bss->mdie); + bss->mdie = os_malloc(elems.mdie_len + 2); + if (bss->mdie) { + os_memcpy(bss->mdie, elems.mdie - 2, + elems.mdie_len + 2); + bss->mdie_len = elems.mdie_len + 2; + } else + bss->mdie_len = 0; + } else if (!elems.mdie && bss->mdie) { + os_free(bss->mdie); + bss->mdie = NULL; + bss->mdie_len = 0; + } +#endif /* CONFIG_IEEE80211R */ + + bss->hw_mode = wpa_s->mlme.phymode; + bss->channel = channel; + bss->freq = wpa_s->mlme.freq; + if (channel != wpa_s->mlme.channel && + (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G || + wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211B) && + channel >= 1 && channel <= 14) { + static const int freq_list[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 + }; + /* IEEE 802.11g/b mode can receive packets from neighboring + * channels, so map the channel into frequency. */ + bss->freq = freq_list[channel - 1]; + } + bss->timestamp = timestamp; + os_get_time(&bss->last_update); + bss->rssi = rx_status->ssi; + if (!beacon) + bss->probe_resp++; +} + + +static void ieee80211_rx_mgmt_probe_resp(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 0); +} + + +static void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + int use_protection; + size_t baselen; + struct ieee802_11_elems elems; + + ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 1); + + if (!wpa_s->mlme.associated || + os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) + return; + + /* Process beacon from the current BSS */ + baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; + if (baselen > len) + return; + + if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, + &elems, 0) == ParseFailed) + return; + + use_protection = 0; + if (elems.erp_info && elems.erp_info_len >= 1) { + use_protection = + (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0; + } + + if (use_protection != !!wpa_s->mlme.use_protection) { + wpa_printf(MSG_DEBUG, "MLME: CTS protection %s (BSSID=" MACSTR + ")", + use_protection ? "enabled" : "disabled", + MAC2STR(wpa_s->bssid)); + wpa_s->mlme.use_protection = use_protection ? 1 : 0; + wpa_s->mlme.cts_protect_erp_frames = use_protection; + } + + if (elems.wmm && wpa_s->mlme.wmm_enabled) { + ieee80211_sta_wmm_params(wpa_s, elems.wmm, + elems.wmm_len); + } +} + + +static void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + int tx_last_beacon, adhoc; +#if 0 /* FIX */ + struct ieee80211_mgmt *resp; +#endif + u8 *pos, *end; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + adhoc = ssid && ssid->mode == WPAS_MODE_IBSS; + + if (!adhoc || wpa_s->mlme.state != IEEE80211_IBSS_JOINED || + len < 24 + 2 || wpa_s->mlme.probe_resp == NULL) + return; + +#if 0 /* FIX */ + if (local->hw->tx_last_beacon) + tx_last_beacon = local->hw->tx_last_beacon(local->mdev); + else +#endif + tx_last_beacon = 1; + +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, "MLME: RX ProbeReq SA=" MACSTR " DA=" MACSTR + " BSSID=" MACSTR " (tx_last_beacon=%d)", + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), + MAC2STR(mgmt->bssid), tx_last_beacon); +#endif /* IEEE80211_IBSS_DEBUG */ + + if (!tx_last_beacon) + return; + + if (os_memcmp(mgmt->bssid, wpa_s->bssid, ETH_ALEN) != 0 && + os_memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) + return; + + end = ((u8 *) mgmt) + len; + pos = mgmt->u.probe_req.variable; + if (pos[0] != WLAN_EID_SSID || + pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "MLME: Invalid SSID IE in ProbeReq from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + if (pos[1] != 0 && + (pos[1] != wpa_s->mlme.ssid_len || + os_memcmp(pos + 2, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) != 0)) + { + /* Ignore ProbeReq for foreign SSID */ + return; + } + +#if 0 /* FIX */ + /* Reply with ProbeResp */ + skb = skb_copy(wpa_s->mlme.probe_resp, GFP_ATOMIC); + if (skb == NULL) + return; + + resp = (struct ieee80211_mgmt *) skb->data; + os_memcpy(resp->da, mgmt->sa, ETH_ALEN); +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, "MLME: Sending ProbeResp to " MACSTR, + MAC2STR(resp->da)); +#endif /* IEEE80211_IBSS_DEBUG */ + ieee80211_sta_tx(wpa_s, skb, 0, 1); +#endif +} + + +#ifdef CONFIG_IEEE80211R +static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + union wpa_event_data data; + u16 status; + u8 *sta_addr, *target_ap_addr; + + if (len < 24 + 1 + sizeof(mgmt->u.action.u.ft_action_resp)) { + wpa_printf(MSG_DEBUG, "MLME: Too short FT Action frame"); + return; + } + + /* + * Only FT Action Response is needed for now since reservation + * protocol is not supported. + */ + if (mgmt->u.action.u.ft_action_resp.action != 2) { + wpa_printf(MSG_DEBUG, "MLME: Unexpected FT Action %d", + mgmt->u.action.u.ft_action_resp.action); + return; + } + + status = le_to_host16(mgmt->u.action.u.ft_action_resp.status_code); + sta_addr = mgmt->u.action.u.ft_action_resp.sta_addr; + target_ap_addr = mgmt->u.action.u.ft_action_resp.target_ap_addr; + wpa_printf(MSG_DEBUG, "MLME: Received FT Action Response: STA " MACSTR + " TargetAP " MACSTR " Status Code %d", + MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); + if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR + " in FT Action Response", MAC2STR(sta_addr)); + return; + } + + if (status) { + wpa_printf(MSG_DEBUG, "MLME: FT Action Response indicates " + "failure (status code %d)", status); + /* TODO: report error to FT code(?) */ + return; + } + + os_memset(&data, 0, sizeof(data)); + data.ft_ies.ies = mgmt->u.action.u.ft_action_resp.variable; + data.ft_ies.ies_len = len - (mgmt->u.action.u.ft_action_resp.variable - + (u8 *) mgmt); + data.ft_ies.ft_action = 1; + os_memcpy(data.ft_ies.target_ap, target_ap_addr, ETH_ALEN); + wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); + /* TODO: should only re-associate, if EVENT_FT_RESPONSE was processed + * successfully */ + wpa_s->mlme.prev_bssid_set = 1; + wpa_s->mlme.auth_alg = WLAN_AUTH_FT; + os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN); + os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN); + ieee80211_associate(wpa_s); +} +#endif /* CONFIG_IEEE80211R */ + + +#ifdef CONFIG_IEEE80211W + +/* MLME-SAQuery.response */ +static int ieee80211_sta_send_sa_query_resp(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *trans_id) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + + mgmt = os_zalloc(sizeof(*mgmt)); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "SA Query action frame"); + return -1; + } + + len = 24; + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_SA_QUERY; + mgmt->u.action.u.sa_query_resp.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(mgmt->u.action.u.sa_query_resp.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + len += 1 + sizeof(mgmt->u.action.u.sa_query_resp); + + res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len); + os_free(mgmt); + + return res; +} + + +static void ieee80211_rx_mgmt_sa_query_action( + struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status) +{ + if (len < 24 + 1 + sizeof(mgmt->u.action.u.sa_query_req)) { + wpa_printf(MSG_DEBUG, "MLME: Too short SA Query Action frame"); + return; + } + + if (mgmt->u.action.u.sa_query_req.action != WLAN_SA_QUERY_REQUEST) { + wpa_printf(MSG_DEBUG, "MLME: Unexpected SA Query Action %d", + mgmt->u.action.u.sa_query_req.action); + return; + } + + if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLME: Ignore SA Query from unknown " + "source " MACSTR, MAC2STR(mgmt->sa)); + return; + } + + if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) { + wpa_printf(MSG_DEBUG, "MLME: Ignore SA query request during " + "association process"); + return; + } + + wpa_printf(MSG_DEBUG, "MLME: Replying to SA Query request"); + ieee80211_sta_send_sa_query_resp(wpa_s, mgmt->sa, mgmt->u.action.u. + sa_query_req.trans_id); +} + +#endif /* CONFIG_IEEE80211W */ + + +static void dump_tspec(struct wmm_tspec_element *tspec) +{ + int up, psb, dir, tid; + u16 val; + + up = (tspec->ts_info[1] >> 3) & 0x07; + psb = (tspec->ts_info[1] >> 2) & 0x01; + dir = (tspec->ts_info[0] >> 5) & 0x03; + tid = (tspec->ts_info[0] >> 1) & 0x0f; + wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d", + up, psb, dir, tid); + val = le_to_host16(tspec->nominal_msdu_size); + wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s", + val & 0x7fff, val & 0x8000 ? " (fixed)" : ""); + wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps", + le_to_host32(tspec->mean_data_rate)); + wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps", + le_to_host32(tspec->minimum_phy_rate)); + val = le_to_host16(tspec->surplus_bandwidth_allowance); + wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u", + val >> 13, 10000 * (val & 0x1fff) / 0x2000); + val = le_to_host16(tspec->medium_time); + wpa_printf(MSG_DEBUG, "WMM: Medium Time: %u (= %u usec/sec)", + val, 32 * val); +} + + +static int is_wmm_tspec(const u8 *ie, size_t len) +{ + const struct wmm_tspec_element *tspec; + + if (len < sizeof(*tspec)) + return 0; + + tspec = (const struct wmm_tspec_element *) ie; + if (tspec->eid != WLAN_EID_VENDOR_SPECIFIC || + tspec->length < sizeof(*tspec) - 2 || + tspec->oui[0] != 0x00 || tspec->oui[1] != 0x50 || + tspec->oui[2] != 0xf2 || tspec->oui_type != 2 || + tspec->oui_subtype != 2 || tspec->version != 1) + return 0; + + return 1; +} + + +static void ieee80211_rx_addts_resp( + struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, + size_t var_len) +{ + struct wmm_tspec_element *tspec; + + wpa_printf(MSG_DEBUG, "WMM: Received ADDTS Response"); + wpa_hexdump(MSG_MSGDUMP, "WMM: ADDTS Response IE(s)", + mgmt->u.action.u.wmm_action.variable, var_len); + if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len)) + return; + tspec = (struct wmm_tspec_element *) + mgmt->u.action.u.wmm_action.variable; + dump_tspec(tspec); +} + + +static void ieee80211_rx_delts( + struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, + size_t var_len) +{ + struct wmm_tspec_element *tspec; + + wpa_printf(MSG_DEBUG, "WMM: Received DELTS"); + wpa_hexdump(MSG_MSGDUMP, "WMM: DELTS IE(s)", + mgmt->u.action.u.wmm_action.variable, var_len); + if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len)) + return; + tspec = (struct wmm_tspec_element *) + mgmt->u.action.u.wmm_action.variable; + dump_tspec(tspec); +} + + +static void ieee80211_rx_mgmt_wmm_action( + struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status) +{ + size_t alen; + + alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt; + if (len < alen) { + wpa_printf(MSG_DEBUG, "WMM: Received Action frame too short"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM: Received Action frame: Action Code %d, " + "Dialog Token %d, Status Code %d", + mgmt->u.action.u.wmm_action.action_code, + mgmt->u.action.u.wmm_action.dialog_token, + mgmt->u.action.u.wmm_action.status_code); + + switch (mgmt->u.action.u.wmm_action.action_code) { + case WMM_ACTION_CODE_ADDTS_RESP: + ieee80211_rx_addts_resp(wpa_s, mgmt, len, len - alen); + break; + case WMM_ACTION_CODE_DELTS: + ieee80211_rx_delts(wpa_s, mgmt, len, len - alen); + break; + default: + wpa_printf(MSG_DEBUG, "WMM: Unsupported Action Code %d", + mgmt->u.action.u.wmm_action.action_code); + break; + } +} + + +static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + wpa_printf(MSG_DEBUG, "MLME: received Action frame"); + + if (len < 25) + return; + + switch (mgmt->u.action.category) { +#ifdef CONFIG_IEEE80211R + case WLAN_ACTION_FT: + ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status); + break; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + case WLAN_ACTION_SA_QUERY: + ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status); + break; +#endif /* CONFIG_IEEE80211W */ + case WLAN_ACTION_WMM: + ieee80211_rx_mgmt_wmm_action(wpa_s, mgmt, len, rx_status); + break; + case WLAN_ACTION_PUBLIC: + if (wpa_s->mlme.public_action_cb) { + wpa_s->mlme.public_action_cb( + wpa_s->mlme.public_action_cb_ctx, + (u8 *) mgmt, len, rx_status->freq); + return; + } + break; + default: + wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d", + mgmt->u.action.category); + break; + } +} + + +static void ieee80211_sta_rx_mgmt(struct wpa_supplicant *wpa_s, + const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt; + u16 fc; + + if (len < 24) + return; + + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + + switch (WLAN_FC_GET_STYPE(fc)) { + case WLAN_FC_STYPE_PROBE_REQ: + ieee80211_rx_mgmt_probe_req(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_BEACON: + ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_AUTH: + ieee80211_rx_mgmt_auth(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 0); + break; + case WLAN_FC_STYPE_REASSOC_RESP: + ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 1); + break; + case WLAN_FC_STYPE_DEAUTH: + ieee80211_rx_mgmt_deauth(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_DISASSOC: + ieee80211_rx_mgmt_disassoc(wpa_s, mgmt, len, rx_status); + break; + case WLAN_FC_STYPE_ACTION: + ieee80211_rx_mgmt_action(wpa_s, mgmt, len, rx_status); + break; + default: + wpa_printf(MSG_DEBUG, "MLME: received unknown management " + "frame - stype=%d", WLAN_FC_GET_STYPE(fc)); + break; + } +} + + +static void ieee80211_sta_rx_scan(struct wpa_supplicant *wpa_s, + const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt; + u16 fc; + + if (len < 24) + return; + + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) { + if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { + ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, + len, rx_status); + } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) { + ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status); + } + } +} + + +static int ieee80211_sta_active_ibss(struct wpa_supplicant *wpa_s) +{ + int active = 0; + +#if 0 /* FIX */ + list_for_each(ptr, &local->sta_list) { + sta = list_entry(ptr, struct sta_info, list); + if (sta->dev == dev && + time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, + jiffies)) { + active++; + break; + } + } +#endif + + return active; +} + + +static void ieee80211_sta_expire(struct wpa_supplicant *wpa_s) +{ +#if 0 /* FIX */ + list_for_each_safe(ptr, n, &local->sta_list) { + sta = list_entry(ptr, struct sta_info, list); + if (time_after(jiffies, sta->last_rx + + IEEE80211_IBSS_INACTIVITY_LIMIT)) { + wpa_printf(MSG_DEBUG, "MLME: expiring inactive STA " + MACSTR, MAC2STR(sta->addr)); + sta_info_free(local, sta, 1); + } + } +#endif +} + + +static void ieee80211_sta_merge_ibss(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_scan_params params; + + ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL); + + ieee80211_sta_expire(wpa_s); + if (ieee80211_sta_active_ibss(wpa_s)) + return; + + wpa_printf(MSG_DEBUG, "MLME: No active IBSS STAs - trying to scan for " + "other IBSS networks with same SSID (merge)"); + os_memset(¶ms, 0, sizeof(params)); + params.ssids[0].ssid = wpa_s->mlme.ssid; + params.ssids[0].ssid_len = wpa_s->mlme.ssid_len; + params.num_ssids = wpa_s->mlme.ssid_len ? 1 : 0; + ieee80211_sta_req_scan(wpa_s, ¶ms); +} + + +static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + switch (wpa_s->mlme.state) { + case IEEE80211_DISABLED: + break; + case IEEE80211_AUTHENTICATE: + ieee80211_authenticate(wpa_s); + break; + case IEEE80211_ASSOCIATE: + ieee80211_associate(wpa_s); + break; + case IEEE80211_ASSOCIATED: + ieee80211_associated(wpa_s); + break; + case IEEE80211_IBSS_SEARCH: + ieee80211_sta_find_ibss(wpa_s); + break; + case IEEE80211_IBSS_JOINED: + ieee80211_sta_merge_ibss(wpa_s); + break; + default: + wpa_printf(MSG_DEBUG, "ieee80211_sta_timer: Unknown state %d", + wpa_s->mlme.state); + break; + } + + if (ieee80211_privacy_mismatch(wpa_s)) { + wpa_printf(MSG_DEBUG, "MLME: privacy configuration mismatch " + "and mixed-cell disabled - disassociate"); + + ieee80211_send_disassoc(wpa_s, WLAN_REASON_UNSPECIFIED); + ieee80211_set_associated(wpa_s, 0); + } +} + + +static void ieee80211_sta_new_auth(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + if (ssid && ssid->mode != WPAS_MODE_INFRA) + return; + +#if 0 /* FIX */ + if (local->hw->reset_tsf) { + /* Reset own TSF to allow time synchronization work. */ + local->hw->reset_tsf(local->mdev); + } +#endif + + wpa_s->mlme.wmm_last_param_set = -1; /* allow any WMM update */ + + + if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN) + wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN; + else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED) + wpa_s->mlme.auth_alg = WLAN_AUTH_SHARED_KEY; + else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP) + wpa_s->mlme.auth_alg = WLAN_AUTH_LEAP; + else + wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN; + wpa_printf(MSG_DEBUG, "MLME: Initial auth_alg=%d", + wpa_s->mlme.auth_alg); + wpa_s->mlme.auth_transaction = -1; + wpa_s->mlme.auth_tries = wpa_s->mlme.assoc_tries = 0; + ieee80211_authenticate(wpa_s); +} + + +static int ieee80211_ibss_allowed(struct wpa_supplicant *wpa_s) +{ +#if 0 /* FIX */ + int m, c; + + for (m = 0; m < local->hw->num_modes; m++) { + struct ieee80211_hw_modes *mode = &local->hw->modes[m]; + if (mode->mode != local->conf.phymode) + continue; + for (c = 0; c < mode->num_channels; c++) { + struct ieee80211_channel *chan = &mode->channels[c]; + if (chan->flag & IEEE80211_CHAN_W_SCAN && + chan->chan == local->conf.channel) { + if (chan->flag & IEEE80211_CHAN_W_IBSS) + return 1; + break; + } + } + } +#endif + + return 0; +} + + +static int ieee80211_sta_join_ibss(struct wpa_supplicant *wpa_s, + struct ieee80211_sta_bss *bss) +{ + int res = 0, rates, done = 0, bssid_changed; + struct ieee80211_mgmt *mgmt; +#if 0 /* FIX */ + struct ieee80211_tx_control control; + struct ieee80211_rate *rate; + struct rate_control_extra extra; +#endif + u8 *pos, *buf; + size_t len; + + /* Remove possible STA entries from other IBSS networks. */ +#if 0 /* FIX */ + sta_info_flush(local, NULL); + + if (local->hw->reset_tsf) { + /* Reset own TSF to allow time synchronization work. */ + local->hw->reset_tsf(local->mdev); + } +#endif + bssid_changed = os_memcmp(wpa_s->bssid, bss->bssid, ETH_ALEN); + os_memcpy(wpa_s->bssid, bss->bssid, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); + +#if 0 /* FIX */ + local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10; + + sdata->drop_unencrypted = bss->capability & + host_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0; +#endif + +#if 0 /* FIX */ + os_memset(&rq, 0, sizeof(rq)); + rq.m = bss->freq * 100000; + rq.e = 1; + res = ieee80211_ioctl_siwfreq(wpa_s, NULL, &rq, NULL); +#endif + + if (!ieee80211_ibss_allowed(wpa_s)) { +#if 0 /* FIX */ + wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed on channel %d " + "(%d MHz)", local->conf.channel, + local->conf.freq); +#endif + return -1; + } + + /* Set beacon template based on scan results */ + buf = os_malloc(400); + len = 0; + do { + if (buf == NULL) + break; + + mgmt = (struct ieee80211_mgmt *) buf; + len += 24 + sizeof(mgmt->u.beacon); + os_memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_BEACON); + os_memset(mgmt->da, 0xff, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); +#if 0 /* FIX */ + mgmt->u.beacon.beacon_int = + host_to_le16(local->conf.beacon_int); +#endif + mgmt->u.beacon.capab_info = host_to_le16(bss->capability); + + pos = buf + len; + len += 2 + wpa_s->mlme.ssid_len; + *pos++ = WLAN_EID_SSID; + *pos++ = wpa_s->mlme.ssid_len; + os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); + + rates = bss->supp_rates_len; + if (rates > 8) + rates = 8; + pos = buf + len; + len += 2 + rates; + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = rates; + os_memcpy(pos, bss->supp_rates, rates); + + pos = buf + len; + len += 2 + 1; + *pos++ = WLAN_EID_DS_PARAMS; + *pos++ = 1; + *pos++ = bss->channel; + + pos = buf + len; + len += 2 + 2; + *pos++ = WLAN_EID_IBSS_PARAMS; + *pos++ = 2; + /* FIX: set ATIM window based on scan results */ + *pos++ = 0; + *pos++ = 0; + + if (bss->supp_rates_len > 8) { + rates = bss->supp_rates_len - 8; + pos = buf + len; + len += 2 + rates; + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = rates; + os_memcpy(pos, &bss->supp_rates[8], rates); + } + +#if 0 /* FIX */ + os_memset(&control, 0, sizeof(control)); + control.pkt_type = PKT_PROBE_RESP; + os_memset(&extra, 0, sizeof(extra)); + extra.endidx = local->num_curr_rates; + rate = rate_control_get_rate(wpa_s, skb, &extra); + if (rate == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to determine TX " + "rate for IBSS beacon"); + break; + } + control.tx_rate = (wpa_s->mlme.short_preamble && + (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? + rate->val2 : rate->val; + control.antenna_sel = local->conf.antenna_sel; + control.power_level = local->conf.power_level; + control.no_ack = 1; + control.retry_limit = 1; + control.rts_cts_duration = 0; +#endif + +#if 0 /* FIX */ + wpa_s->mlme.probe_resp = skb_copy(skb, GFP_ATOMIC); + if (wpa_s->mlme.probe_resp) { + mgmt = (struct ieee80211_mgmt *) + wpa_s->mlme.probe_resp->data; + mgmt->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_PROBE_RESP); + } else { + wpa_printf(MSG_DEBUG, "MLME: Could not allocate " + "ProbeResp template for IBSS"); + } + + if (local->hw->beacon_update && + local->hw->beacon_update(wpa_s, skb, &control) == 0) { + wpa_printf(MSG_DEBUG, "MLME: Configured IBSS beacon " + "template based on scan results"); + skb = NULL; + } + + rates = 0; + for (i = 0; i < bss->supp_rates_len; i++) { + int rate = (bss->supp_rates[i] & 0x7f) * 5; + if (local->conf.phymode == MODE_ATHEROS_TURBO) + rate *= 2; + for (j = 0; j < local->num_curr_rates; j++) + if (local->curr_rates[j] == rate) + rates |= BIT(j); + } + wpa_s->mlme.supp_rates_bits = rates; +#endif + done = 1; + } while (0); + + os_free(buf); + if (!done) { + wpa_printf(MSG_DEBUG, "MLME: Failed to configure IBSS beacon " + "template"); + } + + wpa_s->mlme.state = IEEE80211_IBSS_JOINED; + ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL); + + return res; +} + + +#if 0 /* FIX */ +static int ieee80211_sta_create_ibss(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_sta_bss *bss; + u8 bssid[ETH_ALEN], *pos; + int i; + +#if 0 + /* Easier testing, use fixed BSSID. */ + os_memset(bssid, 0xfe, ETH_ALEN); +#else + /* Generate random, not broadcast, locally administered BSSID. Mix in + * own MAC address to make sure that devices that do not have proper + * random number generator get different BSSID. */ + os_get_random(bssid, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + bssid[i] ^= wpa_s->own_addr[i]; + bssid[0] &= ~0x01; + bssid[0] |= 0x02; +#endif + + wpa_printf(MSG_DEBUG, "MLME: Creating new IBSS network, BSSID " + MACSTR "", MAC2STR(bssid)); + + bss = ieee80211_bss_add(wpa_s, bssid); + if (bss == NULL) + return -ENOMEM; + +#if 0 /* FIX */ + if (local->conf.beacon_int == 0) + local->conf.beacon_int = 100; + bss->beacon_int = local->conf.beacon_int; + bss->hw_mode = local->conf.phymode; + bss->channel = local->conf.channel; + bss->freq = local->conf.freq; +#endif + os_get_time(&bss->last_update); + bss->capability = host_to_le16(WLAN_CAPABILITY_IBSS); +#if 0 /* FIX */ + if (sdata->default_key) { + bss->capability |= host_to_le16(WLAN_CAPABILITY_PRIVACY); + } else + sdata->drop_unencrypted = 0; + bss->supp_rates_len = local->num_curr_rates; +#endif + pos = bss->supp_rates; +#if 0 /* FIX */ + for (i = 0; i < local->num_curr_rates; i++) { + int rate = local->curr_rates[i]; + if (local->conf.phymode == MODE_ATHEROS_TURBO) + rate /= 2; + *pos++ = (u8) (rate / 5); + } +#endif + + return ieee80211_sta_join_ibss(wpa_s, bss); +} +#endif + + +static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_sta_bss *bss; + int found = 0; + u8 bssid[ETH_ALEN]; + int active_ibss; + struct os_time now; + + if (wpa_s->mlme.ssid_len == 0) + return -EINVAL; + + active_ibss = ieee80211_sta_active_ibss(wpa_s); +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, "MLME: sta_find_ibss (active_ibss=%d)", + active_ibss); +#endif /* IEEE80211_IBSS_DEBUG */ + for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) { + if (wpa_s->mlme.ssid_len != bss->ssid_len || + os_memcmp(wpa_s->mlme.ssid, bss->ssid, bss->ssid_len) != 0 + || !(bss->capability & WLAN_CAPABILITY_IBSS)) + continue; +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, " bssid=" MACSTR " found", + MAC2STR(bss->bssid)); +#endif /* IEEE80211_IBSS_DEBUG */ + os_memcpy(bssid, bss->bssid, ETH_ALEN); + found = 1; + if (active_ibss || + os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) + break; + } + +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, " sta_find_ibss: selected " MACSTR " current " + MACSTR, MAC2STR(bssid), MAC2STR(wpa_s->bssid)); +#endif /* IEEE80211_IBSS_DEBUG */ + if (found && os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) != 0 && + (bss = ieee80211_bss_get(wpa_s, bssid))) { + wpa_printf(MSG_DEBUG, "MLME: Selected IBSS BSSID " MACSTR + " based on configured SSID", + MAC2STR(bssid)); + return ieee80211_sta_join_ibss(wpa_s, bss); + } +#ifdef IEEE80211_IBSS_DEBUG + wpa_printf(MSG_DEBUG, " did not try to join ibss"); +#endif /* IEEE80211_IBSS_DEBUG */ + + /* Selected IBSS not found in current scan results - try to scan */ + os_get_time(&now); +#if 0 /* FIX */ + if (wpa_s->mlme.state == IEEE80211_IBSS_JOINED && + !ieee80211_sta_active_ibss(wpa_s)) { + ieee80211_reschedule_timer(wpa_s, + IEEE80211_IBSS_MERGE_INTERVAL); + } else if (time_after(jiffies, wpa_s->mlme.last_scan_completed + + IEEE80211_SCAN_INTERVAL)) { + wpa_printf(MSG_DEBUG, "MLME: Trigger new scan to find an IBSS " + "to join"); + return ieee80211_sta_req_scan(wpa_s->mlme.ssid, + wpa_s->mlme.ssid_len); + } else if (wpa_s->mlme.state != IEEE80211_IBSS_JOINED) { + int interval = IEEE80211_SCAN_INTERVAL; + + if (time_after(jiffies, wpa_s->mlme.ibss_join_req + + IEEE80211_IBSS_JOIN_TIMEOUT)) { + if (wpa_s->mlme.create_ibss && + ieee80211_ibss_allowed(wpa_s)) + return ieee80211_sta_create_ibss(wpa_s); + if (wpa_s->mlme.create_ibss) { + wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed " + "on the configured channel %d " + "(%d MHz)", + local->conf.channel, + local->conf.freq); + } + + /* No IBSS found - decrease scan interval and continue + * scanning. */ + interval = IEEE80211_SCAN_INTERVAL_SLOW; + } + + wpa_s->mlme.state = IEEE80211_IBSS_SEARCH; + ieee80211_reschedule_timer(wpa_s, interval); + return 0; + } +#endif + + return 0; +} + + +int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid, + size_t *len) +{ + os_memcpy(ssid, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); + *len = wpa_s->mlme.ssid_len; + return 0; +} + + +int ieee80211_sta_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params) +{ + struct ieee80211_sta_bss *bss; + int bssid_changed; + + wpa_s->mlme.bssid_set = 0; + wpa_s->mlme.freq = params->freq; + if (params->bssid) { + bssid_changed = os_memcmp(wpa_s->bssid, params->bssid, + ETH_ALEN); + os_memcpy(wpa_s->bssid, params->bssid, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); + + if (!is_zero_ether_addr(params->bssid)) + wpa_s->mlme.bssid_set = 1; + bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); + if (bss) { + wpa_s->mlme.phymode = bss->hw_mode; + wpa_s->mlme.channel = bss->channel; + wpa_s->mlme.freq = bss->freq; + } + } + +#if 0 /* FIX */ + /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is + * not defined. */ + if (local->hw->conf_tx) { + struct ieee80211_tx_queue_params qparam; + int i; + + os_memset(&qparam, 0, sizeof(qparam)); + /* TODO: are these ok defaults for all hw_modes? */ + qparam.aifs = 2; + qparam.cw_min = + local->conf.phymode == MODE_IEEE80211B ? 31 : 15; + qparam.cw_max = 1023; + qparam.burst_time = 0; + for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++) + { + local->hw->conf_tx(wpa_s, i + IEEE80211_TX_QUEUE_DATA0, + &qparam); + } + /* IBSS uses different parameters for Beacon sending */ + qparam.cw_min++; + qparam.cw_min *= 2; + qparam.cw_min--; + local->hw->conf_tx(wpa_s, IEEE80211_TX_QUEUE_BEACON, &qparam); + } +#endif + + if (wpa_s->mlme.ssid_len != params->ssid_len || + os_memcmp(wpa_s->mlme.ssid, params->ssid, params->ssid_len) != 0) + wpa_s->mlme.prev_bssid_set = 0; + os_memcpy(wpa_s->mlme.ssid, params->ssid, params->ssid_len); + os_memset(wpa_s->mlme.ssid + params->ssid_len, 0, + MAX_SSID_LEN - params->ssid_len); + wpa_s->mlme.ssid_len = params->ssid_len; + wpa_s->mlme.ssid_set = 1; + + os_free(wpa_s->mlme.extra_ie); + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + wpa_s->mlme.extra_ie = NULL; + wpa_s->mlme.extra_ie_len = 0; + } else { + wpa_s->mlme.extra_ie = os_malloc(params->wpa_ie_len); + if (wpa_s->mlme.extra_ie == NULL) { + wpa_s->mlme.extra_ie_len = 0; + return -1; + } + os_memcpy(wpa_s->mlme.extra_ie, params->wpa_ie, + params->wpa_ie_len); + wpa_s->mlme.extra_ie_len = params->wpa_ie_len; + } + + wpa_s->mlme.key_mgmt = params->key_mgmt_suite; + + ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode, + wpa_s->mlme.channel, wpa_s->mlme.freq); + + if (params->mode == WPAS_MODE_IBSS && !wpa_s->mlme.bssid_set) { + os_get_time(&wpa_s->mlme.ibss_join_req); + wpa_s->mlme.state = IEEE80211_IBSS_SEARCH; + return ieee80211_sta_find_ibss(wpa_s); + } + + if (wpa_s->mlme.bssid_set) + ieee80211_sta_new_auth(wpa_s); + + return 0; +} + + +static void ieee80211_sta_save_oper_chan(struct wpa_supplicant *wpa_s) +{ + wpa_s->mlme.scan_oper_channel = wpa_s->mlme.channel; + wpa_s->mlme.scan_oper_freq = wpa_s->mlme.freq; + wpa_s->mlme.scan_oper_phymode = wpa_s->mlme.phymode; +} + + +static int ieee80211_sta_restore_oper_chan(struct wpa_supplicant *wpa_s) +{ + wpa_s->mlme.channel = wpa_s->mlme.scan_oper_channel; + wpa_s->mlme.freq = wpa_s->mlme.scan_oper_freq; + wpa_s->mlme.phymode = wpa_s->mlme.scan_oper_phymode; + if (wpa_s->mlme.freq == 0) + return 0; + return ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode, + wpa_s->mlme.channel, + wpa_s->mlme.freq); +} + + +static int ieee80211_active_scan(struct wpa_supplicant *wpa_s) +{ + size_t m; + int c; + + for (m = 0; m < wpa_s->mlme.num_modes; m++) { + struct hostapd_hw_modes *mode = &wpa_s->mlme.modes[m]; + if ((int) mode->mode != (int) wpa_s->mlme.phymode) + continue; + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + chan->chan == wpa_s->mlme.channel) { + if (!(chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN)) + return 1; + break; + } + } + } + + return 0; +} + + +static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int skip = 0; + int timeout = 0; + struct wpa_ssid *ssid = wpa_s->current_ssid; + int adhoc; + + if (!wpa_s->mlme.sta_scanning || wpa_s->mlme.modes == NULL) + return; + + adhoc = ssid && ssid->mode == 1; + + switch (wpa_s->mlme.scan_state) { + case SCAN_SET_CHANNEL: + mode = &wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx]; + if (wpa_s->mlme.scan_hw_mode_idx >= + (int) wpa_s->mlme.num_modes || + (wpa_s->mlme.scan_hw_mode_idx + 1 == + (int) wpa_s->mlme.num_modes + && wpa_s->mlme.scan_channel_idx >= mode->num_channels)) { + if (ieee80211_sta_restore_oper_chan(wpa_s)) { + wpa_printf(MSG_DEBUG, "MLME: failed to " + "restore operational channel after " + "scan"); + } + wpa_printf(MSG_DEBUG, "MLME: scan completed"); + wpa_s->mlme.sta_scanning = 0; + os_get_time(&wpa_s->mlme.last_scan_completed); + wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL); + if (adhoc) { + if (!wpa_s->mlme.bssid_set || + (wpa_s->mlme.state == + IEEE80211_IBSS_JOINED && + !ieee80211_sta_active_ibss(wpa_s))) + ieee80211_sta_find_ibss(wpa_s); + } + return; + } + skip = !(wpa_s->mlme.hw_modes & (1 << mode->mode)); + chan = &mode->channels[wpa_s->mlme.scan_channel_idx]; + if ((chan->flag & HOSTAPD_CHAN_DISABLED) || + (adhoc && (chan->flag & HOSTAPD_CHAN_NO_IBSS)) || + (wpa_s->mlme.hw_modes & (1 << HOSTAPD_MODE_IEEE80211G) && + mode->mode == HOSTAPD_MODE_IEEE80211B && + wpa_s->mlme.scan_skip_11b)) + skip = 1; + if (!skip && wpa_s->mlme.scan_freqs) { + int i, found = 0; + for (i = 0; wpa_s->mlme.scan_freqs[i]; i++) { + if (wpa_s->mlme.scan_freqs[i] == chan->freq) { + found = 1; + break; + } + } + if (!found) + skip = 1; + } + + if (!skip) { + wpa_printf(MSG_MSGDUMP, + "MLME: scan channel %d (%d MHz)", + chan->chan, chan->freq); + + wpa_s->mlme.channel = chan->chan; + wpa_s->mlme.freq = chan->freq; + wpa_s->mlme.phymode = mode->mode; + if (ieee80211_sta_set_channel(wpa_s, mode->mode, + chan->chan, chan->freq)) + { + wpa_printf(MSG_DEBUG, "MLME: failed to set " + "channel %d (%d MHz) for scan", + chan->chan, chan->freq); + skip = 1; + } + } + + wpa_s->mlme.scan_channel_idx++; + if (wpa_s->mlme.scan_channel_idx >= + wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx]. + num_channels) { + wpa_s->mlme.scan_hw_mode_idx++; + wpa_s->mlme.scan_channel_idx = 0; + } + + if (skip) { + timeout = 0; + break; + } + + timeout = IEEE80211_PROBE_DELAY; + wpa_s->mlme.scan_state = SCAN_SEND_PROBE; + break; + case SCAN_SEND_PROBE: + if (ieee80211_active_scan(wpa_s)) { + ieee80211_send_probe_req(wpa_s, NULL, + wpa_s->mlme.scan_ssid, + wpa_s->mlme.scan_ssid_len); + timeout = IEEE80211_CHANNEL_TIME; + } else { + timeout = IEEE80211_PASSIVE_CHANNEL_TIME; + } + wpa_s->mlme.scan_state = SCAN_SET_CHANNEL; + break; + } + + eloop_register_timeout(timeout / 1000, 1000 * (timeout % 1000), + ieee80211_sta_scan_timer, wpa_s, NULL); +} + + +int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; + + if (ssid_len > MAX_SSID_LEN) + return -1; + + /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) + * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS + * BSSID: MACAddress + * SSID + * ScanType: ACTIVE, PASSIVE + * ProbeDelay: delay (in microseconds) to be used prior to transmitting + * a Probe frame during active scanning + * ChannelList + * MinChannelTime (>= ProbeDelay), in TU + * MaxChannelTime: (>= MinChannelTime), in TU + */ + + /* MLME-SCAN.confirm + * BSSDescriptionSet + * ResultCode: SUCCESS, INVALID_PARAMETERS + */ + + /* TODO: if assoc, move to power save mode for the duration of the + * scan */ + + if (wpa_s->mlme.sta_scanning) + return -1; + + wpa_printf(MSG_DEBUG, "MLME: starting scan"); + + ieee80211_sta_set_probe_req_ie(wpa_s, params->extra_ies, + params->extra_ies_len); + + os_free(wpa_s->mlme.scan_freqs); + if (params->freqs) { + int i; + for (i = 0; params->freqs[i]; i++) + ; + wpa_s->mlme.scan_freqs = os_malloc((i + 1) * sizeof(int)); + if (wpa_s->mlme.scan_freqs) + os_memcpy(wpa_s->mlme.scan_freqs, params->freqs, + (i + 1) * sizeof(int)); + } else + wpa_s->mlme.scan_freqs = NULL; + + ieee80211_sta_save_oper_chan(wpa_s); + + wpa_s->mlme.sta_scanning = 1; + /* TODO: stop TX queue? */ + + if (ssid) { + wpa_s->mlme.scan_ssid_len = ssid_len; + os_memcpy(wpa_s->mlme.scan_ssid, ssid, ssid_len); + } else + wpa_s->mlme.scan_ssid_len = 0; + wpa_s->mlme.scan_skip_11b = 1; /* FIX: clear this is 11g is not + * supported */ + wpa_s->mlme.scan_state = SCAN_SET_CHANNEL; + wpa_s->mlme.scan_hw_mode_idx = 0; + wpa_s->mlme.scan_channel_idx = 0; + eloop_register_timeout(0, 1, ieee80211_sta_scan_timer, wpa_s, NULL); + + return 0; +} + + +struct wpa_scan_results * +ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s) +{ + size_t ap_num = 0; + struct wpa_scan_results *res; + struct wpa_scan_res *r; + struct ieee80211_sta_bss *bss; + + res = os_zalloc(sizeof(*res)); + for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) + ap_num++; + res->res = os_zalloc(ap_num * sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(res); + return NULL; + } + + for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) { + r = os_zalloc(sizeof(*r) + bss->ie_len); + if (r == NULL) + break; + os_memcpy(r->bssid, bss->bssid, ETH_ALEN); + r->freq = bss->freq; + r->beacon_int = bss->beacon_int; + r->caps = bss->capability; + r->level = bss->rssi; + r->tsf = bss->timestamp; + if (bss->ie) { + r->ie_len = bss->ie_len; + os_memcpy(r + 1, bss->ie, bss->ie_len); + } + + res->res[res->num++] = r; + } + + return res; +} + + +#if 0 /* FIX */ +struct sta_info * ieee80211_ibss_add_sta(struct wpa_supplicant *wpa_s, + struct sk_buff *skb, u8 *bssid, + u8 *addr) +{ + struct ieee80211_local *local = dev->priv; + struct list_head *ptr; + struct sta_info *sta; + struct wpa_supplicant *sta_dev = NULL; + + /* TODO: Could consider removing the least recently used entry and + * allow new one to be added. */ + if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { + if (net_ratelimit()) { + wpa_printf(MSG_DEBUG, "MLME: No room for a new IBSS " + "STA entry " MACSTR, MAC2STR(addr)); + } + return NULL; + } + + spin_lock_bh(&local->sub_if_lock); + list_for_each(ptr, &local->sub_if_list) { + sdata = list_entry(ptr, struct ieee80211_sub_if_data, list); + if (sdata->type == IEEE80211_SUB_IF_TYPE_STA && + os_memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { + sta_dev = sdata->dev; + break; + } + } + spin_unlock_bh(&local->sub_if_lock); + + if (sta_dev == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "MLME: Adding new IBSS station " MACSTR + " (dev=%s)", MAC2STR(addr), sta_dev->name); + + sta = sta_info_add(wpa_s, addr); + if (sta == NULL) { + return NULL; + } + + sta->dev = sta_dev; + sta->supp_rates = wpa_s->mlme.supp_rates_bits; + + rate_control_rate_init(local, sta); + + return sta; /* caller will call sta_info_release() */ +} +#endif + + +int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason) +{ + wpa_printf(MSG_DEBUG, "MLME: deauthenticate(reason=%d)", reason); + + ieee80211_send_deauth(wpa_s, reason); + ieee80211_set_associated(wpa_s, 0); + return 0; +} + + +int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason) +{ + wpa_printf(MSG_DEBUG, "MLME: disassociate(reason=%d)", reason); + + if (!wpa_s->mlme.associated) + return -1; + + ieee80211_send_disassoc(wpa_s, reason); + ieee80211_set_associated(wpa_s, 0); + return 0; +} + + +void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt; + u16 fc; + const u8 *pos; + + /* wpa_hexdump(MSG_MSGDUMP, "MLME: Received frame", buf, len); */ + + if (wpa_s->mlme.sta_scanning) { + ieee80211_sta_rx_scan(wpa_s, buf, len, rx_status); + return; + } + + if (len < 24) + return; + + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) + ieee80211_sta_rx_mgmt(wpa_s, buf, len, rx_status); + else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) != + WLAN_FC_FROMDS) + return; + /* mgmt->sa is actually BSSID for FromDS data frames */ + if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + /* Skip IEEE 802.11 and LLC headers */ + pos = buf + 24 + 6; + if (WPA_GET_BE16(pos) != ETH_P_EAPOL) + return; + pos += 2; + /* mgmt->bssid is actually BSSID for SA data frames */ + wpa_supplicant_rx_eapol(wpa_s, mgmt->bssid, + pos, buf + len - pos); + } +} + + +int ieee80211_sta_init(struct wpa_supplicant *wpa_s) +{ + u16 num_modes, flags; + + wpa_s->mlme.modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, + &flags); + if (wpa_s->mlme.modes == NULL) { + wpa_printf(MSG_ERROR, "MLME: Failed to read supported " + "channels and rates from the driver"); + return -1; + } + + wpa_s->mlme.num_modes = num_modes; + + wpa_s->mlme.hw_modes = 1 << HOSTAPD_MODE_IEEE80211A; + wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211B; + wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211G; + + wpa_s->mlme.wmm_enabled = 1; + + return 0; +} + + +void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL); + eloop_cancel_timeout(ieee80211_sta_scan_timer, wpa_s, NULL); + os_free(wpa_s->mlme.extra_ie); + wpa_s->mlme.extra_ie = NULL; + os_free(wpa_s->mlme.extra_probe_ie); + wpa_s->mlme.extra_probe_ie = NULL; + os_free(wpa_s->mlme.assocreq_ies); + wpa_s->mlme.assocreq_ies = NULL; + os_free(wpa_s->mlme.assocresp_ies); + wpa_s->mlme.assocresp_ies = NULL; + ieee80211_bss_list_deinit(wpa_s); + ieee80211_sta_free_hw_features(wpa_s->mlme.modes, + wpa_s->mlme.num_modes); +#ifdef CONFIG_IEEE80211R + os_free(wpa_s->mlme.ft_ies); + wpa_s->mlme.ft_ies = NULL; + wpa_s->mlme.ft_ies_len = 0; +#endif /* CONFIG_IEEE80211R */ + + os_free(wpa_s->mlme.scan_freqs); + wpa_s->mlme.scan_freqs = NULL; +} + + +#ifdef CONFIG_IEEE80211R + +int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len) +{ + if (md == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Clear FT mobility domain"); + os_memset(wpa_s->mlme.current_md, 0, MOBILITY_DOMAIN_ID_LEN); + } else { + wpa_printf(MSG_DEBUG, "MLME: Update FT IEs for MD " MACSTR, + MAC2STR(md)); + os_memcpy(wpa_s->mlme.current_md, md, MOBILITY_DOMAIN_ID_LEN); + } + + wpa_hexdump(MSG_DEBUG, "MLME: FT IEs", ies, ies_len); + os_free(wpa_s->mlme.ft_ies); + wpa_s->mlme.ft_ies = os_malloc(ies_len); + if (wpa_s->mlme.ft_ies == NULL) + return -1; + os_memcpy(wpa_s->mlme.ft_ies, ies, ies_len); + wpa_s->mlme.ft_ies_len = ies_len; + + return 0; +} + + +int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + u8 *buf; + size_t len; + struct ieee80211_mgmt *mgmt; + int res; + + /* + * Action frame payload: + * Category[1] = 6 (Fast BSS Transition) + * Action[1] = 1 (Fast BSS Transition Request) + * STA Address + * Target AP Address + * FT IEs + */ + + buf = os_zalloc(sizeof(*mgmt) + ies_len); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "FT action frame"); + return -1; + } + + mgmt = (struct ieee80211_mgmt *) buf; + len = 24; + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_FT; + mgmt->u.action.u.ft_action_req.action = action; + os_memcpy(mgmt->u.action.u.ft_action_req.sta_addr, wpa_s->own_addr, + ETH_ALEN); + os_memcpy(mgmt->u.action.u.ft_action_req.target_ap_addr, target_ap, + ETH_ALEN); + os_memcpy(mgmt->u.action.u.ft_action_req.variable, ies, ies_len); + len += 1 + sizeof(mgmt->u.action.u.ft_action_req) + ies_len; + + wpa_printf(MSG_DEBUG, "MLME: Send FT Action Frame: Action=%d " + "Target AP=" MACSTR " body_len=%lu", + action, MAC2STR(target_ap), (unsigned long) ies_len); + + res = ieee80211_sta_tx(wpa_s, buf, len); + os_free(buf); + + return res; +} + +#endif /* CONFIG_IEEE80211R */ + + +static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + os_free(wpa_s->mlme.extra_probe_ie); + wpa_s->mlme.extra_probe_ie = NULL; + wpa_s->mlme.extra_probe_ie_len = 0; + + if (ies == NULL) + return 0; + + wpa_s->mlme.extra_probe_ie = os_malloc(ies_len); + if (wpa_s->mlme.extra_probe_ie == NULL) + return -1; + + os_memcpy(wpa_s->mlme.extra_probe_ie, ies, ies_len); + wpa_s->mlme.extra_probe_ie_len = ies_len; + + return 0; +} diff --git a/wpa_supplicant/mlme.h b/wpa_supplicant/mlme.h new file mode 100644 index 0000000..fe3c6e4 --- /dev/null +++ b/wpa_supplicant/mlme.h @@ -0,0 +1,121 @@ +/* + * WPA Supplicant - Client mode MLME + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 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. + */ + +#ifndef MLME_H +#define MLME_H + +struct wpa_supplicant; + +struct ieee80211_rx_status { + int freq; + int channel; + int ssi; +}; + +#ifdef CONFIG_CLIENT_MLME + +int ieee80211_sta_init(struct wpa_supplicant *wpa_s); +void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s); +int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params); +int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason); +int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason); +int ieee80211_sta_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params); +int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid, + size_t *len); +void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status); +struct wpa_scan_results * +ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s); +int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len); +int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len); + +#else /* CONFIG_CLIENT_MLME */ + +static inline int ieee80211_sta_init(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + return -1; +} + +static inline int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, + u16 reason) +{ + return -1; +} + +static inline int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, + u16 reason) +{ + return -1; +} + +static inline int +ieee80211_sta_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params) +{ + return -1; +} + +static inline int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, + u8 *ssid, size_t *len) +{ + return -1; +} + +static inline void +ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, + struct ieee80211_rx_status *rx_status) +{ +} + +static inline struct wpa_scan_results * +ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s) +{ + return NULL; +} + +static inline int +ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len) +{ + return -1; +} + +static inline int +ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + return -1; +} + +#endif /* CONFIG_CLIENT_MLME */ + +#endif /* MLME_H */ diff --git a/wpa_supplicant/nmake.mak b/wpa_supplicant/nmake.mak new file mode 100644 index 0000000..80e0ac8 --- /dev/null +++ b/wpa_supplicant/nmake.mak @@ -0,0 +1,240 @@ +# Makefile for Microsoft nmake to build wpa_supplicant + +# This can be run in Visual Studio 2005 Command Prompt + +# Note: Make sure that cl.exe is configured to include Platform SDK +# include and lib directories (vsvars32.bat) + +all: wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe wpasvc.exe win_if_list.exe + +# Root directory for WinPcap developer's pack +# (http://www.winpcap.org/install/bin/WpdPack_3_1.zip) +WINPCAPDIR=C:\dev\WpdPack + +# Root directory for OpenSSL +# (http://www.openssl.org/source/openssl-0.9.8a.tar.gz) +# Build and installed following instructions in INSTALL.W32 +# Note: If EAP-FAST is included in the build, OpenSSL needs to be patched to +# support it (openssl-tls-extensions.patch) +# Alternatively, see README-Windows.txt for information about binary +# installation package for OpenSSL. +OPENSSLDIR=C:\dev\openssl + +CC = cl +OBJDIR = objs + +CFLAGS = /DCONFIG_NATIVE_WINDOWS +CFLAGS = $(CFLAGS) /DCONFIG_NDIS_EVENTS_INTEGRATED +CFLAGS = $(CFLAGS) /DCONFIG_ANSI_C_EXTRA +CFLAGS = $(CFLAGS) /DCONFIG_WINPCAP +CFLAGS = $(CFLAGS) /DIEEE8021X_EAPOL +CFLAGS = $(CFLAGS) /DPKCS12_FUNCS +CFLAGS = $(CFLAGS) /DEAP_MD5 +CFLAGS = $(CFLAGS) /DEAP_TLS +CFLAGS = $(CFLAGS) /DEAP_MSCHAPv2 +CFLAGS = $(CFLAGS) /DEAP_PEAP +CFLAGS = $(CFLAGS) /DEAP_TTLS +CFLAGS = $(CFLAGS) /DEAP_GTC +CFLAGS = $(CFLAGS) /DEAP_OTP +CFLAGS = $(CFLAGS) /DEAP_SIM +CFLAGS = $(CFLAGS) /DEAP_LEAP +CFLAGS = $(CFLAGS) /DEAP_PSK +CFLAGS = $(CFLAGS) /DEAP_AKA +#CFLAGS = $(CFLAGS) /DEAP_FAST +CFLAGS = $(CFLAGS) /DEAP_PAX +CFLAGS = $(CFLAGS) /DEAP_TNC +CFLAGS = $(CFLAGS) /DPCSC_FUNCS +CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE +CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE_NAMED_PIPE +CFLAGS = $(CFLAGS) /DCONFIG_DRIVER_NDIS +CFLAGS = $(CFLAGS) /I..\src /I..\src\utils +CFLAGS = $(CFLAGS) /I. +CFLAGS = $(CFLAGS) /DWIN32 +CFLAGS = $(CFLAGS) /Fo$(OBJDIR)\\ /c +CFLAGS = $(CFLAGS) /W3 + +#CFLAGS = $(CFLAGS) /WX + +# VS 2005 complains about lot of deprecated string functions; let's ignore them +# at least for now since snprintf and strncpy can be used in a safe way +CFLAGS = $(CFLAGS) /D_CRT_SECURE_NO_DEPRECATE + +OBJS = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\eloop_win.obj \ + $(OBJDIR)\sha1.obj \ + $(OBJDIR)\sha1-tlsprf.obj \ + $(OBJDIR)\sha1-pbkdf2.obj \ + $(OBJDIR)\md5.obj \ + $(OBJDIR)\aes-cbc.obj \ + $(OBJDIR)\aes-ctr.obj \ + $(OBJDIR)\aes-eax.obj \ + $(OBJDIR)\aes-encblock.obj \ + $(OBJDIR)\aes-omac1.obj \ + $(OBJDIR)\aes-unwrap.obj \ + $(OBJDIR)\aes-wrap.obj \ + $(OBJDIR)\common.obj \ + $(OBJDIR)\wpa_debug.obj \ + $(OBJDIR)\wpabuf.obj \ + $(OBJDIR)\wpa_supplicant.obj \ + $(OBJDIR)\wpa.obj \ + $(OBJDIR)\wpa_common.obj \ + $(OBJDIR)\wpa_ie.obj \ + $(OBJDIR)\preauth.obj \ + $(OBJDIR)\pmksa_cache.obj \ + $(OBJDIR)\eapol_supp_sm.obj \ + $(OBJDIR)\eap.obj \ + $(OBJDIR)\eap_common.obj \ + $(OBJDIR)\chap.obj \ + $(OBJDIR)\eap_methods.obj \ + $(OBJDIR)\eap_md5.obj \ + $(OBJDIR)\eap_tls.obj \ + $(OBJDIR)\eap_tls_common.obj \ + $(OBJDIR)\eap_mschapv2.obj \ + $(OBJDIR)\mschapv2.obj \ + $(OBJDIR)\eap_peap.obj \ + $(OBJDIR)\eap_peap_common.obj \ + $(OBJDIR)\eap_ttls.obj \ + $(OBJDIR)\eap_gtc.obj \ + $(OBJDIR)\eap_otp.obj \ + $(OBJDIR)\eap_leap.obj \ + $(OBJDIR)\eap_sim.obj \ + $(OBJDIR)\eap_sim_common.obj \ + $(OBJDIR)\eap_aka.obj \ + $(OBJDIR)\eap_pax.obj \ + $(OBJDIR)\eap_pax_common.obj \ + $(OBJDIR)\eap_psk.obj \ + $(OBJDIR)\eap_psk_common.obj \ + $(OBJDIR)\eap_tnc.obj \ + $(OBJDIR)\tncc.obj \ + $(OBJDIR)\base64.obj \ + $(OBJDIR)\ctrl_iface.obj \ + $(OBJDIR)\ctrl_iface_named_pipe.obj \ + $(OBJDIR)\driver_ndis.obj \ + $(OBJDIR)\driver_ndis_.obj \ + $(OBJDIR)\scan_helpers.obj \ + $(OBJDIR)\events.obj \ + $(OBJDIR)\blacklist.obj \ + $(OBJDIR)\scan.obj \ + $(OBJDIR)\wpas_glue.obj \ + $(OBJDIR)\eap_register.obj \ + $(OBJDIR)\config.obj \ + $(OBJDIR)\l2_packet_winpcap.obj \ + $(OBJDIR)\tls_openssl.obj \ + $(OBJDIR)\ms_funcs.obj \ + $(OBJDIR)\crypto_openssl.obj \ + $(OBJDIR)\fips_prf_openssl.obj \ + $(OBJDIR)\pcsc_funcs.obj \ + $(OBJDIR)\notify.obj \ + $(OBJDIR)\ndis_events.obj + +# OBJS = $(OBJS) $(OBJDIR)\eap_fast.obj + +OBJS_t = $(OBJS) \ + $(OBJDIR)\eapol_test.obj \ + $(OBJDIR)\radius.obj \ + $(OBJDIR)\radius_client.obj \ + $(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj + +OBJS_t2 = $(OBJS) \ + $(OBJDIR)\preauth_test.obj \ + $(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj + +OBJS2 = $(OBJDIR)\drivers.obj \ + $(OBJDIR)\config_file.obj \ + $(OBJS2) $(OBJDIR)\main.obj + +OBJS3 = $(OBJDIR)\drivers.obj \ + $(OBJDIR)\config_winreg.obj \ + $(OBJS3) $(OBJDIR)\main_winsvc.obj + +OBJS_c = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\wpa_cli.obj \ + $(OBJDIR)\wpa_ctrl.obj \ + $(OBJDIR)\common.obj + +OBJS_p = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\common.obj \ + $(OBJDIR)\wpa_debug.obj \ + $(OBJDIR)\wpabuf.obj \ + $(OBJDIR)\sha1.obj \ + $(OBJDIR)\md5.obj \ + $(OBJDIR)\crypto_openssl.obj \ + $(OBJDIR)\sha1-pbkdf2.obj \ + $(OBJDIR)\wpa_passphrase.obj + +LIBS = wbemuuid.lib libcmt.lib kernel32.lib uuid.lib ole32.lib oleaut32.lib \ + ws2_32.lib Advapi32.lib Crypt32.lib Winscard.lib \ + Packet.lib wpcap.lib \ + libeay32.lib ssleay32.lib +# If using Win32 OpenSSL binary installation from Shining Light Productions, +# replace the last line with this for dynamic libraries +# libeay32MT.lib ssleay32MT.lib +# and this for static libraries +# libeay32MT.lib ssleay32MT.lib Gdi32.lib User32.lib + +CFLAGS = $(CFLAGS) /I"$(WINPCAPDIR)/Include" /I"$(OPENSSLDIR)\include" +LFLAGS = /libpath:"$(WINPCAPDIR)\Lib" /libpath:"$(OPENSSLDIR)\lib" + +wpa_supplicant.exe: $(OBJDIR) $(OBJS) $(OBJS2) + link.exe /out:wpa_supplicant.exe $(LFLAGS) $(OBJS) $(OBJS2) $(LIBS) + +wpasvc.exe: $(OBJDIR) $(OBJS) $(OBJS3) + link.exe /out:wpasvc.exe $(LFLAGS) $(OBJS) $(OBJS3) $(LIBS) + +wpa_cli.exe: $(OBJDIR) $(OBJS_c) + link.exe /out:wpa_cli.exe $(LFLAGS) $(OBJS_c) $(LIBS) + +wpa_passphrase.exe: $(OBJDIR) $(OBJS_p) + link.exe /out:wpa_passphrase.exe $(LFLAGS) $(OBJS_p) $(LIBS) + +eapol_test.exe: $(OBJDIR) $(OBJS_t) + link.exe /out:eapol_test.exe $(LFLAGS) $(OBJS_t) $(LIBS) + +preauth_test.exe: $(OBJDIR) $(OBJS_t2) + link.exe /out:preauth_test.exe $(LFLAGS) $(OBJS_t2) $(LIBS) + +win_if_list.exe: $(OBJDIR) $(OBJDIR)\win_if_list.obj + link.exe /out:win_if_list.exe $(LFLAGS) $(OBJDIR)\win_if_list.obj $(LIBS) + + +{..\src\utils}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\common}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\rsn_supp}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eapol_supp}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\crypto}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eap_peer}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eap_common}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\drivers}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\l2_packet}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{.\}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{.\}.cpp{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +$(OBJDIR): + if not exist "$(OBJDIR)" mkdir "$(OBJDIR)" + +clean: + erase $(OBJDIR)\*.obj wpa_supplicant.exe diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c new file mode 100644 index 0000000..2053c3d --- /dev/null +++ b/wpa_supplicant/notify.c @@ -0,0 +1,440 @@ +/* + * wpa_supplicant - Event notifications + * Copyright (c) 2009-2010, 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/wpa_ctrl.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "wps_supplicant.h" +#include "dbus/dbus_common.h" +#include "dbus/dbus_old.h" +#include "dbus/dbus_new.h" +#include "driver_i.h" +#include "scan.h" +#include "p2p_supplicant.h" +#include "sme.h" +#include "notify.h" + +int wpas_notify_supplicant_initialized(struct wpa_global *global) +{ +#ifdef CONFIG_DBUS + if (global->params.dbus_ctrl_interface) { + global->dbus = wpas_dbus_init(global); + if (global->dbus == NULL) + return -1; + } +#endif /* CONFIG_DBUS */ + + return 0; +} + + +void wpas_notify_supplicant_deinitialized(struct wpa_global *global) +{ +#ifdef CONFIG_DBUS + if (global->dbus) + wpas_dbus_deinit(global->dbus); +#endif /* CONFIG_DBUS */ +} + + +int wpas_notify_iface_added(struct wpa_supplicant *wpa_s) +{ + if (wpas_dbus_register_iface(wpa_s)) + return -1; + + if (wpas_dbus_register_interface(wpa_s)) + return -1; + + return 0; +} + + +void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s) +{ + /* unregister interface in old DBus ctrl iface */ + wpas_dbus_unregister_iface(wpa_s); + + /* unregister interface in new DBus ctrl iface */ + wpas_dbus_unregister_interface(wpa_s); +} + + +void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, + enum wpa_states new_state, + enum wpa_states old_state) +{ + /* notify the old DBus API */ + wpa_supplicant_dbus_notify_state_change(wpa_s, new_state, + old_state); + + /* 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 (new_state < WPA_ASSOCIATED) + wpas_p2p_notif_disconnected(wpa_s); +#endif /* CONFIG_P2P */ + + sme_state_changed(wpa_s); + +#ifdef ANDROID + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE + "id=%d state=%d BSSID=" MACSTR, + wpa_s->current_ssid ? wpa_s->current_ssid->id : -1, + new_state, MAC2STR(wpa_s->pending_bssid)); +#endif /* ANDROID */ +} + + +void wpas_notify_network_changed(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK); +} + + +void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN); +} + + +void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS); +} + + +void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE); +} + + +void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + wpas_dbus_signal_network_enabled_changed(wpa_s, ssid); +} + + +void wpas_notify_network_selected(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + wpas_dbus_signal_network_selected(wpa_s, ssid->id); +} + + +void wpas_notify_scanning(struct wpa_supplicant *wpa_s) +{ + /* notify the old DBus API */ + wpa_supplicant_dbus_notify_scanning(wpa_s); + + /* notify the new DBus API */ + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCANNING); +} + + +void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success) +{ + wpas_dbus_signal_scan_done(wpa_s, success); +} + + +void wpas_notify_scan_results(struct wpa_supplicant *wpa_s) +{ + /* notify the old DBus API */ + wpa_supplicant_dbus_notify_scan_results(wpa_s); + + wpas_wps_notify_scan_results(wpa_s); +} + + +void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ +#ifdef CONFIG_WPS + /* notify the old DBus API */ + wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred); + /* notify the new DBus API */ + wpas_dbus_signal_wps_cred(wpa_s, cred); +#endif /* CONFIG_WPS */ +} + + +void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s, + struct wps_event_m2d *m2d) +{ +#ifdef CONFIG_WPS + wpas_dbus_signal_wps_event_m2d(wpa_s, m2d); +#endif /* CONFIG_WPS */ +} + + +void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ +#ifdef CONFIG_WPS + wpas_dbus_signal_wps_event_fail(wpa_s, fail); +#endif /* CONFIG_WPS */ +} + + +void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WPS + wpas_dbus_signal_wps_event_success(wpa_s); +#endif /* CONFIG_WPS */ +} + + +void wpas_notify_network_added(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + wpas_dbus_register_network(wpa_s, ssid); +} + + +void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + wpas_dbus_unregister_network(wpa_s, ssid->id); +} + + +void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, + u8 bssid[], unsigned int id) +{ + wpas_dbus_register_bss(wpa_s, bssid, id); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR, + id, MAC2STR(bssid)); +} + + +void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, + u8 bssid[], unsigned int id) +{ + wpas_dbus_unregister_bss(wpa_s, bssid, id); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR, + id, MAC2STR(bssid)); +} + + +void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id); +} + + +void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL, + id); +} + + +void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY, + id); +} + + +void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id); +} + + +void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id); +} + + +void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id); +} + + +void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ +} + + +void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id); +} + + +void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id); +} + + +void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name) +{ + wpas_dbus_signal_blob_added(wpa_s, name); +} + + +void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name) +{ + wpas_dbus_signal_blob_removed(wpa_s, name); +} + + +void wpas_notify_debug_level_changed(struct wpa_global *global) +{ + wpas_dbus_signal_debug_level_changed(global); +} + + +void wpas_notify_debug_timestamp_changed(struct wpa_global *global) +{ + wpas_dbus_signal_debug_timestamp_changed(global); +} + + +void wpas_notify_debug_show_keys_changed(struct wpa_global *global) +{ + wpas_dbus_signal_debug_show_keys_changed(global); +} + + +void wpas_notify_suspend(struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s; + + os_get_time(&global->suspend_time); + wpa_printf(MSG_DEBUG, "System suspend notification"); + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) + wpa_drv_suspend(wpa_s); +} + + +void wpas_notify_resume(struct wpa_global *global) +{ + struct os_time now; + int slept; + struct wpa_supplicant *wpa_s; + + if (global->suspend_time.sec == 0) + slept = -1; + else { + os_get_time(&now); + slept = now.sec - global->suspend_time.sec; + } + wpa_printf(MSG_DEBUG, "System resume notification (slept %d seconds)", + slept); + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + wpa_drv_resume(wpa_s); + if (wpa_s->wpa_state == WPA_DISCONNECTED) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + } +} + + +#ifdef CONFIG_P2P + +void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int new_device) +{ +} + + +void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ +} + + +void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + const char *role) +{ +} + + +void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 dev_passwd_id) +{ +} + + +void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s, int status) +{ +} + + +void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s, + int status, const u8 *bssid) +{ +} + + +void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s, + int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, + size_t tlvs_len) +{ +} + + +void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ +} + +#endif /* CONFIG_P2P */ + + +static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +} + + +static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +} + + +void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *mac_addr, int authorized) +{ + if (authorized) + wpas_notify_ap_sta_authorized(wpa_s, mac_addr); + else + wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr); +} diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h new file mode 100644 index 0000000..6c8fdf7 --- /dev/null +++ b/wpa_supplicant/notify.h @@ -0,0 +1,105 @@ +/* + * wpa_supplicant - Event notifications + * Copyright (c) 2009-2010, 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. + */ + +#ifndef NOTIFY_H +#define NOTIFY_H + +struct wps_credential; +struct wps_event_m2d; +struct wps_event_fail; + +int wpas_notify_supplicant_initialized(struct wpa_global *global); +void wpas_notify_supplicant_deinitialized(struct wpa_global *global); +int wpas_notify_iface_added(struct wpa_supplicant *wpa_s); +void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s); +void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, + enum wpa_states new_state, + enum wpa_states old_state); +void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); +void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s); +void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s); +void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s); +void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_notify_network_selected(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_notify_scanning(struct wpa_supplicant *wpa_s); +void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success); +void wpas_notify_scan_results(struct wpa_supplicant *wpa_s); +void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred); +void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s, + struct wps_event_m2d *m2d); +void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail); +void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s); +void wpas_notify_network_added(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, u8 bssid[], + unsigned int id); +void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, u8 bssid[], + unsigned int id); +void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s, + unsigned int id); +void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name); +void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name); + +void wpas_notify_debug_level_changed(struct wpa_global *global); +void wpas_notify_debug_timestamp_changed(struct wpa_global *global); +void wpas_notify_debug_show_keys_changed(struct wpa_global *global); +void wpas_notify_suspend(struct wpa_global *global); +void wpas_notify_resume(struct wpa_global *global); + +void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *mac_addr, int authorized); +void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int new_device); +void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s, + const u8 *dev_addr); +void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + const char *role); +void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 dev_passwd_id); +void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s, + int status); +void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s, + int status, const u8 *bssid); +void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s, + int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, + size_t tlvs_len); +void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); + +#endif /* NOTIFY_H */ diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c new file mode 100644 index 0000000..d7f7473 --- /dev/null +++ b/wpa_supplicant/p2p_supplicant.c @@ -0,0 +1,4248 @@ +/* + * wpa_supplicant - P2P + * Copyright (c) 2009-2010, Atheros Communications + * + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "wps/wps_i.h" +#include "p2p/p2p.h" +#include "ap/hostapd.h" +#include "ap/p2p_hostapd.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "ap.h" +#include "config_ssid.h" +#include "config.h" +#include "mlme.h" +#include "notify.h" +#include "scan.h" +#include "bss.h" +#include "wps_supplicant.h" +#include "p2p_supplicant.h" + + +/* + * How many times to try to scan to find the GO before giving up on join + * request. + */ +#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10 + + +static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx); +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(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); +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_scan_res_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + size_t i; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + + wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)", + (int) scan_res->num); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid, + bss->freq, bss->level, + (const u8 *) (bss + 1), + bss->ie_len) > 0) + break; + } + + p2p_scan_res_handled(wpa_s->global->p2p); +} + + +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) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_driver_scan_params params; + int ret; + struct wpabuf *wps_ie, *ies; + int social_channels[] = { 2412, 2437, 2462, 0, 0 }; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + 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; + + wpa_s->wps->dev.p2p = 1; + wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, + WPS_REQ_ENROLLEE, + num_req_dev_types, req_dev_types); + if (wps_ie == NULL) + return -1; + + ies = wpabuf_alloc(wpabuf_len(wps_ie) + 100); + if (ies == NULL) { + wpabuf_free(wps_ie); + return -1; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + p2p_scan_ie(wpa_s->global->p2p, ies); + + 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_SPECIFIC: + social_channels[0] = freq; + social_channels[1] = 0; + params.freqs = social_channels; + break; + case P2P_SCAN_SOCIAL_PLUS_ONE: + social_channels[3] = freq; + params.freqs = social_channels; + break; + } + + wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + ret = ieee80211_sta_req_scan(wpa_s, ¶ms); + else + ret = wpa_drv_scan(wpa_s, ¶ms); + + wpabuf_free(ies); + + return ret; +} + + +#ifdef CONFIG_CLIENT_MLME +static void p2p_rx_action_mlme(void *ctx, const u8 *buf, size_t len, int freq) +{ + struct wpa_supplicant *wpa_s = ctx; + const struct ieee80211_mgmt *mgmt; + size_t hdr_len; + + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; + if (hdr_len > len) + return; + p2p_rx_action(wpa_s->global->p2p, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + &mgmt->u.action.u.vs_public_action.action, + len - hdr_len, freq); +} +#endif /* CONFIG_CLIENT_MLME */ + + +static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface) +{ + switch (p2p_group_interface) { + case P2P_GROUP_INTERFACE_PENDING: + return WPA_IF_P2P_GROUP; + case P2P_GROUP_INTERFACE_GO: + return WPA_IF_P2P_GO; + case P2P_GROUP_INTERFACE_CLIENT: + return WPA_IF_P2P_CLIENT; + } + + return WPA_IF_P2P_GROUP; +} + + +static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s, + const u8 *ssid, + size_t ssid_len, int *go) +{ + struct wpa_ssid *s; + + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled != 0 || !s->p2p_group || + s->ssid_len != ssid_len || + os_memcmp(ssid, s->ssid, ssid_len) != 0) + continue; + if (s->mode == WPAS_MODE_P2P_GO && + s != wpa_s->current_ssid) + continue; + if (go) + *go = s->mode == WPAS_MODE_P2P_GO; + return wpa_s; + } + } + + return NULL; +} + + +static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + char *gtype; + const char *reason; + + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); + + ssid = wpa_s->current_ssid; + if (ssid == NULL) { + /* + * The current SSID was not known, but there may still be a + * pending P2P group interface waiting for provisioning. + */ + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid->p2p_group && + (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION || + (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) + break; + ssid = ssid->next; + } + } + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO) + gtype = "GO"; + else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || + (ssid && ssid->mode == WPAS_MODE_INFRA)) { + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + gtype = "client"; + } else + gtype = "GO"; + if (wpa_s->cross_connect_in_use) { + wpa_s->cross_connect_in_use = 0; + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); + } + switch (wpa_s->removal_reason) { + case P2P_GROUP_REMOVAL_REQUESTED: + reason = " reason=REQUESTED"; + break; + case P2P_GROUP_REMOVAL_IDLE_TIMEOUT: + reason = " reason=IDLE"; + break; + case P2P_GROUP_REMOVAL_UNAVAILABLE: + reason = " reason=UNAVAILABLE"; + break; + default: + reason = ""; + break; + } + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s", + wpa_s->ifname, gtype, reason); + + if (ssid) + wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); + + if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { + struct wpa_global *global; + char *ifname; + enum wpa_driver_if_type type; + wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s", + wpa_s->ifname); + global = wpa_s->global; + ifname = os_strdup(wpa_s->ifname); + type = wpas_p2p_if_type(wpa_s->p2p_group_interface); + wpa_supplicant_remove_iface(wpa_s->global, wpa_s); + wpa_s = global->ifaces; + if (wpa_s && ifname) + wpa_drv_if_remove(wpa_s, type, ifname); + os_free(ifname); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network"); + if (ssid && (ssid->p2p_group || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION || + (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) { + int id = ssid->id; + if (ssid == wpa_s->current_ssid) + wpa_s->current_ssid = NULL; + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, id); + wpa_supplicant_clear_status(wpa_s); + } else { + wpa_printf(MSG_DEBUG, "P2P: Temporary group network not " + "found"); + } + wpa_supplicant_ap_deinit(wpa_s); +} + + +static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, + u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + struct wpa_bss *bss; + const u8 *bssid; + struct wpabuf *p2p; + u8 group_capab; + const u8 *addr; + + if (wpa_s->go_params) + bssid = wpa_s->go_params->peer_interface_addr; + else + bssid = wpa_s->bssid; + + bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len); + if (bss == NULL) { + u8 iface_addr[ETH_ALEN]; + if (p2p_get_interface_addr(wpa_s->global->p2p, bssid, + iface_addr) == 0) + bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len); + } + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether " + "group is persistent - BSS " MACSTR " not found", + MAC2STR(bssid)); + return 0; + } + + p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + if (p2p == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether " + "group is persistent - BSS " MACSTR + " did not include P2P IE", MAC2STR(bssid)); + wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs", + (u8 *) (bss + 1), bss->ie_len); + wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs", + ((u8 *) bss + 1) + bss->ie_len, + bss->beacon_ie_len); + return 0; + } + + group_capab = p2p_get_group_capab(p2p); + addr = p2p_get_go_dev_addr(p2p); + wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: " + "group_capab=0x%x", group_capab); + if (addr) { + os_memcpy(go_dev_addr, addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR, + MAC2STR(addr)); + } else + os_memset(go_dev_addr, 0, ETH_ALEN); + wpabuf_free(p2p); + + wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x " + "go_dev_addr=" MACSTR, + MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr)); + + return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP; +} + + +static void wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const u8 *go_dev_addr) +{ + struct wpa_ssid *s; + int changed = 0; + + wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent " + "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr)); + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled == 2 && + os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 && + s->ssid_len == ssid->ssid_len && + os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0) + break; + } + + if (s) { + wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group " + "entry"); + if (ssid->passphrase && !s->passphrase) + changed = 1; + else if (ssid->passphrase && s->passphrase && + os_strcmp(ssid->passphrase, s->passphrase) != 0) + changed = 1; + } else { + wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group " + "entry"); + changed = 1; + s = wpa_config_add_network(wpa_s->conf); + if (s == NULL) + return; + wpa_config_set_network_defaults(s); + } + + s->p2p_group = 1; + s->p2p_persistent_group = 1; + s->disabled = 2; + s->bssid_set = 1; + os_memcpy(s->bssid, go_dev_addr, ETH_ALEN); + s->mode = ssid->mode; + s->auth_alg = WPA_AUTH_ALG_OPEN; + s->key_mgmt = WPA_KEY_MGMT_PSK; + s->proto = WPA_PROTO_RSN; + s->pairwise_cipher = WPA_CIPHER_CCMP; + s->export_keys = 1; + if (ssid->passphrase) { + os_free(s->passphrase); + s->passphrase = os_strdup(ssid->passphrase); + } + if (ssid->psk_set) { + s->psk_set = 1; + os_memcpy(s->psk, ssid->psk, 32); + } + if (s->passphrase && !s->psk_set) + wpa_config_update_psk(s); + if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) { + os_free(s->ssid); + s->ssid = os_malloc(ssid->ssid_len); + } + if (s->ssid) { + s->ssid_len = ssid->ssid_len; + os_memcpy(s->ssid, ssid->ssid, s->ssid_len); + } + +#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 */ +} + + +static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, + int success) +{ + struct wpa_ssid *ssid; + const char *ssid_txt; + int client; + int persistent; + u8 go_dev_addr[ETH_ALEN]; + + /* + * This callback is likely called for the main interface. Update wpa_s + * to use the group interface if a new interface was created for the + * group. + */ + 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 (!success) { + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); + wpas_p2p_group_delete(wpa_s); + return; + } + + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS); + + ssid = wpa_s->current_ssid; + if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { + ssid->mode = WPAS_MODE_P2P_GO; + p2p_group_notif_formation_done(wpa_s->p2p_group); + wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL); + } + + 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; + os_memcpy(go_dev_addr, wpa_s->parent->own_addr, + ETH_ALEN); + } else + persistent = wpas_p2p_persistent_group(wpa_s, + go_dev_addr, + 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); + } + + wpa_s->show_group_started = 0; + if (client) { + /* + * Indicate event only after successfully completed 4-way + * handshake, i.e., when the interface is ready for data + * packets. + */ + wpa_s->show_group_started = 1; + } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) { + char psk[65]; + wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32); + wpa_msg(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(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_cross_connect_setup(wpa_s); + wpas_p2p_set_group_idle_timeout(wpa_s); + } + + if (persistent) + wpas_p2p_store_persistent_group(wpa_s->parent, ssid, + go_dev_addr); +} + + +static struct wpa_supplicant * +wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) +{ + struct wpa_supplicant *iface; + + if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) + return wpa_s; + + /* + * Try to find a group interface that matches with the source address. + */ + iface = wpa_s->global->ifaces; + while (iface) { + if (os_memcmp(wpa_s->pending_action_src, + iface->own_addr, ETH_ALEN) == 0) + break; + iface = iface->next; + } + if (iface) { + wpa_printf(MSG_DEBUG, "P2P: Use group interface %s " + "instead of interface %s for Action TX", + iface->ifname, wpa_s->ifname); + return iface; + } + + return wpa_s; +} + + +static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_supplicant *iface; + int res; + int without_roc; + + without_roc = wpa_s->pending_action_without_roc; + wpa_s->pending_action_without_roc = 0; + wpa_printf(MSG_DEBUG, "P2P: Send Action callback (without_roc=%d " + "pending_action_tx=%p)", + without_roc, wpa_s->pending_action_tx); + + if (wpa_s->pending_action_tx == NULL) + return; + + /* + * This call is likely going to be on the P2P device instance if the + * driver uses a separate interface for that purpose. However, some + * Action frames are actually sent within a P2P Group and when that is + * the case, we need to follow power saving (e.g., GO buffering the + * frame for a client in PS mode or a client following the advertised + * NoA from its GO). To make that easier for the driver, select the + * correct group interface here. + */ + iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src); + + if (wpa_s->off_channel_freq != wpa_s->pending_action_freq && + wpa_s->pending_action_freq != 0 && + wpa_s->pending_action_freq != iface->assoc_freq) { + wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX " + "waiting for another freq=%u (off_channel_freq=%u " + "assoc_freq=%u)", + wpa_s->pending_action_freq, + wpa_s->off_channel_freq, + iface->assoc_freq); + if (without_roc && wpa_s->off_channel_freq == 0) { + /* + * We may get here if wpas_send_action() found us to be + * on the correct channel, but remain-on-channel cancel + * event was received before getting here. + */ + wpa_printf(MSG_DEBUG, "P2P: Schedule " + "remain-on-channel to send Action frame"); + if (wpa_drv_remain_on_channel( + wpa_s, wpa_s->pending_action_freq, 200) < + 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to request " + "driver to remain on channel (%u " + "MHz) for Action Frame TX", + wpa_s->pending_action_freq); + } else { + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = + wpa_s->pending_action_freq; + } + } + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to " + MACSTR " using interface %s", + MAC2STR(wpa_s->pending_action_dst), iface->ifname); + res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0, + 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)); + if (res) { + wpa_printf(MSG_DEBUG, "P2P: Failed to send the pending " + "Action frame"); + /* + * Use fake TX status event to allow P2P state machine to + * continue. + */ + wpas_send_action_tx_status( + wpa_s, wpa_s->pending_action_dst, + wpabuf_head(wpa_s->pending_action_tx), + wpabuf_len(wpa_s->pending_action_tx), + P2P_SEND_ACTION_FAILED); + } +} + + +void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, + const u8 *data, size_t data_len, + enum p2p_send_action_result result) +{ + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) + return; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return; + + if (wpa_s->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - no " + "pending operation"); + return; + } + + if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - unknown " + "destination address"); + return; + } + + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + + p2p_send_action_cb(wpa_s->global->p2p, wpa_s->pending_action_freq, + wpa_s->pending_action_dst, + wpa_s->pending_action_src, + wpa_s->pending_action_bssid, + result); + + if (wpa_s->pending_pd_before_join && + (os_memcmp(wpa_s->pending_action_dst, wpa_s->pending_join_dev_addr, + ETH_ALEN) == 0 || + os_memcmp(wpa_s->pending_action_dst, + wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { + wpa_s->pending_pd_before_join = 0; + wpa_printf(MSG_DEBUG, "P2P: Starting pending " + "join-existing-group operation"); + wpas_p2p_join_start(wpa_s); + } +} + + +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; + + wpa_printf(MSG_DEBUG, "P2P: Send action frame: freq=%d dst=" MACSTR + " src=" MACSTR " bssid=" MACSTR " len=%d", + freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + (int) len); + + if (wpa_s->pending_action_tx) { + wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX " + "to " MACSTR, MAC2STR(wpa_s->pending_action_dst)); + wpabuf_free(wpa_s->pending_action_tx); + } + wpa_s->pending_action_tx = wpabuf_alloc(len); + if (wpa_s->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Failed to allocate Action frame " + "TX buffer (len=%llu)", (unsigned long long) len); + return -1; + } + wpabuf_put_data(wpa_s->pending_action_tx, buf, len); + os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN); + os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN); + os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN); + wpa_s->pending_action_freq = freq; + + if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { + struct wpa_supplicant *iface; + + 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(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)); + } + + if (freq) { + struct wpa_supplicant *tx_iface; + tx_iface = wpas_get_tx_interface(wpa_s, src); + if (tx_iface->assoc_freq == freq) { + wpa_printf(MSG_DEBUG, "P2P: Already on requested " + "channel (TX interface operating channel)"); + freq = 0; + } + } + + if (wpa_s->off_channel_freq == freq || freq == 0) { + wpa_printf(MSG_DEBUG, "P2P: Already on requested channel; " + "send Action frame immediately"); + /* TODO: Would there ever be need to extend the current + * duration on the channel? */ + wpa_s->pending_action_without_roc = 1; + eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); + eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL); + return 0; + } + wpa_s->pending_action_without_roc = 0; + + if (wpa_s->roc_waiting_drv_freq == freq) { + wpa_printf(MSG_DEBUG, "P2P: Already waiting for driver to get " + "to frequency %u MHz; continue waiting to send the " + "Action frame", freq); + return 0; + } + + wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted " + "once the driver gets to the requested channel"); + if (wait_time > wpa_s->max_remain_on_chan) + wait_time = wpa_s->max_remain_on_chan; + if (wpa_drv_remain_on_channel(wpa_s, 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; + } + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = freq; + + return 0; +} + + +static void wpas_send_action_done(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_printf(MSG_DEBUG, "P2P: Action frame sequence done notification"); + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { + if (wpa_s->action_tx_wait_time) + wpa_drv_send_action_cancel_wait(wpa_s); + wpa_s->off_channel_freq = 0; + } else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + } +} + + +static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params) +{ + if (wpa_s->go_params == NULL) { + wpa_s->go_params = os_malloc(sizeof(*params)); + if (wpa_s->go_params == NULL) + return -1; + } + os_memcpy(wpa_s->go_params, params, sizeof(*params)); + return 0; +} + + +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_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) + wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1); + else { + u16 dev_pw_id = DEV_PW_DEFAULT; + if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD) + dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED; + wpas_wps_start_pin(wpa_s, res->peer_interface_addr, + wpa_s->p2p_pin, 1, dev_pw_id); + } +} + + +static void p2p_go_configured(void *ctx, void *data) +{ + struct wpa_supplicant *wpa_s = ctx; + struct p2p_go_neg_results *params = data; + struct wpa_ssid *ssid; + + ssid = wpa_s->current_ssid; + if (ssid && ssid->mode == WPAS_MODE_P2P_GO) { + 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; + wpa_msg(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 ? params->passphrase : "", + MAC2STR(wpa_s->parent->own_addr), + params->persistent_group ? " [PERSISTENT]" : ""); + if (params->persistent_group) + wpas_p2p_store_persistent_group( + wpa_s->parent, ssid, + wpa_s->parent->own_addr); + wpas_p2p_cross_connect_setup(wpa_s); + wpas_p2p_set_group_idle_timeout(wpa_s); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning"); + if (wpa_supplicant_ap_mac_addr_filter(wpa_s, + params->peer_interface_addr)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address " + "filtering"); + return; + } + if (params->wps_method == WPS_PBC) + wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr, + NULL); + else if (wpa_s->p2p_pin[0]) + wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, + wpa_s->p2p_pin, NULL, 0); + os_free(wpa_s->go_params); + wpa_s->go_params = NULL; +} + + +static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + int group_formation) +{ + struct wpa_ssid *ssid; + + if (wpas_copy_go_neg_results(wpa_s, params) < 0) + return; + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return; + + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + ssid->p2p_group = 1; + ssid->p2p_persistent_group = params->persistent_group; + ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION : + WPAS_MODE_P2P_GO; + ssid->frequency = params->freq; + ssid->ssid = os_zalloc(params->ssid_len + 1); + if (ssid->ssid) { + os_memcpy(ssid->ssid, params->ssid, params->ssid_len); + ssid->ssid_len = params->ssid_len; + } + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_PSK; + ssid->proto = WPA_PROTO_RSN; + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + ssid->passphrase = os_strdup(params->passphrase); + + 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->connect_without_scan = 1; + wpa_s->reassociate = 1; + wpa_s->disconnected = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpas_p2p_clone_config(struct wpa_supplicant *dst, + const struct wpa_supplicant *src) +{ + struct wpa_config *d; + const struct wpa_config *s; + + d = dst->conf; + s = src->conf; + +#define C(n) if (s->n) d->n = os_strdup(s->n) + C(device_name); + C(manufacturer); + C(model_name); + C(model_number); + C(serial_number); + C(config_methods); +#undef C + + os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN); + os_memcpy(d->sec_device_type, s->sec_device_type, + sizeof(d->sec_device_type)); + d->num_sec_device_types = s->num_sec_device_types; + + d->p2p_group_idle = s->p2p_group_idle; + d->p2p_intra_bss = s->p2p_intra_bss; +} + + +static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type) +{ + char ifname[120], force_ifname[120]; + + if (wpa_s->pending_interface_name[0]) { + wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists " + "- skip creation of a new one"); + if (is_zero_ether_addr(wpa_s->pending_interface_addr)) { + wpa_printf(MSG_DEBUG, "P2P: Pending virtual address " + "unknown?! ifname='%s'", + wpa_s->pending_interface_name); + return -1; + } + return 0; + } + + os_snprintf(ifname, sizeof(ifname), "p2p-%s-%d", wpa_s->ifname, + wpa_s->p2p_group_idx); + 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); + } + force_ifname[0] = '\0'; + + wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group", + ifname); + wpa_s->p2p_group_idx++; + + wpa_s->pending_interface_type = type; + if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname, + wpa_s->pending_interface_addr, NULL) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to create new group " + "interface"); + return -1; + } + + if (force_ifname[0]) { + wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s", + force_ifname); + os_strlcpy(wpa_s->pending_interface_name, force_ifname, + sizeof(wpa_s->pending_interface_name)); + } else + os_strlcpy(wpa_s->pending_interface_name, ifname, + sizeof(wpa_s->pending_interface_name)); + wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr " + MACSTR, wpa_s->pending_interface_name, + MAC2STR(wpa_s->pending_interface_addr)); + + return 0; +} + + +static void wpas_p2p_remove_pending_group_interface( + struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->pending_interface_name[0] || + is_zero_ether_addr(wpa_s->pending_interface_addr)) + return; /* No pending virtual interface */ + + wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s", + wpa_s->pending_interface_name); + wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type, + wpa_s->pending_interface_name); + os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); + wpa_s->pending_interface_name[0] = '\0'; +} + + +static struct wpa_supplicant * +wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go) +{ + struct wpa_interface iface; + struct wpa_supplicant *group_wpa_s; + + if (!wpa_s->pending_interface_name[0]) { + wpa_printf(MSG_ERROR, "P2P: No pending group interface"); + if (!wpas_p2p_create_iface(wpa_s)) + return NULL; + /* + * Something has forced us to remove the pending interface; try + * to create a new one and hope for the best that we will get + * the same local address. + */ + if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : + WPA_IF_P2P_CLIENT) < 0) + return NULL; + } + + os_memset(&iface, 0, sizeof(iface)); + iface.ifname = wpa_s->pending_interface_name; + iface.driver = wpa_s->driver->name; + iface.ctrl_interface = wpa_s->conf->ctrl_interface; + iface.driver_param = wpa_s->conf->driver_param; + group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); + if (group_wpa_s == NULL) { + wpa_printf(MSG_ERROR, "P2P: Failed to create new " + "wpa_supplicant interface"); + return NULL; + } + wpa_s->pending_interface_name[0] = '\0'; + group_wpa_s->parent = wpa_s; + group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO : + P2P_GROUP_INTERFACE_CLIENT; + wpa_s->global->p2p_group_formation = group_wpa_s; + + wpas_p2p_clone_config(group_wpa_s, wpa_s); + + return group_wpa_s; +} + + +static void wpas_p2p_group_formation_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); + 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); +} + + +void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + } + + if (res->status) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d", + res->status); + wpas_notify_p2p_go_neg_completed(wpa_s, res->status); + wpas_p2p_remove_pending_group_interface(wpa_s); + return; + } + + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS); + wpas_notify_p2p_go_neg_completed(wpa_s, P2P_SC_SUCCESS); + + if (wpa_s->create_p2p_iface) { + struct wpa_supplicant *group_wpa_s = + wpas_p2p_init_group_interface(wpa_s, res->role_go); + if (group_wpa_s == NULL) { + wpas_p2p_remove_pending_group_interface(wpa_s); + return; + } + if (group_wpa_s != wpa_s) { + os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin, + sizeof(group_wpa_s->p2p_pin)); + group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method; + } + os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); + wpa_s->pending_interface_name[0] = '\0'; + group_wpa_s->p2p_in_provisioning = 1; + + if (res->role_go) + wpas_start_wps_go(group_wpa_s, res, 1); + else + wpas_start_wps_enrollee(group_wpa_s, res); + } else { + wpa_s->p2p_in_provisioning = 1; + wpa_s->global->p2p_group_formation = wpa_s; + + if (res->role_go) + wpas_start_wps_go(wpa_s, res, 1); + else + wpas_start_wps_enrollee(ctx, res); + } + + wpa_s->p2p_long_listen = 0; + eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); + + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); + eloop_register_timeout(15 + res->peer_config_timeout / 100, + (res->peer_config_timeout % 100) * 10000, + wpas_p2p_group_formation_timeout, wpa_s, NULL); +} + + +void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR + " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id); + + wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id); +} + + +void wpas_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device) +{ + struct wpa_supplicant *wpa_s = ctx; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + + wpa_msg(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", + 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); + + wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device); +} + + +static void wpas_dev_lost(void *ctx, const u8 *dev_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_p2p_device_lost(wpa_s, dev_addr); +} + + +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; + + 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"); + return -1; + } + + wpa_s->pending_listen_freq = freq; + wpa_s->pending_listen_duration = duration; + + 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; + return -1; + } + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = freq; + + return 0; +} + + +static void wpas_stop_listen(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + } + wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL); + wpa_drv_probe_req_report(wpa_s, 0); +} + + +static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf)); +} + + +static struct p2p_srv_bonjour * +wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + size_t len; + + len = wpabuf_len(query); + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (len == wpabuf_len(bsrv->query) && + os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query), + len) == 0) + return bsrv; + } + return NULL; +} + + +static struct p2p_srv_upnp * +wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version == usrv->version && + os_strcmp(service, usrv->service) == 0) + return usrv; + } + return NULL; +} + + +static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + u8 *len_pos; + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, srv_proto); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_bonjour *bsrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + return; + } + + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (wpabuf_tailroom(resp) < + 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) + return; + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), + wpabuf_len(bsrv->resp)); + /* Response Data */ + wpabuf_put_buf(resp, bsrv->query); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_bonjour *bsrv; + struct wpabuf buf; + u8 *len_pos; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour", + query, query_len); + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + wpabuf_set(&buf, query, query_len); + bsrv = wpas_p2p_service_get_bonjour(wpa_s, &buf); + if (bsrv == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not " + "available"); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + return; + } + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), wpabuf_len(bsrv->resp)); + + if (wpabuf_tailroom(resp) >= + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) { + /* Response Data */ + wpabuf_put_buf(resp, bsrv->query); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + } + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + return; + } + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service)) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, usrv->version); + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + wpabuf_put_str(resp, usrv->service); + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + u8 version; + char *str; + int count = 0; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP", + query, query_len); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + version = query[0]; + str = os_malloc(query_len); + if (str == NULL) + return; + os_memcpy(str, query + 1, query_len - 1); + str[query_len - 1] = '\0'; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version != usrv->version) + continue; + + if (os_strcmp(str, "ssdp:all") != 0 && + os_strstr(usrv->service, str) == NULL) + continue; + + if (wpabuf_tailroom(resp) < 2) + break; + if (count == 0) { + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, version); + } else + wpabuf_put_u8(resp, ','); + + count++; + + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + if (wpabuf_tailroom(resp) < os_strlen(usrv->service)) + break; + wpabuf_put_str(resp, usrv->service); + } + os_free(str); + + if (count == 0) { + wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not " + "available"); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +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; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + struct wpabuf *resp; + u8 srv_proto, srv_trans_id; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs", + tlvs, tlvs_len); + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d " + MACSTR " %u %u %s", + freq, MAC2STR(sa), dialog_token, update_indic, + buf); + os_free(buf); + } + + if (wpa_s->p2p_sd_over_ctrl_iface) + return; /* to be processed by an external program */ + + resp = wpabuf_alloc(10000); + if (resp == NULL) + return; + + while (pos + 1 < end) { + wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 2) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " + "length"); + wpabuf_free(resp); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data", + pos, tlv_end - pos); + + + if (wpa_s->force_long_sd) { + wpa_printf(MSG_DEBUG, "P2P: SD test - force long " + "response"); + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + goto done; + } + + switch (srv_proto) { + case P2P_SERV_ALL_SERVICES: + wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request " + "for all services"); + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) && + dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: No service " + "discovery protocols available"); + wpas_sd_add_proto_not_avail( + resp, P2P_SERV_ALL_SERVICES, + srv_trans_id); + break; + } + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + break; + case P2P_SERV_BONJOUR: + wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + case P2P_SERV_UPNP: + wpas_sd_req_upnp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "P2P: Unavailable service " + "protocol %u", srv_proto); + wpas_sd_add_proto_not_avail(resp, srv_proto, + srv_trans_id); + break; + } + + pos = tlv_end; + } + +done: + wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); + + wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp); + + wpabuf_free(resp); +} + + +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; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs", + tlvs, tlvs_len); + if (tlvs_len > 1500) { + /* TODO: better way for handling this */ + wpa_msg_ctrl(wpa_s, MSG_INFO, + P2P_EVENT_SERV_DISC_RESP MACSTR + " %u <long response: %u bytes>", + MAC2STR(sa), update_indic, + (unsigned int) tlvs_len); + } else { + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, + P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s", + MAC2STR(sa), update_indic, buf); + os_free(buf); + } + } + + while (pos < end) { + u8 srv_proto, srv_trans_id, status; + + wpa_printf(MSG_DEBUG, "P2P: Service Response TLV"); + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data " + "length"); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + status = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u", + status); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data", + pos, tlv_end - pos); + + pos = tlv_end; + } + + wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len); +} + + +void * 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 (void *) wpa_drv_p2p_sd_request(wpa_s, dst, tlvs); + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return NULL; + return p2p_sd_request(wpa_s->global->p2p, dst, tlvs); +} + + +void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query) +{ + struct wpabuf *tlvs; + void *ret; + + tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query)); + if (tlvs == NULL) + return NULL; + wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query)); + wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, version); + wpabuf_put_str(tlvs, query); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + + +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_sd_cancel_request(wpa_s, (u64) req); + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + return p2p_sd_cancel_request(wpa_s->global->p2p, req); +} + + +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, + 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, + resp_tlvs); +} + + +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); +} + + +static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv) +{ + dl_list_del(&bsrv->list); + wpabuf_free(bsrv->query); + wpabuf_free(bsrv->resp); + os_free(bsrv); +} + + +static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv) +{ + dl_list_del(&usrv->list); + os_free(usrv->service); + os_free(usrv); +} + + +void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s) +{ + struct p2p_srv_bonjour *bsrv, *bn; + struct p2p_srv_upnp *usrv, *un; + + dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) + wpas_p2p_srv_bonjour_free(bsrv); + + dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) + wpas_p2p_srv_upnp_free(usrv); + + wpas_p2p_sd_service_update(wpa_s); +} + + +int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *query, struct wpabuf *resp) +{ + struct p2p_srv_bonjour *bsrv; + + bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); + if (bsrv) { + wpabuf_free(query); + wpabuf_free(bsrv->resp); + bsrv->resp = resp; + return 0; + } + + bsrv = os_zalloc(sizeof(*bsrv)); + if (bsrv == NULL) + return -1; + bsrv->query = query; + bsrv->resp = resp; + dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list); + + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + + bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); + if (bsrv == NULL) + return -1; + wpas_p2p_srv_bonjour_free(bsrv); + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + if (wpas_p2p_service_get_upnp(wpa_s, version, service)) + return 0; /* Already listed */ + usrv = os_zalloc(sizeof(*usrv)); + if (usrv == NULL) + return -1; + usrv->version = version; + usrv->service = os_strdup(service); + if (usrv->service == NULL) { + os_free(usrv); + return -1; + } + dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list); + + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + usrv = wpas_p2p_service_get_upnp(wpa_s, version, service); + if (usrv == NULL) + return -1; + wpas_p2p_srv_upnp_free(usrv); + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s, + const u8 *peer, const char *params, + unsigned int generated_pin) +{ + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR " %08d%s", + MAC2STR(peer), generated_pin, params); +} + + +static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s, + const u8 *peer, const char *params) +{ + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s", + MAC2STR(peer), params); +} + + +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) +{ + struct wpa_supplicant *wpa_s = ctx; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + char params[200]; + u8 empty_dev_type[8]; + unsigned int generated_pin = 0; + + if (pri_dev_type == NULL) { + os_memset(empty_dev_type, 0, sizeof(empty_dev_type)); + pri_dev_type = empty_dev_type; + } + os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s' config_methods=0x%x " + "dev_capab=0x%x group_capab=0x%x", + MAC2STR(dev_addr), + wps_dev_type_bin2str(pri_dev_type, devtype, + sizeof(devtype)), + dev_name, supp_config_methods, dev_capab, group_capab); + params[sizeof(params) - 1] = '\0'; + + if (config_methods & WPS_CONFIG_DISPLAY) { + generated_pin = wps_generate_pin(); + wpas_prov_disc_local_display(wpa_s, peer, params, + generated_pin); + } else if (config_methods & WPS_CONFIG_KEYPAD) + wpas_prov_disc_local_keypad(wpa_s, peer, params); + else if (config_methods & WPS_CONFIG_PUSHBUTTON) + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ MACSTR + "%s", MAC2STR(peer), params); +} + + +void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) +{ + struct wpa_supplicant *wpa_s = ctx; + unsigned int generated_pin = 0; + + if (config_methods & WPS_CONFIG_DISPLAY) + wpas_prov_disc_local_keypad(wpa_s, peer, ""); + else if (config_methods & WPS_CONFIG_KEYPAD) { + generated_pin = wps_generate_pin(); + wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin); + } else if (config_methods & WPS_CONFIG_PUSHBUTTON) + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR, + MAC2STR(peer)); + + if (wpa_s->pending_pd_before_join && + (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || + os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { + wpa_s->pending_pd_before_join = 0; + wpa_printf(MSG_DEBUG, "P2P: Starting pending " + "join-existing-group operation"); + wpas_p2p_join_start(wpa_s); + } +} + + +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) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + u8 cur_bssid[ETH_ALEN]; + int res; + struct wpa_supplicant *grp; + + if (!persistent_group) { + wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR + " to join an active group", MAC2STR(sa)); + if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) && + (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN) + == 0 || + os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) { + wpa_printf(MSG_DEBUG, "P2P: Accept previously " + "authorized invitation"); + goto accept_inv; + } + /* + * Do not accept the invitation automatically; notify user and + * request approval. + */ + return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + } + + grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go); + if (grp) { + wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already " + "running persistent group"); + if (*go) + os_memcpy(group_bssid, grp->own_addr, ETH_ALEN); + goto accept_inv; + } + + if (!wpa_s->conf->persistent_reconnect) + return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled == 2 && + os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 && + s->ssid_len == ssid_len && + os_memcmp(ssid, s->ssid, ssid_len) == 0) + break; + } + + if (!s) { + wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR + " requested reinvocation of an unknown group", + MAC2STR(sa)); + return P2P_SC_FAIL_UNKNOWN_GROUP; + } + + if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) { + *go = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { + wpa_printf(MSG_DEBUG, "P2P: The only available " + "interface is already in use - reject " + "invitation"); + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN); + } else if (s->mode == WPAS_MODE_P2P_GO) { + *go = 1; + if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0) + { + wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " + "interface address for the group"); + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + os_memcpy(group_bssid, wpa_s->pending_interface_addr, + ETH_ALEN); + } + +accept_inv: + if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 && + wpa_s->assoc_freq) { + wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match " + "the channel we are already using"); + *force_freq = wpa_s->assoc_freq; + } + + res = wpa_drv_shared_freq(wpa_s); + if (res > 0) { + wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match " + "with the channel we are already using on a " + "shared interface"); + *force_freq = res; + } + + return P2P_SC_SUCCESS; +} + + +static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, u8 status, + int op_freq) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled == 2 && + s->ssid_len == ssid_len && + os_memcmp(ssid, s->ssid, ssid_len) == 0) + break; + } + + if (status == P2P_SC_SUCCESS) { + wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR + " was accepted; op_freq=%d MHz", + MAC2STR(sa), op_freq); + if (s) { + wpas_p2p_group_add_persistent( + wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0); + } else if (bssid) { + wpas_p2p_join(wpa_s, bssid, go_dev_addr, + wpa_s->p2p_wps_method); + } + return; + } + + if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR + " was rejected (status %u)", MAC2STR(sa), status); + return; + } + + if (!s) { + if (bssid) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " go_dev_addr=" MACSTR + " bssid=" MACSTR " unknown-network", + MAC2STR(sa), MAC2STR(go_dev_addr), + MAC2STR(bssid)); + } else { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " go_dev_addr=" MACSTR + " unknown-network", + MAC2STR(sa), MAC2STR(go_dev_addr)); + } + return; + } + + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR + " persistent=%d", MAC2STR(sa), s->id); +} + + +static void wpas_invitation_result(void *ctx, int status, const u8 *bssid) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *ssid; + + if (bssid) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT + "status=%d " MACSTR, + status, MAC2STR(bssid)); + } else { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT + "status=%d ", status); + } + wpas_notify_p2p_invitation_result(wpa_s, status, bssid); + + if (wpa_s->pending_invite_ssid_id == -1) + return; /* Invitation to active group */ + + if (status != P2P_SC_SUCCESS) { + wpas_p2p_remove_pending_group_interface(wpa_s); + return; + } + + ssid = wpa_config_get_network(wpa_s->conf, + wpa_s->pending_invite_ssid_id); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "P2P: Could not find persistent group " + "data matching with invitation"); + return; + } + + wpas_p2p_group_add_persistent(wpa_s, ssid, + ssid->mode == WPAS_MODE_P2P_GO, 0); +} + + +static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, + struct p2p_channels *chan) +{ + int i, cla = 0; + + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz " + "band"); + + /* Operating class 81 - 2.4 GHz band channels 1..13 */ + chan->reg_class[cla].reg_class = 81; + chan->reg_class[cla].channels = 11; + for (i = 0; i < 11; i++) + chan->reg_class[cla].channel[i] = i + 1; + cla++; + + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz " + "band"); + + /* Operating class 115 - 5 GHz, channels 36-48 */ + chan->reg_class[cla].reg_class = 115; + chan->reg_class[cla].channels = 4; + chan->reg_class[cla].channel[0] = 36; + chan->reg_class[cla].channel[1] = 40; + chan->reg_class[cla].channel[2] = 44; + chan->reg_class[cla].channel[3] = 48; + cla++; + + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz " + "band"); + + /* Operating class 124 - 5 GHz, channels 149,153,157,161 */ + chan->reg_class[cla].reg_class = 124; + chan->reg_class[cla].channels = 4; + chan->reg_class[cla].channel[0] = 149; + chan->reg_class[cla].channel[1] = 153; + chan->reg_class[cla].channel[2] = 157; + chan->reg_class[cla].channel[3] = 161; + cla++; + + chan->reg_classes = cla; + return 0; +} + + +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, + enum hostapd_hw_mode mode) +{ + u16 i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } + + return NULL; +} + + +static int has_channel(struct hostapd_hw_modes *mode, u8 chan, int *flags) +{ + int i; + + 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)); + } + } + + return 0; +} + + +struct p2p_oper_class_map { + enum hostapd_hw_mode mode; + u8 op_class; + u8 min_chan; + u8 max_chan; + u8 inc; + enum { BW20, BW40PLUS, BW40MINUS } bw; +}; + +static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, + struct p2p_channels *chan) +{ + struct hostapd_hw_modes *modes, *mode; + u16 num_modes, flags; + int cla, op; + struct p2p_oper_class_map op_class[] = { + { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, + { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20 }, +#if 0 /* Do not enable HT40 on 2 GHz for now */ + { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, +#endif + { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, + { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, + { -1, 0, 0, 0, 0, BW20 } + }; + + modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, &flags); + if (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); + } + + 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; + + mode = get_mode(modes, num_modes, o->mode); + if (mode == NULL) + continue; + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { + int flag; + if (!has_channel(mode, ch, &flag)) + continue; + if (o->bw == BW40MINUS && + (!(flag & HOSTAPD_CHAN_HT40MINUS) || + !has_channel(mode, ch - 4, NULL))) + continue; + if (o->bw == BW40PLUS && + (!(flag & HOSTAPD_CHAN_HT40PLUS) || + !has_channel(mode, ch + 4, NULL))) + 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; + } + reg->channel[reg->channels] = ch; + reg->channels++; + } + if (reg) { + wpa_hexdump(MSG_DEBUG, "P2P: Channels", + reg->channel, reg->channels); + } + } + + chan->reg_classes = cla; + + ieee80211_sta_free_hw_features(modes, num_modes); + + return 0; +} + + +static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, + size_t buf_len) +{ + struct wpa_supplicant *wpa_s = ctx; + + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0) + break; + } + if (wpa_s == NULL) + return -1; + + return wpa_drv_get_noa(wpa_s, buf, buf_len); +} + + +/** + * wpas_p2p_init - Initialize P2P module for %wpa_supplicant + * @global: Pointer to global data from wpa_supplicant_init() + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * Returns: 0 on success, -1 on failure + */ +int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) +{ + struct p2p_config p2p; + unsigned int r; + int i; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) + return 0; + +#ifdef CONFIG_CLIENT_MLME + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)) { + wpa_s->mlme.public_action_cb = p2p_rx_action_mlme; + wpa_s->mlme.public_action_cb_ctx = wpa_s; + } +#endif /* CONFIG_CLIENT_MLME */ + + if (wpa_drv_disable_11b_rates(wpa_s, 1) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to disable 11b rates"); + /* Continue anyway; this is not really a fatal error */ + } + + 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.msg_ctx = wpa_s; + p2p.cb_ctx = wpa_s; + p2p.p2p_scan = wpas_p2p_scan; + p2p.send_action = wpas_send_action; + p2p.send_action_done = wpas_send_action_done; + p2p.go_neg_completed = wpas_go_neg_completed; + p2p.go_neg_req_rx = wpas_go_neg_req_rx; + p2p.dev_found = wpas_dev_found; + p2p.dev_lost = wpas_dev_lost; + p2p.start_listen = wpas_start_listen; + p2p.stop_listen = wpas_stop_listen; + p2p.send_probe_resp = wpas_send_probe_resp; + p2p.sd_request = wpas_sd_request; + p2p.sd_response = wpas_sd_response; + p2p.prov_disc_req = wpas_prov_disc_req; + p2p.prov_disc_resp = wpas_prov_disc_resp; + p2p.invitation_process = wpas_invitation_process; + p2p.invitation_received = wpas_invitation_received; + p2p.invitation_result = wpas_invitation_result; + p2p.get_noa = wpas_get_noa; + + os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); + os_memcpy(p2p.dev_addr, wpa_s->own_addr, ETH_ALEN); + p2p.dev_name = wpa_s->conf->device_name; + p2p.manufacturer = wpa_s->conf->manufacturer; + p2p.model_name = wpa_s->conf->model_name; + p2p.model_number = wpa_s->conf->model_number; + p2p.serial_number = wpa_s->conf->serial_number; + if (wpa_s->wps) { + os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16); + p2p.config_methods = wpa_s->wps->config_methods; + } + + if (wpa_s->conf->p2p_listen_reg_class && + wpa_s->conf->p2p_listen_channel) { + p2p.reg_class = wpa_s->conf->p2p_listen_reg_class; + p2p.channel = wpa_s->conf->p2p_listen_channel; + } else { + p2p.reg_class = 81; + /* + * Pick one of the social channels randomly as the listen + * channel. + */ + os_get_random((u8 *) &r, sizeof(r)); + p2p.channel = 1 + (r % 3) * 5; + } + wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel); + + if (wpa_s->conf->p2p_oper_reg_class && + wpa_s->conf->p2p_oper_channel) { + p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class; + p2p.op_channel = wpa_s->conf->p2p_oper_channel; + p2p.cfg_op_channel = 1; + wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: " + "%d:%d", p2p.op_reg_class, p2p.op_channel); + + } else { + p2p.op_reg_class = 81; + /* + * Use random operation channel from (1, 6, 11) if no other + * preference is indicated. + */ + os_get_random((u8 *) &r, sizeof(r)); + p2p.op_channel = 1 + (r % 3) * 5; + p2p.cfg_op_channel = 0; + wpa_printf(MSG_DEBUG, "P2P: Random operating channel: " + "%d:%d", p2p.op_reg_class, p2p.op_channel); + } + if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + os_memcpy(p2p.country, wpa_s->conf->country, 2); + p2p.country[2] = 0x04; + } else + os_memcpy(p2p.country, "XX\x04", 3); + + if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) { + wpa_printf(MSG_ERROR, "P2P: Failed to configure supported " + "channel list"); + return -1; + } + + os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + + p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types; + os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type, + p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN); + + p2p.concurrent_operations = !!(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_P2P_CONCURRENT); + + p2p.max_peers = 100; + + if (wpa_s->conf->p2p_ssid_postfix) { + p2p.ssid_postfix_len = + os_strlen(wpa_s->conf->p2p_ssid_postfix); + if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix)) + p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix); + os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix, + p2p.ssid_postfix_len); + } + + p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss; + + global->p2p = p2p_init(&p2p); + if (global->p2p == NULL) + return -1; + + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { + if (wpa_s->conf->wps_vendor_ext[i] == NULL) + continue; + p2p_add_wps_vendor_extension( + global->p2p, wpa_s->conf->wps_vendor_ext[i]); + } + + return 0; +} + + +/** + * wpas_p2p_deinit - Deinitialize per-interface P2P data + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * + * This function deinitialize per-interface P2P data. + */ +void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver && wpa_s->drv_priv) + wpa_drv_probe_req_report(wpa_s, 0); + os_free(wpa_s->go_params); + wpa_s->go_params = NULL; + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + eloop_cancel_timeout(wpas_send_action_cb, 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); + + /* TODO: remove group interface from the driver if this wpa_s instance + * is on top of a P2P group interface */ +} + + +/** + * wpas_p2p_deinit_global - Deinitialize global P2P module + * @global: Pointer to global data from wpa_supplicant_init() + * + * This function deinitializes the global (per device) P2P module. + */ +void wpas_p2p_deinit_global(struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s, *tmp; + char *ifname; + + if (global->p2p == NULL) + return; + + /* Remove remaining P2P group interfaces */ + wpa_s = global->ifaces; + if (wpa_s) + wpas_p2p_service_flush(wpa_s); + while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) + wpa_s = wpa_s->next; + while (wpa_s) { + enum wpa_driver_if_type type; + tmp = global->ifaces; + while (tmp && + (tmp == wpa_s || + tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) { + tmp = tmp->next; + } + if (tmp == NULL) + break; + ifname = os_strdup(tmp->ifname); + type = wpas_p2p_if_type(tmp->p2p_group_interface); + wpa_supplicant_remove_iface(global, tmp); + if (ifname) + wpa_drv_if_remove(wpa_s, type, ifname); + os_free(ifname); + } + + /* + * Deinit GO data on any possibly remaining interface (if main + * interface is used as GO). + */ + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->ap_iface) + wpas_p2p_group_deinit(wpa_s); + } + + p2p_deinit(global->p2p); + global->p2p = NULL; +} + + +static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->drv_flags & + (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE | + WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P)) + return 1; /* P2P group requires a new interface in every case + */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT)) + return 0; /* driver does not support concurrent operations */ + if (wpa_s->global->ifaces->next) + return 1; /* more that one interface already in use */ + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + return 1; /* this interface is already in use */ + return 0; +} + + +static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group) +{ + 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); + } + + return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method, + go_intent, own_interface_addr, force_freq, + persistent_group); +} + + +static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group) +{ + 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); +} + + +static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) +{ + wpa_s->p2p_join_scan_count++; + wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d", + wpa_s->p2p_join_scan_count); + if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) { + wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR + " for join operationg - stop join attempt", + MAC2STR(wpa_s->pending_join_iface_addr)); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); + } +} + + +static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + struct wpa_bss *bss; + int freq; + u8 iface_addr[ETH_ALEN]; + + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + + if (wpa_s->global->p2p_disabled) + return; + + wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for join", + scan_res ? (int) scan_res->num : -1); + + if (scan_res) + wpas_p2p_scan_res_handler(wpa_s, scan_res); + + freq = p2p_get_oper_freq(wpa_s->global->p2p, + wpa_s->pending_join_iface_addr); + if (freq < 0 && + p2p_get_interface_addr(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr, + iface_addr) == 0 && + os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0) + { + wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface " + "address for join from " MACSTR " to " MACSTR + " based on newly discovered P2P peer entry", + MAC2STR(wpa_s->pending_join_iface_addr), + MAC2STR(iface_addr)); + os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, + ETH_ALEN); + + freq = p2p_get_oper_freq(wpa_s->global->p2p, + wpa_s->pending_join_iface_addr); + } + if (freq >= 0) { + wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " + "from P2P peer table: %d MHz", freq); + } + bss = wpa_bss_get_bssid(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); + } + if (freq > 0) { + u16 method; + + wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request " + "prior to joining an existing group (GO " MACSTR + " freq=%u MHz)", + MAC2STR(wpa_s->pending_join_dev_addr), freq); + wpa_s->pending_pd_before_join = 1; + + switch (wpa_s->pending_join_wps_method) { + case WPS_PIN_LABEL: + case WPS_PIN_DISPLAY: + method = WPS_CONFIG_KEYPAD; + break; + case WPS_PIN_KEYPAD: + method = WPS_CONFIG_DISPLAY; + break; + case WPS_PBC: + method = WPS_CONFIG_PUSHBUTTON; + break; + default: + method = 0; + break; + } + + if (p2p_prov_disc_req(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr, method, 1) + < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision " + "Discovery Request before joining an " + "existing group"); + wpa_s->pending_pd_before_join = 0; + goto start; + } + + /* + * Actual join operation will be started from the Action frame + * TX status callback. + */ + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later"); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL); + wpas_p2p_check_join_scan_limit(wpa_s); + return; + +start: + /* Start join operation immediately */ + wpas_p2p_join_start(wpa_s); +} + + +static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + int ret; + struct wpa_driver_scan_params params; + struct wpabuf *wps_ie, *ies; + + 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; + + wpa_s->wps->dev.p2p = 1; + wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, + WPS_REQ_ENROLLEE, 0, NULL); + if (wps_ie == NULL) { + wpas_p2p_scan_res_join(wpa_s, NULL); + return; + } + + ies = wpabuf_alloc(wpabuf_len(wps_ie) + 100); + if (ies == NULL) { + wpabuf_free(wps_ie); + wpas_p2p_scan_res_join(wpa_s, NULL); + return; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + p2p_scan_ie(wpa_s->global->p2p, ies); + + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + + /* + * Run a scan to update BSS table and start Provision Discovery once + * the new scan results become available. + */ + wpa_s->scan_res_handler = wpas_p2p_scan_res_join; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + ret = ieee80211_sta_req_scan(wpa_s, ¶ms); + else + ret = wpa_drv_scan(wpa_s, ¶ms); + + wpabuf_free(ies); + + if (ret) { + wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - " + "try again later"); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL); + wpas_p2p_check_join_scan_limit(wpa_s); + } +} + + +static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, + const u8 *dev_addr, enum p2p_wps_method wps_method) +{ + wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface " + MACSTR " dev " MACSTR ")", + MAC2STR(iface_addr), MAC2STR(dev_addr)); + + os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN); + os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN); + wpa_s->pending_join_wps_method = wps_method; + + /* Make sure we are not running find during connection establishment */ + wpas_p2p_stop_find(wpa_s); + + wpa_s->p2p_join_scan_count = 0; + wpas_p2p_join_scan(wpa_s, NULL); + return 0; +} + + +static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *group; + struct p2p_go_neg_results res; + + group = wpas_p2p_get_group_iface(wpa_s, 0, 0); + if (group == NULL) + return -1; + if (group != wpa_s) { + os_memcpy(group->p2p_pin, wpa_s->p2p_pin, + sizeof(group->p2p_pin)); + group->p2p_wps_method = wpa_s->p2p_wps_method; + } + + group->p2p_in_provisioning = 1; + + os_memset(&res, 0, sizeof(res)); + os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr, + ETH_ALEN); + res.wps_method = wpa_s->pending_join_wps_method; + wpas_start_wps_enrollee(group, &res); + + /* + * Allow a longer timeout for join-a-running-group than normal 15 + * second group formation timeout since the GO may not have authorized + * our connection yet. + */ + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); + eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout, + wpa_s, NULL); + + return 0; +} + + +/** + * wpas_p2p_connect - Request P2P Group Formation to be started + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @peer_addr: Address of the peer P2P Device + * @pin: PIN to use during provisioning or %NULL to indicate PBC mode + * @persistent_group: Whether to create a persistent group + * @join: Whether to join an existing group (as a client) instead of starting + * Group Owner negotiation; @peer_addr is BSSID in that case + * @auth: Whether to only authorize the connection instead of doing that and + * initiating Group Owner negotiation + * @go_intent: GO Intent or -1 to use default + * @freq: Frequency for the group or 0 for auto-selection + * 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 + */ +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 join, int auth, int go_intent, + int freq) +{ + int force_freq = 0, oper_freq = 0; + u8 bssid[ETH_ALEN]; + int ret = 0; + enum wpa_driver_if_type iftype; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + if (go_intent < 0) + go_intent = wpa_s->conf->p2p_go_intent; + + if (!auth) + wpa_s->p2p_long_listen = 0; + + wpa_s->p2p_wps_method = wps_method; + + if (pin) + os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); + else if (wps_method == WPS_PIN_DISPLAY) { + ret = wps_generate_pin(); + os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d", + ret); + wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s", + wpa_s->p2p_pin); + } else + wpa_s->p2p_pin[0] = '\0'; + + if (join) { + u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN]; + if (auth) { + wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to " + "connect a running group from " MACSTR, + MAC2STR(peer_addr)); + os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN); + return ret; + } + os_memcpy(dev_addr, peer_addr, ETH_ALEN); + if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr, + iface_addr) < 0) { + os_memcpy(iface_addr, peer_addr, ETH_ALEN); + p2p_get_dev_addr(wpa_s->global->p2p, peer_addr, + dev_addr); + } + if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method) < + 0) + return -1; + return ret; + } + + if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 && + wpa_s->assoc_freq) + oper_freq = wpa_s->assoc_freq; + else { + oper_freq = wpa_drv_shared_freq(wpa_s); + if (oper_freq < 0) + oper_freq = 0; + } + + if (freq > 0) { + if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_DEBUG, "P2P: The forced channel " + "(%u MHz) is not supported for P2P uses", + freq); + return -3; + } + + if (oper_freq > 0 && freq != oper_freq && + !(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { + wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group " + "on %u MHz while connected on another " + "channel (%u MHz)", freq, oper_freq); + return -2; + } + wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " + "requested channel (%u MHz)", freq); + force_freq = freq; + } else if (oper_freq > 0 && + !p2p_supported_freq(wpa_s->global->p2p, oper_freq)) { + if (!(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { + wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group " + "while connected on non-P2P supported " + "channel (%u MHz)", oper_freq); + return -2; + } + wpa_printf(MSG_DEBUG, "P2P: Current operating channel " + "(%u MHz) not available for P2P - try to use " + "another channel", oper_freq); + force_freq = 0; + } else if (oper_freq > 0) { + wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " + "channel we are already using (%u MHz) on another " + "interface", oper_freq); + force_freq = oper_freq; + } + + wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); + + if (!wpa_s->create_p2p_iface) { + if (auth) { + if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, + go_intent, wpa_s->own_addr, + force_freq, persistent_group) + < 0) + return -1; + return ret; + } + if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, + go_intent, wpa_s->own_addr, + force_freq, persistent_group) < 0) + return -1; + return ret; + } + + /* Prepare to add a new interface for the group */ + iftype = WPA_IF_P2P_GROUP; + if (join) + iftype = WPA_IF_P2P_CLIENT; + else 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 (auth) { + if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, + go_intent, + wpa_s->pending_interface_addr, + force_freq, persistent_group) < 0) + return -1; + return ret; + } + if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent, + wpa_s->pending_interface_addr, + force_freq, persistent_group) < 0) { + wpas_p2p_remove_pending_group_interface(wpa_s); + return -1; + } + return ret; +} + + +/** + * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @freq: Frequency of the channel in MHz + * @duration: Duration of the stay on the channel in milliseconds + * + * This callback is called when the driver indicates that it has started the + * requested remain-on-channel duration. + */ +void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + wpa_s->roc_waiting_drv_freq = 0; + wpa_s->off_channel_freq = freq; + wpas_send_action_cb(wpa_s, NULL); + if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) { + p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq, + wpa_s->pending_listen_duration); + wpa_s->pending_listen_freq = 0; + } +} + + +static 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); +} + + +/** + * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @freq: Frequency of the channel in MHz + * + * This callback is called when the driver indicates that a remain-on-channel + * operation has been completed, i.e., the duration on the requested channel + * has timed out. + */ +void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback " + "(p2p_long_listen=%d ms pending_action_tx=%p)", + wpa_s->p2p_long_listen, wpa_s->pending_action_tx); + wpa_s->off_channel_freq = 0; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (p2p_listen_end(wpa_s->global->p2p, freq) > 0) + return; /* P2P module started a new operation */ + if (wpa_s->pending_action_tx) + 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); + } +} + + +/** + * wpas_p2p_group_remove - Remove a P2P group + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @ifname: Network interface name of the group interface or "*" to remove all + * groups + * Returns: 0 on success, -1 on failure + * + * This function is used to remove a P2P group. This can be used to disconnect + * from a group in which the local end is a P2P Client or to end a P2P Group in + * case the local end is the Group Owner. If a virtual network interface was + * created for this group, that interface will be removed. Otherwise, only the + * configured P2P group network will be removed from the interface. + */ +int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) +{ + struct wpa_global *global = wpa_s->global; + + if (os_strcmp(ifname, "*") == 0) { + struct wpa_supplicant *prev; + wpa_s = global->ifaces; + while (wpa_s) { + prev = wpa_s; + wpa_s = wpa_s->next; + wpas_p2p_disconnect(prev); + } + return 0; + } + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, ifname) == 0) + break; + } + + return wpas_p2p_disconnect(wpa_s); +} + + +static void wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + int freq) +{ + u8 bssid[ETH_ALEN]; + int res; + + os_memset(params, 0, sizeof(*params)); + params->role_go = 1; + if (freq) { + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced " + "frequency %d MHz", freq); + params->freq = freq; + } else if (wpa_s->conf->p2p_oper_reg_class == 81 && + wpa_s->conf->p2p_oper_channel >= 1 && + wpa_s->conf->p2p_oper_channel <= 11) { + params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " + "frequency %d MHz", params->freq); + } else if (wpa_s->conf->p2p_oper_reg_class == 115 || + wpa_s->conf->p2p_oper_reg_class == 118) { + params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " + "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)) { + 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)) { + 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)) { + params->freq = wpa_s->best_5_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz " + "channel %d MHz", params->freq); + } else { + params->freq = 2412; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference " + "known)", params->freq); + } + + if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 && + wpa_s->assoc_freq && !freq) { + wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are " + "already using"); + params->freq = wpa_s->assoc_freq; + } + + res = wpa_drv_shared_freq(wpa_s); + if (res > 0 && !freq) { + wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are " + "already using on a shared interface"); + params->freq = res; + } +} + + +static struct wpa_supplicant * +wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, + int go) +{ + struct wpa_supplicant *group_wpa_s; + + if (!wpas_p2p_create_iface(wpa_s)) + return wpa_s; + + if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : + WPA_IF_P2P_CLIENT) < 0) + return NULL; + group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go); + if (group_wpa_s == NULL) { + wpas_p2p_remove_pending_group_interface(wpa_s); + return NULL; + } + + return group_wpa_s; +} + + +/** + * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner + * @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 + * 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) +{ + struct p2p_go_neg_results params; + unsigned int r; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + if (freq == 2) { + 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)) { + freq = wpa_s->best_24_freq; + wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band " + "channel: %d MHz", freq); + } else { + os_get_random((u8 *) &r, sizeof(r)); + freq = 2412 + (r % 3) * 25; + wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band " + "channel: %d MHz", freq); + } + } + + if (freq == 5) { + 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, + wpa_s->best_5_freq)) { + freq = wpa_s->best_5_freq; + wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band " + "channel: %d MHz", freq); + } else { + os_get_random((u8 *) &r, sizeof(r)); + freq = 5180 + (r % 4) * 20; + if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_DEBUG, "P2P: Could not select " + "5 GHz channel for P2P group"); + return -1; + } + wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band " + "channel: %d MHz", freq); + } + } + + if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO " + "(%u MHz) is not supported for P2P uses", + freq); + return -1; + } + + wpas_p2p_init_go_params(wpa_s, ¶ms, freq); + p2p_go_params(wpa_s->global->p2p, ¶ms); + params.persistent_group = persistent_group; + + wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1); + if (wpa_s == NULL) + return -1; + wpas_start_wps_go(wpa_s, ¶ms, 0); + + return 0; +} + + +static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, + struct wpa_ssid *params, int addr_allocated) +{ + struct wpa_ssid *ssid; + + wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0); + if (wpa_s == NULL) + return -1; + + wpa_supplicant_ap_deinit(wpa_s); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + ssid->proto = WPA_PROTO_RSN; + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + ssid->group_cipher = WPA_CIPHER_CCMP; + ssid->key_mgmt = WPA_KEY_MGMT_PSK; + ssid->ssid = os_malloc(params->ssid_len); + if (ssid->ssid == NULL) { + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return -1; + } + os_memcpy(ssid->ssid, params->ssid, params->ssid_len); + ssid->ssid_len = params->ssid_len; + ssid->p2p_group = 1; + ssid->export_keys = 1; + if (params->psk_set) { + os_memcpy(ssid->psk, params->psk, 32); + ssid->psk_set = 1; + } + if (params->passphrase) + ssid->passphrase = os_strdup(params->passphrase); + + wpa_supplicant_select_network(wpa_s, ssid); + + wpa_s->show_group_started = 1; + + return 0; +} + + +int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int addr_allocated, + int freq) +{ + struct p2p_go_neg_results params; + int go = 0; + + if (ssid->disabled != 2 || ssid->ssid == NULL) + return -1; + + if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) && + go == (ssid->mode == WPAS_MODE_P2P_GO)) { + wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is " + "already running"); + return 0; + } + + /* Make sure we are not running find during connection establishment */ + wpas_p2p_stop_find(wpa_s); + + if (ssid->mode == WPAS_MODE_INFRA) + return wpas_start_p2p_client(wpa_s, ssid, addr_allocated); + + if (ssid->mode != WPAS_MODE_P2P_GO) + return -1; + + wpas_p2p_init_go_params(wpa_s, ¶ms, freq); + + params.role_go = 1; + if (ssid->passphrase == NULL || + os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) { + wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent " + "group"); + return -1; + } + os_strlcpy(params.passphrase, ssid->passphrase, + sizeof(params.passphrase)); + os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len); + params.ssid_len = ssid->ssid_len; + params.persistent_group = 1; + + wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1); + if (wpa_s == NULL) + return -1; + + wpas_start_wps_go(wpa_s, ¶ms, 0); + + return 0; +} + + +static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies, + struct wpabuf *proberesp_ies) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->ap_iface) { + struct hostapd_data *hapd = wpa_s->ap_iface->bss[0]; + if (beacon_ies) { + wpabuf_free(hapd->p2p_beacon_ie); + hapd->p2p_beacon_ie = beacon_ies; + } + wpabuf_free(hapd->p2p_probe_resp_ie); + hapd->p2p_probe_resp_ie = proberesp_ies; + } else { + wpabuf_free(beacon_ies); + wpabuf_free(proberesp_ies); + } + wpa_supplicant_ap_update_beacon(wpa_s); +} + + +static void wpas_p2p_idle_update(void *ctx, int idle) +{ + struct wpa_supplicant *wpa_s = ctx; + if (!wpa_s->ap_iface) + return; + wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not "); + if (idle) + wpas_p2p_set_group_idle_timeout(wpa_s); + else + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); +} + + +struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, + int persistent_group, + int group_formation) +{ + 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; + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return NULL; + + cfg->persistent_group = persistent_group; + os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN); + if (wpa_s->max_stations && + wpa_s->max_stations < wpa_s->conf->max_num_sta) + cfg->max_clients = wpa_s->max_stations; + else + cfg->max_clients = wpa_s->conf->max_num_sta; + cfg->cb_ctx = wpa_s; + cfg->ie_update = wpas_p2p_ie_update; + cfg->idle_update = wpas_p2p_idle_update; + + group = p2p_group_init(wpa_s->global->p2p, cfg); + if (group == NULL) + os_free(cfg); + if (!group_formation) + p2p_group_notif_formation_done(group); + wpa_s->p2p_group = group; + return group; +} + + +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + int registrar) +{ + if (!wpa_s->p2p_in_provisioning) { + wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P " + "provisioning not in progress"); + return; + } + + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, + NULL); + 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); +} + + +int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *config_method) +{ + u16 config_methods; + + if (os_strcmp(config_method, "display") == 0) + config_methods = WPS_CONFIG_DISPLAY; + else if (os_strcmp(config_method, "keypad") == 0) + config_methods = WPS_CONFIG_KEYPAD; + else if (os_strcmp(config_method, "pbc") == 0 || + os_strcmp(config_method, "pushbutton") == 0) + config_methods = WPS_CONFIG_PUSHBUTTON; + else + return -1; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr, + config_methods); + } + + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) + return -1; + + return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, + config_methods, 0); +} + + +int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end) +{ + return p2p_scan_result_text(ies, ies_len, buf, end); +} + + +static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->pending_action_tx) + return; + + wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new " + "operation request"); + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; +} + + +int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types) +{ + 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) + return -1; + + return p2p_find(wpa_s->global->p2p, timeout, type, + num_req_dev_types, req_dev_types); +} + + +void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) +{ + wpas_p2p_clear_pending_action_tx(wpa_s); + 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); + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + wpa_drv_p2p_stop_find(wpa_s); + return; + } + + if (wpa_s->global->p2p) + p2p_stop_find(wpa_s->global->p2p); + + wpas_p2p_remove_pending_group_interface(wpa_s); +} + + +static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_s->p2p_long_listen = 0; +} + + +int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout) +{ + int res; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + wpas_p2p_clear_pending_action_tx(wpa_s); + + if (timeout == 0) { + /* + * This is a request for unlimited Listen state. However, at + * least for now, this is mapped to a Listen state for one + * hour. + */ + timeout = 3600; + } + eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); + wpa_s->p2p_long_listen = 0; + + res = wpas_p2p_listen_start(wpa_s, timeout * 1000); + if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) { + wpa_s->p2p_long_listen = timeout * 1000; + eloop_register_timeout(timeout, 0, + wpas_p2p_long_listen_timeout, + wpa_s, NULL); + } + + return res; +} + + +int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + u8 *buf, size_t len, int p2p_group) +{ + struct wpabuf *p2p_ie; + int ret; + + if (wpa_s->global->p2p_disabled) + return -1; + if (wpa_s->global->p2p == NULL) + return -1; + if (bss == NULL) + return -1; + + p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len, + p2p_group, p2p_ie); + wpabuf_free(p2p_ie); + + return ret; +} + + +int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, + const u8 *ie, size_t ie_len) +{ + if (wpa_s->global->p2p_disabled) + return 0; + if (wpa_s->global->p2p == NULL) + return 0; + + return p2p_probe_req_rx(wpa_s->global->p2p, addr, ie, ie_len); +} + + +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) +{ + if (wpa_s->global->p2p_disabled) + return; + if (wpa_s->global->p2p == NULL) + return; + + p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len, + freq); +} + + +void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies) +{ + if (wpa_s->global->p2p_disabled) + return; + if (wpa_s->global->p2p == NULL) + return; + + p2p_scan_ie(wpa_s->global->p2p, ies); +} + + +void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s) +{ + p2p_group_deinit(wpa_s->p2p_group); + wpa_s->p2p_group = NULL; +} + + +int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + 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; + + return p2p_reject(wpa_s->global->p2p, addr); +} + + +/* 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) +{ + enum p2p_invite_role role; + u8 *bssid = NULL; + + if (ssid->mode == WPAS_MODE_P2P_GO) { + role = P2P_INVITE_ROLE_GO; + if (peer_addr == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Missing peer " + "address in invitation command"); + return -1; + } + if (wpas_p2p_create_iface(wpa_s)) { + if (wpas_p2p_add_group_interface(wpa_s, + WPA_IF_P2P_GO) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to " + "allocate a new interface for the " + "group"); + return -1; + } + bssid = wpa_s->pending_interface_addr; + } else + bssid = wpa_s->own_addr; + } else { + role = P2P_INVITE_ROLE_CLIENT; + peer_addr = ssid->bssid; + } + wpa_s->pending_invite_ssid_id = ssid->id; + + 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; + + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, + ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 1); +} + + +/* Invite to join an active group */ +int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, + const u8 *peer_addr, const u8 *go_dev_addr) +{ + struct wpa_global *global = wpa_s->global; + enum p2p_invite_role role; + u8 *bssid = NULL; + struct wpa_ssid *ssid; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, ifname) == 0) + break; + } + if (wpa_s == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname); + return -1; + } + + ssid = wpa_s->current_ssid; + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for " + "invitation"); + return -1; + } + + if (ssid->mode == WPAS_MODE_P2P_GO) { + role = P2P_INVITE_ROLE_ACTIVE_GO; + bssid = wpa_s->own_addr; + if (go_dev_addr == NULL) + go_dev_addr = wpa_s->parent->own_addr; + } else { + role = P2P_INVITE_ROLE_CLIENT; + if (wpa_s->wpa_state < WPA_ASSOCIATED) { + wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot " + "invite to current group"); + return -1; + } + bssid = wpa_s->bssid; + if (go_dev_addr == NULL && + !is_zero_ether_addr(wpa_s->go_dev_addr)) + go_dev_addr = wpa_s->go_dev_addr; + } + 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, 0); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, + ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq, + go_dev_addr, 0); +} + + +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 persistent; + + if (!wpa_s->show_group_started || !ssid) + 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); + persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, + ssid->ssid_len); + os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN); + + if (wpa_s->global->p2p_group_formation == wpa_s) + wpa_s->global->p2p_group_formation = NULL; + + if (ssid->passphrase == NULL && ssid->psk_set) { + char psk[65]; + wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32); + wpa_msg(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, ssid->frequency, psk, + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : ""); + } else { + wpa_msg(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, ssid->frequency, + ssid->passphrase ? ssid->passphrase : "", + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : ""); + } + + if (persistent) + wpas_p2p_store_persistent_group(wpa_s->parent, ssid, + go_dev_addr); +} + + +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; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + if (wpa_s->wpa_state < WPA_ASSOCIATED || + wpa_s->current_ssid == NULL || + 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); +} + + +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; + + return p2p_ext_listen(wpa_s->global->p2p, period, interval); +} + + +static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->conf->p2p_group_idle == 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - " + "disabled"); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate " + "group"); + wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT; + wpas_p2p_group_delete(wpa_s); +} + + +static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); + if (wpa_s->conf->p2p_group_idle == 0) + return; + + if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group) + return; + + wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds", + wpa_s->conf->p2p_group_idle); + eloop_register_timeout(wpa_s->conf->p2p_group_idle, 0, + wpas_p2p_group_idle_timeout, wpa_s, NULL); +} + + +void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return; + + p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len); +} + + +void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return; + + p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len); +} + + +void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (p2p == NULL) + return; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) + return; + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME) + p2p_set_dev_name(p2p, wpa_s->conf->device_name); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) + p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type); + + if (wpa_s->wps && + (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS)) + p2p_set_config_methods(p2p, wpa_s->wps->config_methods); + + if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)) + p2p_set_uuid(p2p, wpa_s->wps->uuid); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) { + p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer); + p2p_set_model_name(p2p, wpa_s->conf->model_name); + p2p_set_model_number(p2p, wpa_s->conf->model_number); + p2p_set_serial_number(p2p, wpa_s->conf->serial_number); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) + p2p_set_sec_dev_types(p2p, + (void *) wpa_s->conf->sec_device_type, + wpa_s->conf->num_sec_device_types); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) { + int i; + p2p_remove_wps_vendor_extensions(p2p); + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { + if (wpa_s->conf->wps_vendor_ext[i] == NULL) + continue; + p2p_add_wps_vendor_extension( + p2p, wpa_s->conf->wps_vendor_ext[i]); + } + } + + if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && + wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + char country[3]; + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = 0x04; + p2p_set_country(p2p, country); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) { + p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix, + wpa_s->conf->p2p_ssid_postfix ? + os_strlen(wpa_s->conf->p2p_ssid_postfix) : + 0); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS) + p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss); +} + + +int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start, + int duration) +{ + if (!wpa_s->ap_iface) + return -1; + return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start, + duration); +} + + +int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled) +{ + 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); + + if (!enabled) { + struct wpa_supplicant *iface; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) + { + if (iface->cross_connect_enabled == 0) + continue; + + iface->cross_connect_enabled = 0; + iface->cross_connect_in_use = 0; + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + } + } + + return 0; +} + + +static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink) +{ + struct wpa_supplicant *iface; + + if (!uplink->global->cross_connection) + return; + + for (iface = uplink->global->ifaces; iface; iface = iface->next) { + if (!iface->cross_connect_enabled) + continue; + if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) != + 0) + continue; + if (iface->ap_iface == NULL) + continue; + if (iface->cross_connect_in_use) + continue; + + iface->cross_connect_in_use = 1; + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + } +} + + +static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink) +{ + struct wpa_supplicant *iface; + + for (iface = uplink->global->ifaces; iface; iface = iface->next) { + if (!iface->cross_connect_enabled) + continue; + if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) != + 0) + continue; + if (!iface->cross_connect_in_use) + continue; + + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + iface->cross_connect_in_use = 0; + } +} + + +void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->ap_iface || wpa_s->current_ssid == NULL || + wpa_s->current_ssid->mode != WPAS_MODE_INFRA || + wpa_s->cross_connect_disallowed) + wpas_p2p_disable_cross_connect(wpa_s); + else + wpas_p2p_enable_cross_connect(wpa_s); + if (!wpa_s->ap_iface) + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); +} + + +void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s) +{ + wpas_p2p_disable_cross_connect(wpa_s); + if (!wpa_s->ap_iface) + wpas_p2p_set_group_idle_timeout(wpa_s); +} + + +static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *iface; + + if (!wpa_s->global->cross_connection) + return; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (iface == wpa_s) + continue; + if (iface->drv_flags & + WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE) + continue; + if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) + continue; + + wpa_s->cross_connect_enabled = 1; + os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname, + sizeof(wpa_s->cross_connect_uplink)); + wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from " + "%s to %s whenever uplink is available", + wpa_s->ifname, wpa_s->cross_connect_uplink); + + if (iface->ap_iface || iface->current_ssid == NULL || + iface->current_ssid->mode != WPAS_MODE_INFRA || + iface->cross_connect_disallowed || + iface->wpa_state != WPA_COMPLETED) + break; + + wpa_s->cross_connect_in_use = 1; + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); + break; + } +} + + +int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT && + !wpa_s->p2p_in_provisioning) + return 0; /* not P2P client operation */ + + wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC " + "session overlap"); + if (wpa_s != wpa_s->parent) + wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP); + + if (wpa_s->global->p2p) + p2p_group_formation_failed(wpa_s->global->p2p); + + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + + wpas_group_formation_completed(wpa_s, 0); + return 1; +} + + +void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) +{ + struct p2p_channels chan; + + 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)) { + wpa_printf(MSG_ERROR, "P2P: Failed to update supported " + "channel list"); + return; + } + + p2p_update_channel_list(wpa_s->global->p2p, &chan); +} + + +int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) +{ + struct wpa_global *global = wpa_s->global; + int found = 0; + const u8 *peer; + + if (global->p2p == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation"); + + if (wpa_s->pending_interface_name[0] && + !is_zero_ether_addr(wpa_s->pending_interface_addr)) + found = 1; + + peer = p2p_get_go_neg_peer(global->p2p); + if (peer) { + wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer " + MACSTR, MAC2STR(peer)); + p2p_unauthorize(global->p2p, peer); + } + + wpas_p2p_stop_find(wpa_s); + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s == global->p2p_group_formation && + (wpa_s->p2p_in_provisioning || + wpa_s->parent->pending_interface_type == + WPA_IF_P2P_CLIENT)) { + wpa_printf(MSG_DEBUG, "P2P: Interface %s in group " + "formation found - cancelling", + wpa_s->ifname); + found = 1; + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + wpas_p2p_group_delete(wpa_s); + break; + } + } + + if (!found) { + wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found"); + return -1; + } + + return 0; +} + + +void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group) + return; + + wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not " + "being available anymore"); + wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE; + wpas_p2p_group_delete(wpa_s); +} + + +void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, + 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)) + return; + p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall); +} + + +int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr) +{ + u8 peer[ETH_ALEN]; + struct p2p_data *p2p = wpa_s->global->p2p; + + if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)) + return -1; + + if (hwaddr_aton(addr, peer)) + return -1; + + return p2p_unauthorize(p2p, peer); +} + + +/** + * wpas_p2p_disconnect - Disconnect from a P2P Group + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This can be used to disconnect from a group in which the local end is a P2P + * Client or to end a P2P Group in case the local end is the Group Owner. If a + * virtual network interface was created for this group, that interface will be + * removed. Otherwise, only the configured P2P group network will be removed + * from the interface. + */ +int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s) +{ + + if (wpa_s == NULL) + return -1; + + wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED; + wpas_p2p_group_delete(wpa_s); + + return 0; +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h new file mode 100644 index 0000000..69df475 --- /dev/null +++ b/wpa_supplicant/p2p_supplicant.h @@ -0,0 +1,128 @@ +/* + * wpa_supplicant - P2P + * Copyright (c) 2009-2010, Atheros Communications + * + * 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 P2P_SUPPLICANT_H +#define P2P_SUPPLICANT_H + +enum p2p_wps_method; +struct p2p_go_neg_results; +enum p2p_send_action_result; +struct p2p_peer_info; + +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_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *pin, enum p2p_wps_method wps_method, + int persistent_group, int join, int auth, int go_intent, + 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); +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 wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int addr_allocated, + int freq); +struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, + int persistent_group, + int group_formation); +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + int registrar); +int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *config_method); +void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, + const u8 *data, size_t data_len, + enum p2p_send_action_result result); +int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end); +enum p2p_discovery_type; +int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types); +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_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 *ie, size_t ie_len); +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_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); +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); +void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs); +void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query); +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req); +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, + const u8 *dst, u8 dialog_token, + const struct wpabuf *resp_tlvs); +void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s); +void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s); +int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *query, struct wpabuf *resp); +int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query); +int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service); +int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service); +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 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, + unsigned int interval); +void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len); +void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len); +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); + +#endif /* P2P_SUPPLICANT_H */ diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c new file mode 100644 index 0000000..d38a6bb --- /dev/null +++ b/wpa_supplicant/preauth_test.c @@ -0,0 +1,376 @@ +/* + * WPA Supplicant - test code for pre-authentication + * Copyright (c) 2003-2007, 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. + * + * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. + * Not used in production version. + */ + +#include "includes.h" +#include <assert.h> + +#include "common.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "eloop.h" +#include "rsn_supp/wpa.h" +#include "eap_peer/eap.h" +#include "wpa_supplicant_i.h" +#include "l2_packet/l2_packet.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" +#include "rsn_supp/preauth.h" +#include "rsn_supp/pmksa_cache.h" +#include "drivers/driver.h" + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; + +struct wpa_driver_ops *wpa_drivers[] = { NULL }; + + +struct preauth_test_data { + int auth_timed_out; +}; + + +static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) +{ + wpa_supplicant_disassociate(wpa_s, reason_code); +} + + +static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) +{ + wpa_supplicant_deauthenticate(wpa_s, reason_code); +} + + +static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + struct ieee802_1x_hdr *hdr; + + *msg_len = sizeof(*hdr) + data_len; + hdr = os_malloc(*msg_len); + if (hdr == NULL) + return NULL; + + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = htons(data_len); + + if (data) + os_memcpy(hdr + 1, data, data_len); + else + os_memset(hdr + 1, 0, data_len); + + if (data_pos) + *data_pos = hdr + 1; + + return (u8 *) hdr; +} + + +static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos); +} + + +static void _wpa_supplicant_set_state(void *ctx, enum wpa_states state) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_s->wpa_state = state; +} + + +static enum wpa_states _wpa_supplicant_get_state(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_s->wpa_state; +} + + +static int wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto, + const u8 *buf, size_t len) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static void * wpa_supplicant_get_network_ctx(void *wpa_s) +{ + return wpa_supplicant_get_ssid(wpa_s); +} + + +static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s) +{ + wpa_supplicant_cancel_auth_timeout(wpa_s); +} + + +static int wpa_supplicant_get_beacon_ie(void *wpa_s) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_get_bssid(void *wpa_s, u8 *bssid) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_set_key(void *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) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr, + int protection_type, + int key_type) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_add_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static int wpa_supplicant_remove_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + printf("%s - not implemented\n", __func__); + return -1; +} + + +static void wpa_supplicant_set_config_blob(void *ctx, + struct wpa_config_blob *blob) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_config_set_blob(wpa_s->conf, blob); +} + + +static const struct wpa_config_blob * +wpa_supplicant_get_config_blob(void *ctx, const char *name) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_config_get_blob(wpa_s->conf, name); +} + + +static void test_eapol_clean(struct wpa_supplicant *wpa_s) +{ + rsn_preauth_deinit(wpa_s->wpa); + pmksa_candidate_free(wpa_s->wpa); + wpa_sm_deinit(wpa_s->wpa); + scard_deinit(wpa_s->scard); + if (wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + wpa_config_free(wpa_s->conf); +} + + +static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct preauth_test_data *p = eloop_ctx; + printf("EAPOL test timed out\n"); + p->auth_timed_out = 1; + eloop_terminate(); +} + + +static void eapol_test_poll(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (!rsn_preauth_in_progress(wpa_s->wpa)) + eloop_terminate(); + else { + eloop_register_timeout(0, 100000, eapol_test_poll, eloop_ctx, + timeout_ctx); + } +} + + +static struct wpa_driver_ops dummy_driver; + + +static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname) +{ + struct l2_packet_data *l2; + struct wpa_sm_ctx *ctx; + + os_memset(&dummy_driver, 0, sizeof(dummy_driver)); + wpa_s->driver = &dummy_driver; + + ctx = os_zalloc(sizeof(*ctx)); + assert(ctx != NULL); + + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->set_state = _wpa_supplicant_set_state; + ctx->get_state = _wpa_supplicant_get_state; + ctx->deauthenticate = _wpa_supplicant_deauthenticate; + ctx->disassociate = _wpa_supplicant_disassociate; + ctx->set_key = wpa_supplicant_set_key; + ctx->get_network_ctx = wpa_supplicant_get_network_ctx; + ctx->get_bssid = wpa_supplicant_get_bssid; + ctx->ether_send = wpa_ether_send; + ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie; + ctx->alloc_eapol = _wpa_alloc_eapol; + ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout; + ctx->add_pmkid = wpa_supplicant_add_pmkid; + ctx->remove_pmkid = wpa_supplicant_remove_pmkid; + ctx->set_config_blob = wpa_supplicant_set_config_blob; + ctx->get_config_blob = wpa_supplicant_get_config_blob; + ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection; + + wpa_s->wpa = wpa_sm_init(ctx); + assert(wpa_s->wpa != NULL); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, WPA_PROTO_RSN); + + os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, NULL); + + l2 = l2_packet_init(wpa_s->ifname, NULL, ETH_P_RSN_PREAUTH, NULL, + NULL, 0); + assert(l2 != NULL); + if (l2_packet_get_own_addr(l2, wpa_s->own_addr)) { + wpa_printf(MSG_WARNING, "Failed to get own L2 address\n"); + exit(-1); + } + l2_packet_deinit(l2); + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); +} + + +static void eapol_test_terminate(int sig, void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = signal_ctx; + wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant wpa_s; + int ret = 1; + u8 bssid[ETH_ALEN]; + struct preauth_test_data preauth_test; + + if (os_program_init()) + return -1; + + os_memset(&preauth_test, 0, sizeof(preauth_test)); + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + if (argc != 4) { + printf("usage: preauth_test <conf> <target MAC address> " + "<ifname>\n"); + return -1; + } + + if (hwaddr_aton(argv[2], bssid)) { + printf("Failed to parse target address '%s'.\n", argv[2]); + return -1; + } + + if (eap_register_methods()) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + return -1; + } + + if (eloop_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + + os_memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.conf = wpa_config_read(argv[1]); + if (wpa_s.conf == NULL) { + printf("Failed to parse configuration file '%s'.\n", argv[1]); + return -1; + } + if (wpa_s.conf->ssid == NULL) { + printf("No networks defined.\n"); + return -1; + } + + wpa_init_conf(&wpa_s, argv[3]); + wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s); + if (wpa_s.ctrl_iface == NULL) { + printf("Failed to initialize control interface '%s'.\n" + "You may have another preauth_test process already " + "running or the file was\n" + "left by an unclean termination of preauth_test in " + "which case you will need\n" + "to manually remove this file before starting " + "preauth_test again.\n", + wpa_s.conf->ctrl_interface); + return -1; + } + if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) + return -1; + + if (rsn_preauth_init(wpa_s.wpa, bssid, &wpa_s.conf->ssid->eap)) + return -1; + + eloop_register_timeout(30, 0, eapol_test_timeout, &preauth_test, NULL); + eloop_register_timeout(0, 100000, eapol_test_poll, &wpa_s, NULL); + eloop_register_signal_terminate(eapol_test_terminate, &wpa_s); + eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s); + eloop_run(); + + if (preauth_test.auth_timed_out) + ret = -2; + else { + ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0) + ? 0 : -3; + } + + test_eapol_clean(&wpa_s); + + eap_peer_unregister_methods(); + + eloop_destroy(); + + os_program_deinit(); + + return ret; +} diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c new file mode 100644 index 0000000..4fb9bef --- /dev/null +++ b/wpa_supplicant/scan.c @@ -0,0 +1,840 @@ +/* + * WPA Supplicant - Scanning + * Copyright (c) 2003-2010, 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "mlme.h" +#include "wps_supplicant.h" +#include "p2p_supplicant.h" +#include "p2p/p2p.h" +#include "notify.h" +#include "bss.h" +#include "scan.h" + + +static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + union wpa_event_data data; + + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) + return; + + if (wpa_s->current_ssid == NULL) { + wpa_s->current_ssid = ssid; + if (wpa_s->current_ssid != NULL) + wpas_notify_network_changed(wpa_s); + } + wpa_supplicant_initiate_eapol(wpa_s); + wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured " + "network - generating associated event"); + os_memset(&data, 0, sizeof(data)); + wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); +} + + +#ifdef CONFIG_WPS +static int wpas_wps_in_use(struct wpa_config *conf, + enum wps_request_type *req_type) +{ + struct wpa_ssid *ssid; + int wps = 0; + + for (ssid = conf->ssid; ssid; ssid = ssid->next) { + if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) + continue; + + wps = 1; + *req_type = wpas_wps_get_req_type(ssid); + if (!ssid->eap.phase1) + continue; + + if (os_strstr(ssid->eap.phase1, "pbc=1")) + return 2; + } + + return wps; +} +#endif /* CONFIG_WPS */ + + +int wpa_supplicant_enabled_networks(struct wpa_config *conf) +{ + struct wpa_ssid *ssid = conf->ssid; + int count = 0; + while (ssid) { + if (!ssid->disabled) + count++; + ssid = ssid->next; + } + return count; +} + + +static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + while (ssid) { + if (!ssid->disabled) + break; + ssid = ssid->next; + } + + /* ap_scan=2 mode - try to associate with each SSID. */ + if (ssid == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached " + "end of scan list - go back to beginning"); + wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return; + } + if (ssid->next) { + /* Continue from the next SSID on the next attempt. */ + wpa_s->prev_scan_ssid = ssid; + } else { + /* Start from the beginning of the SSID list. */ + wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; + } + wpa_supplicant_associate(wpa_s, NULL, ssid); +} + + +static int int_array_len(const int *a) +{ + int i; + for (i = 0; a && a[i]; i++) + ; + return i; +} + + +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(*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; + } + a[++i] = a[j++]; + } + if (a[i]) + i++; + a[i] = 0; +} + + +int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + int ret; + + wpa_supplicant_notify_scanning(wpa_s, 1); + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + ret = ieee80211_sta_req_scan(wpa_s, params); + else + ret = wpa_drv_scan(wpa_s, params); + + if (ret) { + wpa_supplicant_notify_scanning(wpa_s, 0); + wpas_notify_scan_done(wpa_s, 0); + } else + wpa_s->scan_runs++; + + return ret; +} + + +static struct wpa_driver_scan_filter * +wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) +{ + struct wpa_driver_scan_filter *ssids; + struct wpa_ssid *ssid; + size_t count; + + *num_ssids = 0; + if (!conf->filter_ssids) + return NULL; + + for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) { + if (ssid->ssid && ssid->ssid_len) + count++; + } + if (count == 0) + return NULL; + ssids = os_zalloc(count * sizeof(struct wpa_driver_scan_filter)); + if (ssids == NULL) + return NULL; + + for (ssid = conf->ssid; ssid; ssid = ssid->next) { + if (!ssid->ssid || !ssid->ssid_len) + continue; + os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len); + ssids[*num_ssids].ssid_len = ssid->ssid_len; + (*num_ssids)++; + } + + return ssids; +} + + +static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_ssid *ssid; + int scan_req = 0, ret; + struct wpabuf *wps_ie = NULL; +#ifdef CONFIG_WPS + int wps = 0; + enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; +#endif /* CONFIG_WPS */ + struct wpa_driver_scan_params params; + size_t max_ssids; + enum wpa_states prev_state; + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled"); + return; + } + + if (wpa_s->disconnected && !wpa_s->scan_req) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + return; + } + + if (!wpa_supplicant_enabled_networks(wpa_s->conf) && + !wpa_s->scan_req) { + wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan"); + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + return; + } + + if (wpa_s->conf->ap_scan != 0 && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - " + "overriding ap_scan configuration"); + wpa_s->conf->ap_scan = 0; + wpas_notify_ap_scan_changed(wpa_s); + } + + if (wpa_s->conf->ap_scan == 0) { + wpa_supplicant_gen_assoc_event(wpa_s); + return; + } + + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) || + wpa_s->conf->ap_scan == 2) + max_ssids = 1; + else { + max_ssids = wpa_s->max_scan_ssids; + if (max_ssids > WPAS_MAX_SCAN_SSIDS) + max_ssids = WPAS_MAX_SCAN_SSIDS; + } + +#ifdef CONFIG_WPS + wps = wpas_wps_in_use(wpa_s->conf, &req_type); +#endif /* CONFIG_WPS */ + + scan_req = wpa_s->scan_req; + wpa_s->scan_req = 0; + + os_memset(¶ms, 0, sizeof(params)); + + prev_state = wpa_s->wpa_state; + if (wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_INACTIVE) + wpa_supplicant_set_state(wpa_s, WPA_SCANNING); + + /* Find the starting point from which to continue scanning */ + ssid = wpa_s->conf->ssid; + if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) { + while (ssid) { + if (ssid == wpa_s->prev_scan_ssid) { + ssid = ssid->next; + break; + } + ssid = ssid->next; + } + } + + if (scan_req != 2 && (wpa_s->conf->ap_scan == 2 || + wpa_s->connect_without_scan)) { + wpa_s->connect_without_scan = 0; + wpa_supplicant_assoc_try(wpa_s, ssid); + return; + } else if (wpa_s->conf->ap_scan == 2) { + /* + * User-initiated scan request in ap_scan == 2; scan with + * wildcard SSID. + */ + ssid = NULL; + } else { + struct wpa_ssid *start = ssid, *tssid; + int freqs_set = 0; + if (ssid == NULL && max_ssids > 1) + ssid = wpa_s->conf->ssid; + while (ssid) { + if (!ssid->disabled && ssid->scan_ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", + ssid->ssid, ssid->ssid_len); + params.ssids[params.num_ssids].ssid = + ssid->ssid; + params.ssids[params.num_ssids].ssid_len = + ssid->ssid_len; + params.num_ssids++; + if (params.num_ssids + 1 >= max_ssids) + break; + } + ssid = ssid->next; + if (ssid == start) + break; + if (ssid == NULL && max_ssids > 1 && + start != wpa_s->conf->ssid) + ssid = wpa_s->conf->ssid; + } + + for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) { + if (tssid->disabled) + continue; + if ((params.freqs || !freqs_set) && tssid->scan_freq) { + int_array_concat(¶ms.freqs, + tssid->scan_freq); + } else { + os_free(params.freqs); + params.freqs = NULL; + } + freqs_set = 1; + } + int_array_sort_unique(params.freqs); + } + + if (ssid) { + wpa_s->prev_scan_ssid = ssid; + if (max_ssids > 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in " + "the scan request"); + params.num_ssids++; + } + wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for specific " + "SSID(s)"); + } else { + wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; + params.num_ssids++; + wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard " + "SSID"); + } + +#ifdef CONFIG_P2P + wpa_s->wps->dev.p2p = 1; + if (!wps) { + wps = 1; + req_type = WPS_REQ_ENROLLEE_INFO; + } + + if (params.freqs == NULL && wpa_s->p2p_in_provisioning && + wpa_s->go_params) { + /* Optimize provisioning state scan based on GO information */ + if (wpa_s->p2p_in_provisioning < 5 && + wpa_s->go_params->freq > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO " + "preferred frequency %d MHz", + wpa_s->go_params->freq); + params.freqs = os_zalloc(2 * sizeof(int)); + if (params.freqs) + params.freqs[0] = wpa_s->go_params->freq; + } else if (wpa_s->p2p_in_provisioning < 8 && + wpa_s->go_params->freq_list[0]) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common " + "channels"); + int_array_concat(¶ms.freqs, + wpa_s->go_params->freq_list); + if (params.freqs) + int_array_sort_unique(params.freqs); + } + wpa_s->p2p_in_provisioning++; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WPS + if (params.freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) { + /* + * Optimize post-provisioning scan based on channel used + * during provisioning. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz " + "that was used during provisioning", wpa_s->wps_freq); + params.freqs = os_zalloc(2 * sizeof(int)); + if (params.freqs) + params.freqs[0] = wpa_s->wps_freq; + wpa_s->after_wps--; + } + + if (wps) { + wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, + wpa_s->wps->uuid, req_type, + 0, NULL); + if (wps_ie) { + params.extra_ies = wpabuf_head(wps_ie); + params.extra_ies_len = wpabuf_len(wps_ie); + } + } +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P + if (wps_ie) { + if (wpabuf_resize(&wps_ie, 100) == 0) { + wpas_p2p_scan_ie(wpa_s, wps_ie); + params.extra_ies = wpabuf_head(wps_ie); + params.extra_ies_len = wpabuf_len(wps_ie); + } + } +#endif /* CONFIG_P2P */ + + if (params.freqs == NULL && wpa_s->next_scan_freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously " + "generated frequency list"); + params.freqs = wpa_s->next_scan_freqs; + } else + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; + + params.filter_ssids = wpa_supplicant_build_filter_ssids( + wpa_s->conf, ¶ms.num_filter_ssids); + + ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); + + wpabuf_free(wps_ie); + os_free(params.freqs); + os_free(params.filter_ssids); + + if (ret) { + wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan"); + if (prev_state != wpa_s->wpa_state) + wpa_supplicant_set_state(wpa_s, prev_state); + wpa_supplicant_req_scan(wpa_s, 1, 0); + } +} + + +/** + * wpa_supplicant_req_scan - Schedule a scan for neighboring access points + * @wpa_s: Pointer to wpa_supplicant data + * @sec: Number of seconds after which to scan + * @usec: Number of microseconds after which to scan + * + * This function is used to schedule a scan for neighboring access points after + * the specified time. + */ +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) +{ + /* 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 (!ssid->disabled && 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; + } + } + + 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); +} + + +/** + * wpa_supplicant_cancel_scan - Cancel a scheduled scan request + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to cancel a scan request scheduled with + * wpa_supplicant_req_scan(). + */ +void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request"); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); +} + + +void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, + int scanning) +{ + if (wpa_s->scanning != scanning) { + wpa_s->scanning = scanning; + wpas_notify_scanning(wpa_s); + } +} + + +static int wpa_scan_get_max_rate(const struct wpa_scan_res *res) +{ + int rate = 0; + const u8 *ie; + int i; + + ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES); + for (i = 0; ie && i < ie[1]; i++) { + if ((ie[i + 2] & 0x7f) > rate) + rate = ie[i + 2] & 0x7f; + } + + ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES); + for (i = 0; ie && i < ie[1]; i++) { + if ((ie[i + 2] & 0x7f) > rate) + rate = ie[i + 2] & 0x7f; + } + + return rate; +} + + +const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, + u32 vendor_type) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + vendor_type == WPA_GET_BE32(&pos[2])) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, + u32 vendor_type) +{ + struct wpabuf *buf; + const u8 *end, *pos; + + buf = wpabuf_alloc(res->ie_len); + if (buf == NULL) + return NULL; + + pos = (const u8 *) (res + 1); + end = pos + res->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])) + wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(buf) == 0) { + wpabuf_free(buf); + buf = NULL; + } + + return buf; +} + + +struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon( + const struct wpa_scan_res *res, u32 vendor_type) +{ + struct wpabuf *buf; + const u8 *end, *pos; + + if (res->beacon_ie_len == 0) + return NULL; + buf = wpabuf_alloc(res->beacon_ie_len); + if (buf == NULL) + 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])) + wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(buf) == 0) { + wpabuf_free(buf); + buf = NULL; + } + + return buf; +} + + +/* Compare function for sorting scan results. Return >0 if @b is considered + * better. */ +static int wpa_scan_result_compar(const void *a, const void *b) +{ + struct wpa_scan_res **_wa = (void *) a; + struct wpa_scan_res **_wb = (void *) b; + struct wpa_scan_res *wa = *_wa; + struct wpa_scan_res *wb = *_wb; + int wpa_a, wpa_b, maxrate_a, maxrate_b; + + /* WPA/WPA2 support preferred */ + wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || + wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL; + wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL || + wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL; + + if (wpa_b && !wpa_a) + return 1; + if (!wpa_b && wpa_a) + return -1; + + /* privacy support preferred */ + if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 && + (wb->caps & IEEE80211_CAP_PRIVACY)) + return 1; + if ((wa->caps & IEEE80211_CAP_PRIVACY) && + (wb->caps & IEEE80211_CAP_PRIVACY) == 0) + return -1; + + /* best/max rate preferred if signal level close enough XXX */ + if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) || + (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { + maxrate_a = wpa_scan_get_max_rate(wa); + maxrate_b = wpa_scan_get_max_rate(wb); + if (maxrate_a != maxrate_b) + return maxrate_b - maxrate_a; + } + + /* use freq for channel preference */ + + /* all things being equal, use signal level; if signal levels are + * identical, use quality values since some drivers may only report + * that value and leave the signal level zero */ + if (wb->level == wa->level) + return wb->qual - wa->qual; + return wb->level - wa->level; +} + + +#ifdef CONFIG_WPS +/* Compare function for sorting scan results when searching a WPS AP for + * provisioning. Return >0 if @b is considered better. */ +static int wpa_scan_result_wps_compar(const void *a, const void *b) +{ + struct wpa_scan_res **_wa = (void *) a; + struct wpa_scan_res **_wb = (void *) b; + struct wpa_scan_res *wa = *_wa; + struct wpa_scan_res *wb = *_wb; + int uses_wps_a, uses_wps_b; + struct wpabuf *wps_a, *wps_b; + int res; + + /* Optimization - check WPS IE existence before allocated memory and + * doing full reassembly. */ + uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL; + uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL; + if (uses_wps_a && !uses_wps_b) + return -1; + if (!uses_wps_a && uses_wps_b) + return 1; + + if (uses_wps_a && uses_wps_b) { + wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE); + wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE); + res = wps_ap_priority_compar(wps_a, wps_b); + wpabuf_free(wps_a); + wpabuf_free(wps_b); + if (res) + return res; + } + + /* + * Do not use current AP security policy as a sorting criteria during + * WPS provisioning step since the AP may get reconfigured at the + * completion of provisioning. + */ + + /* all things being equal, use signal level; if signal levels are + * identical, use quality values since some drivers may only report + * that value and leave the signal level zero */ + if (wb->level == wa->level) + return wb->qual - wa->qual; + return wb->level - wa->level; +} +#endif /* CONFIG_WPS */ + + +/** + * wpa_supplicant_get_scan_results - Get scan results + * @wpa_s: Pointer to wpa_supplicant data + * @info: Information about what was scanned or %NULL if not available + * @new_scan: Whether a new scan was performed + * Returns: Scan results, %NULL on failure + * + * This function request the current scan results from the driver and updates + * the local BSS list wpa_s->bss. The caller is responsible for freeing the + * results with wpa_scan_results_free(). + */ +struct wpa_scan_results * +wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, + struct scan_info *info, int new_scan) +{ + struct wpa_scan_results *scan_res; + size_t i; + int (*compar)(const void *, const void *) = wpa_scan_result_compar; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + scan_res = ieee80211_sta_get_scan_results(wpa_s); + else + scan_res = wpa_drv_get_scan_results2(wpa_s); + if (scan_res == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results"); + return NULL; + } + +#ifdef CONFIG_WPS + if (wpas_wps_in_progress(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS " + "provisioning rules"); + compar = wpa_scan_result_wps_compar; + } +#endif /* CONFIG_WPS */ + + qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), + compar); + + wpa_bss_update_start(wpa_s); + for (i = 0; i < scan_res->num; i++) + wpa_bss_update_scan_res(wpa_s, scan_res->res[i]); + wpa_bss_update_end(wpa_s, info, new_scan); + + return scan_res; +} + + +int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s) +{ + struct wpa_scan_results *scan_res; + scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0); + if (scan_res == NULL) + return -1; + wpa_scan_results_free(scan_res); + + return 0; +} + + +void wpa_scan_results_free(struct wpa_scan_results *res) +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->num; i++) + os_free(res->res[i]); + os_free(res->res); + os_free(res); +} diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h new file mode 100644 index 0000000..025b815 --- /dev/null +++ b/wpa_supplicant/scan.h @@ -0,0 +1,39 @@ +/* + * WPA Supplicant - Scanning + * Copyright (c) 2003-2010, 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. + */ + +#ifndef SCAN_H +#define SCAN_H + +int wpa_supplicant_enabled_networks(struct wpa_config *conf); +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec); +void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s); +void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, + int scanning); +struct wpa_driver_scan_params; +int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params); +struct wpa_scan_results * +wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, + struct scan_info *info, int new_scan); +int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s); +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); +struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, + u32 vendor_type); +struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon( + const struct wpa_scan_res *res, u32 vendor_type); +void wpa_scan_results_free(struct wpa_scan_results *res); + +#endif /* SCAN_H */ diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c new file mode 100644 index 0000000..325ffc5 --- /dev/null +++ b/wpa_supplicant/sme.c @@ -0,0 +1,748 @@ +/* + * wpa_supplicant - SME + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "common/wpa_common.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/pmksa_cache.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "wpas_glue.h" +#include "wps_supplicant.h" +#include "p2p_supplicant.h" +#include "notify.h" +#include "blacklist.h" +#include "bss.h" +#include "scan.h" +#include "sme.h" + +#define SME_AUTH_TIMEOUT 5 +#define SME_ASSOC_TIMEOUT 5 + +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx); +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx); +#ifdef CONFIG_IEEE80211W +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); +#endif /* CONFIG_IEEE80211W */ + + +void sme_authenticate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid) +{ + struct wpa_driver_auth_params params; + struct wpa_ssid *old_ssid; +#ifdef CONFIG_IEEE80211R + const u8 *ie; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211R + const u8 *md = NULL; +#endif /* CONFIG_IEEE80211R */ + int i, bssid_changed; + + if (bss == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " + "the network"); + return; + } + + wpa_s->current_bss = bss; + + os_memset(¶ms, 0, sizeof(params)); + wpa_s->reassociate = 0; + + params.freq = bss->freq; + params.bssid = bss->bssid; + params.ssid = bss->ssid; + params.ssid_len = bss->ssid_len; + + if (wpa_s->sme.ssid_len != params.ssid_len || + os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0) + wpa_s->sme.prev_bssid_set = 0; + + wpa_s->sme.freq = params.freq; + os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len); + wpa_s->sme.ssid_len = params.ssid_len; + + params.auth_alg = WPA_AUTH_ALG_OPEN; +#ifdef IEEE8021X_EAPOL + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if (ssid->leap) { + if (ssid->non_leap == 0) + params.auth_alg = WPA_AUTH_ALG_LEAP; + else + params.auth_alg |= WPA_AUTH_ALG_LEAP; + } + } +#endif /* IEEE8021X_EAPOL */ + wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", + params.auth_alg); + if (ssid->auth_alg) { + params.auth_alg = ssid->auth_alg; + wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " + "0x%x", params.auth_alg); + } + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) + params.wep_key[i] = ssid->wep_key[i]; + params.wep_key_len[i] = ssid->wep_key_len[i]; + } + params.wep_tx_keyidx = ssid->wep_tx_keyidx; + + bssid_changed = !is_zero_ether_addr(wpa_s->bssid); + os_memset(wpa_s->bssid, 0, ETH_ALEN); + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); + + if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || + wpa_bss_get_ie(bss, WLAN_EID_RSN)) && + (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_PSK_SHA256))) { + int try_opportunistic; + try_opportunistic = ssid->proactive_key_caching && + (ssid->proto & WPA_PROTO_RSN); + if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, + wpa_s->current_ssid, + try_opportunistic) == 0) + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); + if (wpa_supplicant_set_suites(wpa_s, bss, ssid, + wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len)) { + wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " + "key management and encryption suites"); + return; + } + } else if (ssid->key_mgmt & + (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256)) { + wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); + if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len)) { + wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " + "key management and encryption suites (no " + "scan results)"); + return; + } +#ifdef CONFIG_WPS + } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + struct wpabuf *wps_ie; + wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); + if (wps_ie && wpabuf_len(wps_ie) <= + sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie); + os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie), + wpa_s->sme.assoc_req_ie_len); + } else + wpa_s->sme.assoc_req_ie_len = 0; + wpabuf_free(wps_ie); + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); +#endif /* CONFIG_WPS */ + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_s->sme.assoc_req_ie_len = 0; + } + +#ifdef CONFIG_IEEE80211R + ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); + if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) + md = ie + 2; + wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); + if (md) { + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(wpa_s->wpa, ie); + } + + if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X)) { + if (wpa_s->sme.assoc_req_ie_len + 5 < + sizeof(wpa_s->sme.assoc_req_ie)) { + struct rsn_mdie *mdie; + u8 *pos = wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len; + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = sizeof(*mdie); + mdie = (struct rsn_mdie *) pos; + os_memcpy(mdie->mobility_domain, md, + MOBILITY_DOMAIN_ID_LEN); + mdie->ft_capab = md[MOBILITY_DOMAIN_ID_LEN]; + wpa_s->sme.assoc_req_ie_len += 5; + } + + if (wpa_s->sme.ft_used && + os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 && + wpa_sm_has_ptk(wpa_s->wpa)) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT " + "over-the-air"); + params.auth_alg = WPA_AUTH_ALG_FT; + params.ie = wpa_s->sme.ft_ies; + params.ie_len = wpa_s->sme.ft_ies_len; + } + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + wpa_s->sme.mfp = ssid->ieee80211w; + if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + struct wpa_ie_data _ie; + if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && + _ie.capabilities & + (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected AP supports " + "MFP: require MFP"); + wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED; + } + } +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) { + u8 *pos; + size_t len; + int res; + int p2p_group; + p2p_group = wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE; + pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; + len = sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len; + res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, p2p_group); + if (res >= 0) + wpa_s->sme.assoc_req_ie_len += res; + } +#endif /* CONFIG_P2P */ + + wpa_supplicant_cancel_scan(wpa_s); + + wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR + " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), + wpa_ssid_txt(params.ssid, params.ssid_len), params.freq); + + wpa_clear_keys(wpa_s, bss->bssid); + wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); + old_ssid = wpa_s->current_ssid; + wpa_s->current_ssid = ssid; + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); + wpa_supplicant_initiate_eapol(wpa_s); + if (old_ssid != wpa_s->current_ssid) + wpas_notify_network_changed(wpa_s); + + 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 " + "driver failed"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + return; + } + + eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s, + NULL); + + /* + * Association will be started based on the authentication event from + * the driver. + */ +} + + +void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " + "when network is not selected"); + return; + } + + if (wpa_s->wpa_state != WPA_AUTHENTICATING) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " + "when not in authenticating state"); + return; + } + + if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with " + "unexpected peer " MACSTR, + MAC2STR(data->auth.peer)); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR + " auth_type=%d status_code=%d", + MAC2STR(data->auth.peer), data->auth.auth_type, + data->auth.status_code); + wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", + data->auth.ies, data->auth.ies_len); + + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + + if (data->auth.status_code != WLAN_STATUS_SUCCESS) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status " + "code %d)", data->auth.status_code); + + if (data->auth.status_code != + WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG || + wpa_s->sme.auth_alg == data->auth.auth_type || + wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + return; + } + + switch (data->auth.auth_type) { + case WLAN_AUTH_OPEN: + wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED; + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth"); + wpa_supplicant_associate(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid); + return; + + case WLAN_AUTH_SHARED_KEY: + wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP; + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth"); + wpa_supplicant_associate(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid); + return; + + default: + return; + } + } + +#ifdef CONFIG_IEEE80211R + if (data->auth.auth_type == WLAN_AUTH_FT) { + union wpa_event_data edata; + os_memset(&edata, 0, sizeof(edata)); + edata.ft_ies.ies = data->auth.ies; + edata.ft_ies.ies_len = data->auth.ies_len; + os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN); + wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata); + } +#endif /* CONFIG_IEEE80211R */ + + sme_associate(wpa_s, ssid->mode, data->auth.peer, + data->auth.auth_type); +} + + +void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, + const u8 *bssid, u16 auth_type) +{ + struct wpa_driver_associate_params params; + struct ieee802_11_elems elems; + + os_memset(¶ms, 0, sizeof(params)); + params.bssid = bssid; + params.ssid = wpa_s->sme.ssid; + params.ssid_len = wpa_s->sme.ssid_len; + params.freq = wpa_s->sme.freq; + 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 = cipher_suite2driver(wpa_s->pairwise_cipher); + params.group_suite = cipher_suite2driver(wpa_s->group_cipher); +#ifdef CONFIG_IEEE80211R + if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { + params.wpa_ie = wpa_s->sme.ft_ies; + params.wpa_ie_len = wpa_s->sme.ft_ies_len; + } +#endif /* CONFIG_IEEE80211R */ + params.mode = mode; + params.mgmt_frame_protection = wpa_s->sme.mfp; + if (wpa_s->sme.prev_bssid_set) + params.prev_bssid = wpa_s->sme.prev_bssid; + + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR + " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), + params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "", + params.freq); + + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); + + if (params.wpa_ie == NULL || + ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0) + < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!"); + os_memset(&elems, 0, sizeof(elems)); + } + if (elems.rsn_ie) + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2, + elems.rsn_ie_len + 2); + else if (elems.wpa_ie) + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2, + elems.wpa_ie_len + 2); + else + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + if (elems.p2p && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) + params.p2p = 1; + + if (wpa_s->parent->set_sta_uapsd) + params.uapsd = wpa_s->parent->sta_uapsd; + else + params.uapsd = -1; + + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { + wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the " + "driver failed"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + return; + } + + eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s, + NULL); +} + + +int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len) +{ + if (md == NULL || ies == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain"); + os_free(wpa_s->sme.ft_ies); + wpa_s->sme.ft_ies = NULL; + wpa_s->sme.ft_ies_len = 0; + wpa_s->sme.ft_used = 0; + return 0; + } + + os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); + wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len); + os_free(wpa_s->sme.ft_ies); + wpa_s->sme.ft_ies = os_malloc(ies_len); + if (wpa_s->sme.ft_ies == NULL) + return -1; + os_memcpy(wpa_s->sme.ft_ies, ies, ies_len); + wpa_s->sme.ft_ies_len = ies_len; + return 0; +} + + +static void sme_deauth(struct wpa_supplicant *wpa_s) +{ + int bssid_changed; + + bssid_changed = !is_zero_ether_addr(wpa_s->bssid); + + if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, + WLAN_REASON_DEAUTH_LEAVING) < 0) { + wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver " + "failed"); + } + wpa_s->sme.prev_bssid_set = 0; + + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + os_memset(wpa_s->bssid, 0, ETH_ALEN); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); +} + + +void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: " + "status code %d", MAC2STR(wpa_s->pending_bssid), + data->assoc_reject.status_code); + + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + + /* + * For now, unconditionally terminate the previous authentication. In + * theory, this should not be needed, but mac80211 gets quite confused + * if the authentication is left pending.. Some roaming cases might + * benefit from using the previous authentication, so this could be + * optimized in the future. + */ + sme_deauth(wpa_s); +} + + +void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); +} + + +void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_mark_disassoc(wpa_s); +} + + +void sme_event_disassoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received"); + if (wpa_s->sme.prev_bssid_set && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) { + /* + * cfg80211/mac80211 can get into somewhat confused state if + * the AP only disassociates us and leaves us in authenticated + * state. For now, force the state to be cleared to avoid + * confusing errors if we try to associate with the AP again. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear " + "driver state"); + wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid, + WLAN_REASON_DEAUTH_LEAVING); + } +} + + +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (wpa_s->wpa_state == WPA_AUTHENTICATING) { + wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout"); + sme_deauth(wpa_s); + } +} + + +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (wpa_s->wpa_state == WPA_ASSOCIATING) { + wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout"); + sme_deauth(wpa_s); + } +} + + +void sme_state_changed(struct wpa_supplicant *wpa_s) +{ + /* Make sure timers are cleaned up appropriately. */ + if (wpa_s->wpa_state != WPA_ASSOCIATING) + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + if (wpa_s->wpa_state != WPA_AUTHENTICATING) + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); +} + + +void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, + const u8 *prev_pending_bssid) +{ + /* + * mac80211-workaround to force deauth on failed auth cmd, + * requires us to remain in authenticating state to allow the + * second authentication attempt to be continued properly. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication " + "to proceed after disconnection event"); + wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); + os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN); + + /* + * Re-arm authentication timer in case auth fails for whatever reason. + */ + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s, + NULL); +} + + +void sme_deinit(struct wpa_supplicant *wpa_s) +{ + os_free(wpa_s->sme.ft_ies); + wpa_s->sme.ft_ies = NULL; + wpa_s->sme.ft_ies_len = 0; +#ifdef CONFIG_IEEE80211W + sme_stop_sa_query(wpa_s); +#endif /* CONFIG_IEEE80211W */ + + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); +} + + +#ifdef CONFIG_IEEE80211W + +static const unsigned int sa_query_max_timeout = 1000; +static const unsigned int sa_query_retry_timeout = 201; + +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); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (sa_query_max_timeout < tu) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out"); + sme_stop_sa_query(wpa_s); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID); + return 1; + } + + return 0; +} + + +static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s, + const u8 *trans_id) +{ + u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN]; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to " + MACSTR, MAC2STR(wpa_s->bssid)); + wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + req[0] = WLAN_ACTION_SA_QUERY; + req[1] = WLAN_SA_QUERY_REQUEST; + os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN); + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + req, sizeof(req)) < 0) + wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query " + "Request"); +} + + +static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + unsigned int timeout, sec, usec; + u8 *trans_id, *nbuf; + + if (wpa_s->sme.sa_query_count > 0 && + sme_check_sa_query_timeout(wpa_s)) + return; + + nbuf = os_realloc(wpa_s->sme.sa_query_trans_id, + (wpa_s->sme.sa_query_count + 1) * + WLAN_SA_QUERY_TR_ID_LEN); + if (nbuf == NULL) + return; + if (wpa_s->sme.sa_query_count == 0) { + /* Starting a new SA Query procedure */ + os_get_time(&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; + wpa_s->sme.sa_query_count++; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + timeout = sa_query_retry_timeout; + sec = ((timeout / 1000) * 1024) / 1000; + usec = (timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL); + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d", + wpa_s->sme.sa_query_count); + + sme_send_sa_query_req(wpa_s, trans_id); +} + + +static void sme_start_sa_query(struct wpa_supplicant *wpa_s) +{ + sme_sa_query_timer(wpa_s, NULL); +} + + +void sme_stop_sa_query(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL); + os_free(wpa_s->sme.sa_query_trans_id); + wpa_s->sme.sa_query_trans_id = NULL; + wpa_s->sme.sa_query_count = 0; +} + + +void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *da, u16 reason_code) +{ + struct wpa_ssid *ssid; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + return; + if (wpa_s->wpa_state != WPA_COMPLETED) + return; + ssid = wpa_s->current_ssid; + if (ssid == NULL || ssid->ieee80211w == 0) + return; + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA && + reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA) + return; + if (wpa_s->sme.sa_query_count > 0) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - " + "possible AP/STA state mismatch - trigger SA Query"); + sme_start_sa_query(wpa_s); +} + + +void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *data, size_t len) +{ + int i; + + if (wpa_s->sme.sa_query_trans_id == NULL || + len < 1 + WLAN_SA_QUERY_TR_ID_LEN || + data[0] != WLAN_SA_QUERY_RESPONSE) + return; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from " + MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]); + + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + + for (i = 0; i < wpa_s->sme.sa_query_count; i++) { + if (os_memcmp(wpa_s->sme.sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= wpa_s->sme.sa_query_count) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query " + "transaction identifier found"); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received " + "from " MACSTR, MAC2STR(sa)); + sme_stop_sa_query(wpa_s); +} + +#endif /* CONFIG_IEEE80211W */ diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h new file mode 100644 index 0000000..a59b38d --- /dev/null +++ b/wpa_supplicant/sme.h @@ -0,0 +1,106 @@ +/* + * wpa_supplicant - SME + * Copyright (c) 2009-2010, 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. + */ + +#ifndef SME_H +#define SME_H + +#ifdef CONFIG_SME + +void sme_authenticate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid); +void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, + const u8 *bssid, u16 auth_type); +void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data); +int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len); +void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, + union wpa_event_data *data); +void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, + union wpa_event_data *data); +void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, + union wpa_event_data *data); +void sme_event_disassoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data); +void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *da, u16 reason_code); +void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *data, size_t len); +void sme_state_changed(struct wpa_supplicant *wpa_s); +void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, + const u8 *prev_pending_bssid); +void sme_deinit(struct wpa_supplicant *wpa_s); + +#else /* CONFIG_SME */ + +static inline void sme_authenticate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_ssid *ssid) +{ +} + +static inline void sme_event_auth(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ +} + +static inline int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, + const u8 *ies, size_t ies_len) +{ + return -1; +} + + +static inline void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ +} + +static inline void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ +} + +static inline void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ +} + +static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ +} + +static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *da, + u16 reason_code) +{ +} + +static inline void sme_state_changed(struct wpa_supplicant *wpa_s) +{ +} + +static inline void +sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, + const u8 *prev_pending_bssid) +{ +} + +static inline void sme_deinit(struct wpa_supplicant *wpa_s) +{ +} + +#endif /* CONFIG_SME */ + +#endif /* SME_H */ diff --git a/wpa_supplicant/src b/wpa_supplicant/src new file mode 120000 index 0000000..5cd551c --- /dev/null +++ b/wpa_supplicant/src @@ -0,0 +1 @@ +../src
\ No newline at end of file diff --git a/wpa_supplicant/tests/link_test.c b/wpa_supplicant/tests/link_test.c new file mode 100644 index 0000000..3bfbed5 --- /dev/null +++ b/wpa_supplicant/tests/link_test.c @@ -0,0 +1,83 @@ +/* + * Dummy functions to allow link_test to be linked. The need for these + * functions should be removed to allow IEEE 802.1X/EAPOL authenticator to + * be built outside hostapd. + */ + +#include "includes.h" + +#include "common.h" + + +struct hostapd_data; +struct sta_info; +struct rsn_pmksa_cache_entry; +struct eapol_state_machine; +struct hostapd_eap_user; +struct hostapd_bss_config; +struct hostapd_vlan; + + +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) +{ + return NULL; +} + + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx) +{ + return 0; +} + + +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout) +{ +} + + +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid) +{ + return 0; +} + + +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ +} + + +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ +} + + +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) +{ +} + + +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ +} + + +const struct hostapd_eap_user * +hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, + size_t identity_len, int phase2) +{ + return NULL; +} + + +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +{ + return NULL; +} diff --git a/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c new file mode 100644 index 0000000..deb19f6 --- /dev/null +++ b/wpa_supplicant/tests/test_eap_sim_common.c @@ -0,0 +1,53 @@ +/* + * Test program for EAP-SIM PRF + * Copyright (c) 2004-2006, 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. + */ + +#include "eap_common/eap_sim_common.c" + + +static int test_eap_sim_prf(void) +{ + /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ + u8 xkey[] = { + 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 + }; + u8 w[] = { + 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + }; + u8 buf[40]; + + printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); + eap_sim_prf(xkey, buf, sizeof(buf)); + if (memcmp(w, buf, sizeof(w) != 0)) { + printf("eap_sim_prf failed\n"); + return 1; + } + + return 0; +} + + +int main(int argc, char *argv[]) +{ + int errors = 0; + + errors += test_eap_sim_prf(); + + return errors; +} diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c new file mode 100644 index 0000000..7947137 --- /dev/null +++ b/wpa_supplicant/tests/test_wpa.c @@ -0,0 +1,379 @@ +/* + * Test program for combined WPA authenticator/supplicant + * Copyright (c) 2006-2007, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "../config.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/wpa_ie.h" +#include "../hostapd/wpa.h" + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; + + +struct wpa { + u8 auth_addr[ETH_ALEN]; + u8 supp_addr[ETH_ALEN]; + u8 psk[PMK_LEN]; + + /* from authenticator */ + u8 auth_eapol_dst[ETH_ALEN]; + u8 *auth_eapol; + size_t auth_eapol_len; + + /* from supplicant */ + u8 *supp_eapol; + size_t supp_eapol_len; + + struct wpa_sm *supp; + struct wpa_authenticator *auth_group; + struct wpa_state_machine *auth; + + struct wpa_ssid ssid; + u8 supp_ie[80]; + size_t supp_ie_len; +}; + + +static int supp_get_bssid(void *ctx, u8 *bssid) +{ + struct wpa *wpa = ctx; + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); + os_memcpy(bssid, wpa->auth_addr, ETH_ALEN); + return 0; +} + + +static void supp_set_state(void *ctx, enum wpa_states state) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state); +} + + +static void auth_eapol_rx(void *eloop_data, void *user_ctx) +{ + struct wpa *wpa = eloop_data; + + wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame"); + wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol, + wpa->supp_eapol_len); +} + + +static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, + size_t len) +{ + struct wpa *wpa = ctx; + + wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x " + "len=%lu)", + __func__, MAC2STR(dest), proto, (unsigned long) len); + + os_free(wpa->supp_eapol); + wpa->supp_eapol = os_malloc(len); + if (wpa->supp_eapol == NULL) + return -1; + os_memcpy(wpa->supp_eapol, buf, len); + wpa->supp_eapol_len = len; + eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL); + + return 0; +} + + +static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data, + u16 data_len, size_t *msg_len, void **data_pos) +{ + struct ieee802_1x_hdr *hdr; + + wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)", + __func__, type, data_len); + + *msg_len = sizeof(*hdr) + data_len; + hdr = os_malloc(*msg_len); + if (hdr == NULL) + return NULL; + + hdr->version = 2; + hdr->type = type; + hdr->length = host_to_be16(data_len); + + if (data) + os_memcpy(hdr + 1, data, data_len); + else + os_memset(hdr + 1, 0, data_len); + + if (data_pos) + *data_pos = hdr + 1; + + return (u8 *) hdr; +} + + +static int supp_get_beacon_ie(void *ctx) +{ + struct wpa *wpa = ctx; + const u8 *ie; + size_t ielen; + + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); + + ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen); + if (ie == NULL || ielen < 1) + return -1; + if (ie[0] == WLAN_EID_RSN) + return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]); + return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]); +} + + +static int supp_set_key(void *ctx, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d " + "set_tx=%d)", + __func__, alg, MAC2STR(addr), key_idx, set_tx); + wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len); + wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len); + return 0; +} + + +static int supp_mlme_setprotection(void *ctx, const u8 *addr, + int protection_type, int key_type) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d " + "key_type=%d)", + __func__, MAC2STR(addr), protection_type, key_type); + return 0; +} + + +static void supp_cancel_auth_timeout(void *ctx) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); +} + + +static int supp_init(struct wpa *wpa) +{ + struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return -1; + + ctx->ctx = wpa; + ctx->msg_ctx = wpa; + ctx->set_state = supp_set_state; + ctx->get_bssid = supp_get_bssid; + ctx->ether_send = supp_ether_send; + ctx->get_beacon_ie = supp_get_beacon_ie; + ctx->alloc_eapol = supp_alloc_eapol; + ctx->set_key = supp_set_key; + ctx->mlme_setprotection = supp_mlme_setprotection; + ctx->cancel_auth_timeout = supp_cancel_auth_timeout; + wpa->supp = wpa_sm_init(ctx); + if (wpa->supp == NULL) { + wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); + return -1; + } + + wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr); + wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1); + wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN); + wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); + wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); + wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); + wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN); + + wpa->supp_ie_len = sizeof(wpa->supp_ie); + if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie, + &wpa->supp_ie_len) < 0) { + wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()" + " failed"); + return -1; + } + + wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr); + + return 0; +} + + +static void auth_logger(void *ctx, const u8 *addr, logger_level level, + const char *txt) +{ + if (addr) + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", + MAC2STR(addr), txt); + else + wpa_printf(MSG_DEBUG, "AUTH: %s", txt); +} + + +static void supp_eapol_rx(void *eloop_data, void *user_ctx) +{ + struct wpa *wpa = eloop_data; + + wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame"); + wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol, + wpa->auth_eapol_len); +} + + +static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, + size_t data_len, int encrypt) +{ + struct wpa *wpa = ctx; + + wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu " + "encrypt=%d)", + __func__, MAC2STR(addr), (unsigned long) data_len, encrypt); + + os_free(wpa->auth_eapol); + wpa->auth_eapol = os_malloc(data_len); + if (wpa->auth_eapol == NULL) + return -1; + os_memcpy(wpa->auth_eapol_dst, addr, ETH_ALEN); + os_memcpy(wpa->auth_eapol, data, data_len); + wpa->auth_eapol_len = data_len; + eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL); + + return 0; +} + + +static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk) +{ + struct wpa *wpa = ctx; + wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", + __func__, MAC2STR(addr), prev_psk); + if (prev_psk) + return NULL; + return wpa->psk; +} + + +static int auth_init_group(struct wpa *wpa) +{ + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); + + os_memset(&conf, 0, sizeof(conf)); + conf.wpa = 2; + conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK; + conf.wpa_pairwise = WPA_CIPHER_CCMP; + conf.rsn_pairwise = WPA_CIPHER_CCMP; + conf.wpa_group = WPA_CIPHER_CCMP; + conf.eapol_version = 2; + + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = wpa; + cb.logger = auth_logger; + cb.send_eapol = auth_send_eapol; + cb.get_psk = auth_get_psk; + + wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb); + if (wpa->auth_group == NULL) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); + return -1; + } + + return 0; +} + + +static int auth_init(struct wpa *wpa) +{ + wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr); + if (wpa->auth == NULL) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); + return -1; + } + + if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie, + wpa->supp_ie_len, NULL, 0) != WPA_IE_OK) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); + return -1; + } + + wpa_auth_sm_event(wpa->auth, WPA_ASSOC); + + wpa_auth_sta_associated(wpa->auth_group, wpa->auth); + + return 0; +} + + +static void deinit(struct wpa *wpa) +{ + wpa_auth_sta_deinit(wpa->auth); + wpa_sm_deinit(wpa->supp); + wpa_deinit(wpa->auth_group); + os_free(wpa->auth_eapol); + wpa->auth_eapol = NULL; + os_free(wpa->supp_eapol); + wpa->supp_eapol = NULL; +} + + +int main(int argc, char *argv[]) +{ + struct wpa wpa; + + if (os_program_init()) + return -1; + + os_memset(&wpa, 0, sizeof(wpa)); + os_memset(wpa.auth_addr, 0x12, ETH_ALEN); + os_memset(wpa.supp_addr, 0x32, ETH_ALEN); + os_memset(wpa.psk, 0x44, PMK_LEN); + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + if (eloop_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + + if (auth_init_group(&wpa) < 0) + return -1; + + if (supp_init(&wpa) < 0) + return -1; + + if (auth_init(&wpa) < 0) + return -1; + + wpa_printf(MSG_DEBUG, "Starting eloop"); + eloop_run(); + wpa_printf(MSG_DEBUG, "eloop done"); + + deinit(&wpa); + + eloop_destroy(); + + os_program_deinit(); + + return 0; +} diff --git a/wpa_supplicant/todo.txt b/wpa_supplicant/todo.txt new file mode 100644 index 0000000..b84cccc --- /dev/null +++ b/wpa_supplicant/todo.txt @@ -0,0 +1,85 @@ +To do: +- add support for WPA with ap_scan=0 (update selected cipher etc. based on + AssocInfo; make sure these match with configuration) +- consider closing smart card / PCSC connection when EAP-SIM/EAP-AKA + authentication has been completed (cache scard data based on serial#(?) + and try to optimize next connection if the same card is present for next + auth) +- on disconnect event, could try to associate with another AP if one is + present in scan results; would need to update scan results periodically.. +- if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from + ssid->proto fields to avoid detecting downgrade attacks when the driver + is not reporting RSN IE, but msg 3/4 has one +- Cisco AP and non-zero keyidx for unicast -> map to broadcast + (actually, this already works with driver_ndis; so maybe just change + driver_*.c to do the mapping for drivers that cannot handle non-zero keyidx + for unicast); worked also with Host AP driver and madwifi +- IEEE 802.1X and key update with driver_ndis?? wpa_supplicant did not seem + to see unencrypted EAPOL-Key frames at all.. +- EAP-PAX with PAX_SEC +- EAP (RFC 3748) + * OTP Extended Responses (Sect. 5.5) +- test what happens if authenticator sends EAP-Success before real EAP + authentication ("canned" Success); this should be ignored based on + RFC 3748 Sect. 4.2 +- test compilation with gcc -W options (more warnings?) + (Done once; number of unused function arguments still present) +- add proper support for using dot11RSNAConfigSATimeout +- ctrl_iface: get/set/remove blob +- use doc/docbook/*.sgml and docbook2{txt,html,pdf} to replace README and + web pages including the same information.. i.e., have this information only + in one page; how to build a PDF file with all the SGML included? +- EAP-POTP/RSA SecurID profile (RFC 4793) +- document wpa_gui build and consider adding it to 'make install' +- test madwifi with pairwise=TKIP group=WEP104 +- consider merging hostapd and wpa_supplicant PMKSA cache implementations +- consider redesigning pending EAP requests (identity/password/otp from + ctrl_iface) by moving the retrying of the previous request into EAP + state machine so that EAPOL state machine is not needed for this +- rfc4284.txt (network selection for eap) +- www pages about configuring wpa_supplicant: + * global options (ap_scan, ctrl_interfaces) based on OS/driver + * network block + * key_mgmt selection + * WPA parameters + * EAP options (one page for each method) + * "configuration wizard" (step 1: select OS, step 2: select driver, ...) to + generate example configuration +- error path in rsn_preauth_init: should probably deinit l2_packet handlers + if something fails; does something else need deinit? +- consider moving SIM card functionality (IMSI fetching) away from eap.c; + this should likely happen before EAP is initialized for authentication; + now IMSI is read only after receiving EAP-Identity/Request, but since it is + really needed for all cases, reading IMSI and generating Identity string + could very well be done before EAP has been started +- try to work around race in receiving association event and first EAPOL + message +- try to work around race in configuring PTK and sending msg 4/4 (some NDIS + drivers with ndiswrapper end up not being able to complete 4-way handshake + in some cases; extra delay before setting the key seems to help) +- add wpa_secure_memzero() macro and secure implementation (volatile u8*) to + clear memory; this would be used to clear temporary buffers containing + private data (e.g., keys); the macro can be defined to NOP in order to save + space (i.e., no code should depend on the macro doing something) +- make sure that TLS session cache is not shared between EAP types or if it + is, that the cache entries are bound to only one EAP type; e.g., cache entry + created with EAP-TLS must not be allowed to do fast re-auth with EAP-TTLS +- consider moving eap_tls_build_ack() call into eap_tls_process_helper() + (it seems to be called always if helper returns 1) + * could need to modify eap_{ttls,peap,fast}_decrypt to do same +- add support for fetching full user cert chain from Windows certificate + stores even when there are intermediate CA certs that are not in the + configured ca_cert store (e.g., ROOT) (they could be, e.g., in CA store) +- clean up common.[ch] +- change TLS/crypto library interface to use a structure of function + pointers and helper inline functions (like driver_ops) instead of + requiring every TLS wrapper to implement all functions +- add support for encrypted configuration fields (e.g., password, psk, + passphrase, pin) +- wpa_gui: add support for setting and showing priority +- cleanup TLS/PEAP/TTLS/FAST fragmentation: both the handshake and Appl. Data + phases should be able to use the same functions for this; + the last step in processing sent should be this code and rest of the code + should not need to care about fragmentation at all +- test EAP-FAST peer with OpenSSL and verify that fallback to full handshake + (ServerHello followed by something else than ChangeCipherSpec) diff --git a/wpa_supplicant/win_if_list.c b/wpa_supplicant/win_if_list.c new file mode 100644 index 0000000..0e1532e --- /dev/null +++ b/wpa_supplicant/win_if_list.c @@ -0,0 +1,179 @@ +/* + * win_if_list - Display network interfaces with description (for Windows) + * Copyright (c) 2004-2006, 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 small tool is for the Windows build to provide an easy way of fetching + * a list of available network interfaces. + */ + +#include "includes.h" +#include <stdio.h> +#ifdef CONFIG_USE_NDISUIO +#include <winsock2.h> +#include <ntddndis.h> +#else /* CONFIG_USE_NDISUIO */ +#include "pcap.h" +#include <winsock.h> +#endif /* CONFIG_USE_NDISUIO */ + +#ifdef CONFIG_USE_NDISUIO + +/* from nuiouser.h */ +#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK + +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) + +#define IOCTL_NDISUIO_QUERY_BINDING \ + _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_BIND_WAIT \ + _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _NDISUIO_QUERY_BINDING +{ + ULONG BindingIndex; + ULONG DeviceNameOffset; + ULONG DeviceNameLength; + ULONG DeviceDescrOffset; + ULONG DeviceDescrLength; +} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING; + + +static HANDLE ndisuio_open(void) +{ + DWORD written; + HANDLE h; + + h = CreateFile(TEXT("\\\\.\\\\Ndisuio"), + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + INVALID_HANDLE_VALUE); + if (h == INVALID_HANDLE_VALUE) + return h; + +#ifndef _WIN32_WCE + if (!DeviceIoControl(h, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, NULL, 0, + &written, NULL)) { + printf("IOCTL_NDISUIO_BIND_WAIT failed: %d", + (int) GetLastError()); + CloseHandle(h); + return INVALID_HANDLE_VALUE; + } +#endif /* _WIN32_WCE */ + + return h; +} + + +static void ndisuio_query_bindings(HANDLE ndisuio) +{ + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error; + DWORD written; + char name[256], desc[256]; + WCHAR *pos; + size_t j, len; + + b = malloc(blen); + if (b == NULL) + return; + + for (i = 0; ; i++) { + memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, + (DWORD) blen, &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + printf("IOCTL_NDISUIO_QUERY_BINDING failed: %d", + error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + printf("ifname: %s\ndescription: %s\n\n", name, desc); + } + + free(b); +} + + +static void ndisuio_enum_bindings(void) +{ + HANDLE ndisuio = ndisuio_open(); + if (ndisuio == INVALID_HANDLE_VALUE) + return; + + ndisuio_query_bindings(ndisuio); + CloseHandle(ndisuio); +} + +#else /* CONFIG_USE_NDISUIO */ + +static void show_dev(pcap_if_t *dev) +{ + printf("ifname: %s\ndescription: %s\n\n", + dev->name, dev->description); +} + + +static void pcap_enum_devs(void) +{ + pcap_if_t *devs, *dev; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + fprintf(stderr, "Error - pcap_findalldevs: %s\n", err); + return; + } + + for (dev = devs; dev; dev = dev->next) { + show_dev(dev); + } + + pcap_freealldevs(devs); +} + +#endif /* CONFIG_USE_NDISUIO */ + + +int main(int argc, char *argv[]) +{ +#ifdef CONFIG_USE_NDISUIO + ndisuio_enum_bindings(); +#else /* CONFIG_USE_NDISUIO */ + pcap_enum_devs(); +#endif /* CONFIG_USE_NDISUIO */ + + return 0; +} diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c new file mode 100644 index 0000000..a1db53a --- /dev/null +++ b/wpa_supplicant/wpa_cli.c @@ -0,0 +1,3296 @@ +/* + * WPA Supplicant - command line interface for wpa_supplicant daemon + * Copyright (c) 2004-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. + */ + +#include "includes.h" + +#ifdef CONFIG_CTRL_IFACE + +#ifdef CONFIG_CTRL_IFACE_UNIX +#include <dirent.h> +#endif /* CONFIG_CTRL_IFACE_UNIX */ + +#include "common/wpa_ctrl.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/edit.h" +#include "common/version.h" +#ifdef ANDROID +#include <cutils/properties.h> +#endif /* ANDROID */ + + +static const char *wpa_cli_version = +"wpa_cli v" VERSION_STR "\n" +"Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> and contributors"; + + +static const char *wpa_cli_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n"; + +static const char *wpa_cli_full_license = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + +static struct wpa_ctrl *ctrl_conn; +static struct wpa_ctrl *mon_conn; +static int wpa_cli_quit = 0; +static int wpa_cli_attached = 0; +static int wpa_cli_connected = 0; +static int wpa_cli_last_id = 0; +#ifndef CONFIG_CTRL_IFACE_DIR +#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant" +#endif /* CONFIG_CTRL_IFACE_DIR */ +static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; +static char *ctrl_ifname = NULL; +static const char *pid_file = NULL; +static const char *action_file = NULL; +static int ping_interval = 5; +static int interactive = 0; + + +static void print_help(void); +static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx); + + +static void usage(void) +{ + printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] " + "[-a<action file>] \\\n" + " [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] " + "[command..]\n" + " -h = help (show this usage text)\n" + " -v = shown version information\n" + " -a = run in daemon mode executing the action file based on " + "events from\n" + " wpa_supplicant\n" + " -B = run a daemon in the background\n" + " default path: " CONFIG_CTRL_IFACE_DIR "\n" + " default interface: first interface found in socket path\n"); + print_help(); +} + + +static int str_starts(const char *src, const char *match) +{ + return os_strncmp(src, match, os_strlen(match)) == 0; +} + + +static int wpa_cli_show_event(const char *event) +{ + const char *start; + + start = os_strchr(event, '>'); + if (start == NULL) + return 1; + + start++; + /* + * Skip BSS added/removed events since they can be relatively frequent + * and are likely of not much use for an interactive user. + */ + if (str_starts(start, WPA_EVENT_BSS_ADDED) || + str_starts(start, WPA_EVENT_BSS_REMOVED)) + return 0; + + return 1; +} + + +static int wpa_cli_open_connection(const char *ifname, int attach) +{ +#if defined(CONFIG_CTRL_IFACE_UDP) || defined(CONFIG_CTRL_IFACE_NAMED_PIPE) + ctrl_conn = wpa_ctrl_open(ifname); + if (ctrl_conn == NULL) + return -1; + + if (attach && interactive) + mon_conn = wpa_ctrl_open(ifname); + else + mon_conn = NULL; +#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */ + char *cfile = NULL; + int flen, res; + + if (ifname == NULL) + return -1; + +#ifdef ANDROID + if (access(ctrl_iface_dir, F_OK) < 0) { + cfile = os_strdup(ifname); + if (cfile == NULL) + return -1; + } +#endif /* ANDROID */ + + if (cfile == NULL) { + flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2; + cfile = os_malloc(flen); + if (cfile == NULL) + return -1; + res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, + ifname); + if (res < 0 || res >= flen) { + os_free(cfile); + return -1; + } + } + + ctrl_conn = wpa_ctrl_open(cfile); + if (ctrl_conn == NULL) { + os_free(cfile); + return -1; + } + + if (attach && interactive) + mon_conn = wpa_ctrl_open(cfile); + else + mon_conn = NULL; + os_free(cfile); +#endif /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */ + + if (mon_conn) { + if (wpa_ctrl_attach(mon_conn) == 0) { + wpa_cli_attached = 1; + if (interactive) + eloop_register_read_sock( + wpa_ctrl_get_fd(mon_conn), + wpa_cli_mon_receive, NULL, NULL); + } else { + printf("Warning: Failed to attach to " + "wpa_supplicant.\n"); + return -1; + } + } + + return 0; +} + + +static void wpa_cli_close_connection(void) +{ + if (ctrl_conn == NULL) + return; + + if (wpa_cli_attached) { + wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn); + wpa_cli_attached = 0; + } + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + if (mon_conn) { + eloop_unregister_read_sock(wpa_ctrl_get_fd(mon_conn)); + wpa_ctrl_close(mon_conn); + mon_conn = NULL; + } +} + + +static void wpa_cli_msg_cb(char *msg, size_t len) +{ + printf("%s\n", msg); +} + + +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) +{ + char buf[2048]; + size_t len; + int ret; + + if (ctrl_conn == NULL) { + printf("Not connected to wpa_supplicant - command dropped.\n"); + return -1; + } + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, + wpa_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + if (print) { + buf[len] = '\0'; + printf("%s", buf); + if (interactive && len > 0 && buf[len - 1] != '\n') + printf("\n"); + } + return 0; +} + + +static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) +{ + return _wpa_ctrl_command(ctrl, cmd, 1); +} + + +static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0; + return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS"); +} + + +static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PING"); +} + + +static int wpa_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOG"); +} + + +static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int ret; + if (argc == 0) + return -1; + ret = os_snprintf(cmd, sizeof(cmd), "NOTE %s", argv[0]); + if (ret < 0 || (size_t) ret >= sizeof(cmd)) + return -1; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "MIB"); +} + + +static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA"); +} + + +static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + print_help(); + return 0; +} + + +static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license); + return 0; +} + + +static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + wpa_cli_quit = 1; + if (interactive) + eloop_terminate(); + return 0; +} + + +static void wpa_cli_show_variables(void) +{ + printf("set variables:\n" + " EAPOL::heldPeriod (EAPOL state machine held period, " + "in seconds)\n" + " EAPOL::authPeriod (EAPOL state machine authentication " + "period, in seconds)\n" + " EAPOL::startPeriod (EAPOL state machine start period, in " + "seconds)\n" + " EAPOL::maxStart (EAPOL state machine maximum start " + "attempts)\n"); + printf(" dot11RSNAConfigPMKLifetime (WPA/WPA2 PMK lifetime in " + "seconds)\n" + " dot11RSNAConfigPMKReauthThreshold (WPA/WPA2 reauthentication" + " threshold\n\tpercentage)\n" + " dot11RSNAConfigSATimeout (WPA/WPA2 timeout for completing " + "security\n\tassociation in seconds)\n"); +} + + +static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + wpa_cli_show_variables(); + return 0; + } + + if (argc != 2) { + printf("Invalid SET command: needs two arguments (variable " + "name and value)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long SET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid GET command: need one argument (variable " + "name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long GET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LOGOFF"); +} + + +static int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LOGON"); +} + + +static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "REASSOCIATE"); +} + + +static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid PREAUTH command: needs one argument " + "(BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long PREAUTH command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid AP_SCAN command: needs one argument (ap_scan " + "value)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long AP_SCAN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid SCAN_INTERVAL command: needs one argument " + "scan_interval value)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "SCAN_INTERVAL %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long SCAN_INTERVAL command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid BSS_EXPIRE_AGE command: needs one argument " + "(bss_expire_age value)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_AGE %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long BSS_EXPIRE_AGE command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid BSS_EXPIRE_COUNT command: needs one argument " + "(bss_expire_count value)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_COUNT %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long BSS_EXPIRE_COUNT command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid STKSTART command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long STKSTART command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid FT_DS command: needs one argument " + "(Target AP MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long FT_DS command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + /* Any BSSID */ + return wpa_ctrl_command(ctrl, "WPS_PBC"); + } + + /* Specific BSSID */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PBC command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + printf("Invalid WPS_PIN command: need one or two arguments:\n" + "- BSSID: use 'any' to select any\n" + "- PIN: optional, used only with devices that have no " + "display\n"); + return -1; + } + + if (argc == 1) { + /* Use dynamically generated PIN (returned as reply) */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); + } + + /* Use hardcoded PIN from a label */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1 && argc != 2) { + printf("Invalid WPS_CHECK_PIN command: needs one argument:\n" + "- PIN to be verified\n"); + return -1; + } + + if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_CHECK_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_CANCEL"); +} + + +#ifdef CONFIG_WPS_OOB +static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 3 && argc != 4) { + printf("Invalid WPS_OOB command: need three or four " + "arguments:\n" + "- DEV_TYPE: use 'ufd' or 'nfc'\n" + "- PATH: path of OOB device like '/mnt'\n" + "- METHOD: OOB method 'pin-e' or 'pin-r', " + "'cred'\n" + "- DEV_NAME: (only for NFC) device name like " + "'pn531'\n"); + return -1; + } + + if (argc == 3) + res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_OOB command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} +#endif /* CONFIG_WPS_OOB */ + + +static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", + argv[0], argv[1]); + else if (argc == 5 || argc == 6) { + char ssid_hex[2 * 32 + 1]; + char key_hex[2 * 64 + 1]; + int i; + + ssid_hex[0] = '\0'; + for (i = 0; i < 32; i++) { + if (argv[2][i] == '\0') + break; + os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); + } + + key_hex[0] = '\0'; + if (argc == 6) { + for (i = 0; i < 64; i++) { + if (argv[5][i] == '\0') + break; + os_snprintf(&key_hex[i * 2], 3, "%02x", + argv[5][i]); + } + } + + res = os_snprintf(cmd, sizeof(cmd), + "WPS_REG %s %s %s %s %s %s", + argv[0], argv[1], ssid_hex, argv[3], argv[4], + key_hex); + } else { + printf("Invalid WPS_REG command: need two arguments:\n" + "- BSSID of the target AP\n" + "- AP PIN\n"); + printf("Alternatively, six arguments can be used to " + "reconfigure the AP:\n" + "- BSSID of the target AP\n" + "- AP PIN\n" + "- new SSID\n" + "- new auth (OPEN, WPAPSK, WPA2PSK)\n" + "- new encr (NONE, WEP, TKIP, CCMP)\n" + "- new key\n"); + return -1; + } + + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_REG command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 1) { + printf("Invalid WPS_AP_PIN command: needs at least one " + "argument\n"); + return -1; + } + + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else if (argc > 1) + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_AP_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + if (argc > 0) { + os_snprintf(cmd, sizeof(cmd), "WPS_ER_START %s", argv[0]); + return wpa_ctrl_command(ctrl, cmd); + } + return wpa_ctrl_command(ctrl, "WPS_ER_START"); +} + + +static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_ER_STOP"); + +} + + +static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 2) { + printf("Invalid WPS_ER_PIN command: need at least two " + "arguments:\n" + "- UUID: use 'any' to select any\n" + "- PIN: Enrollee PIN\n" + "optional: - Enrollee MAC address\n"); + return -1; + } + + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_ER_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid WPS_ER_PBC command: need one argument:\n" + "- UUID: Specify the Enrollee\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_ER_PBC command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 2) { + printf("Invalid WPS_ER_LEARN command: need two arguments:\n" + "- UUID: specify which AP to use\n" + "- PIN: AP PIN\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_ER_LEARN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 2) { + printf("Invalid WPS_ER_SET_CONFIG command: need two " + "arguments:\n" + "- UUID: specify which AP to use\n" + "- Network configuration id\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_SET_CONFIG %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_ER_SET_CONFIG command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 5 || argc == 6) { + char ssid_hex[2 * 32 + 1]; + char key_hex[2 * 64 + 1]; + int i; + + ssid_hex[0] = '\0'; + for (i = 0; i < 32; i++) { + if (argv[2][i] == '\0') + break; + os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); + } + + key_hex[0] = '\0'; + if (argc == 6) { + for (i = 0; i < 64; i++) { + if (argv[5][i] == '\0') + break; + os_snprintf(&key_hex[i * 2], 3, "%02x", + argv[5][i]); + } + } + + res = os_snprintf(cmd, sizeof(cmd), + "WPS_ER_CONFIG %s %s %s %s %s %s", + argv[0], argv[1], ssid_hex, argv[3], argv[4], + key_hex); + } else { + printf("Invalid WPS_ER_CONFIG command: need six arguments:\n" + "- AP UUID\n" + "- AP PIN\n" + "- new SSID\n" + "- new auth (OPEN, WPAPSK, WPA2PSK)\n" + "- new encr (NONE, WEP, TKIP, CCMP)\n" + "- new key\n"); + return -1; + } + + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_ER_CONFIG command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid IBSS_RSN command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long IBSS_RSN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid LEVEL command: needs one argument (debug " + "level)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long LEVEL command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid IDENTITY command: needs two arguments " + "(network id and identity)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long IDENTITY 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 IDENTITY command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid PASSWORD command: needs two arguments " + "(network id and password)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PASSWORD 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 PASSWORD command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid NEW_PASSWORD command: needs two arguments " + "(network id and password)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long NEW_PASSWORD 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 NEW_PASSWORD command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid PIN command: needs two arguments " + "(network id and pin)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PIN 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 PIN command.\n"); + return -1; + } + pos += ret; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid OTP command: needs two arguments (network " + "id and password)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long OTP 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 OTP 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[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid PASSPHRASE command: needs two arguments " + "(network id and passphrase)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long PASSPHRASE 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 PASSPHRASE command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid BSSID command: needs two arguments (network " + "id and BSSID)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "BSSID"); + if (ret < 0 || ret >= end - pos) { + printf("Too long BSSID command.\n"); + return -1; + } + pos += ret; + for (i = 0; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long BSSID command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LIST_NETWORKS"); +} + + +static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[32]; + int res; + + if (argc < 1) { + printf("Invalid SELECT_NETWORK command: needs one argument " + "(network id)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[32]; + int res; + + if (argc < 1) { + printf("Invalid ENABLE_NETWORK command: needs one argument " + "(network id)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[32]; + int res; + + if (argc < 1) { + printf("Invalid DISABLE_NETWORK command: needs one argument " + "(network id)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ADD_NETWORK"); +} + + +static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[32]; + int res; + + if (argc < 1) { + printf("Invalid REMOVE_NETWORK command: needs one argument " + "(network id)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static void wpa_cli_show_network_variables(void) +{ + printf("set_network variables:\n" + " ssid (network name, SSID)\n" + " psk (WPA passphrase or pre-shared key)\n" + " key_mgmt (key management protocol)\n" + " identity (EAP identity)\n" + " password (EAP password)\n" + " ...\n" + "\n" + "Note: Values are entered in the same format as the " + "configuration file is using,\n" + "i.e., strings values need to be inside double quotation " + "marks.\n" + "For example: set_network 1 ssid \"network name\"\n" + "\n" + "Please see wpa_supplicant.conf documentation for full list " + "of\navailable variables.\n"); +} + + +static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + wpa_cli_show_network_variables(); + return 0; + } + + if (argc != 3) { + printf("Invalid SET_NETWORK command: needs three arguments\n" + "(network id, variable name, and value)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s", + argv[0], argv[1], argv[2]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long SET_NETWORK command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + wpa_cli_show_network_variables(); + return 0; + } + + if (argc != 2) { + printf("Invalid GET_NETWORK command: needs two arguments\n" + "(network id and variable name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long GET_NETWORK command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DISCONNECT"); +} + + +static int wpa_cli_cmd_reconnect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RECONNECT"); +} + + +static int wpa_cli_cmd_save_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SAVE_CONFIG"); +} + + +static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SCAN"); +} + + +static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SCAN_RESULTS"); +} + + +static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 1) { + printf("Invalid BSS command: need one argument (index or " + "BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "BSS %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[64]; + int res; + + if (argc < 1 || argc > 2) { + printf("Invalid GET_CAPABILITY command: need either one or " + "two arguments\n"); + return -1; + } + + if ((argc == 2) && os_strcmp(argv[1], "strict") != 0) { + printf("Invalid GET_CAPABILITY command: second argument, " + "if any, must be 'strict'\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0], + (argc == 2) ? " strict" : ""); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl) +{ + printf("Available interfaces:\n"); + return wpa_ctrl_command(ctrl, "INTERFACES"); +} + + +static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc < 1) { + wpa_cli_list_interfaces(ctrl); + return 0; + } + + wpa_cli_close_connection(); + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(argv[0]); + + if (wpa_cli_open_connection(ctrl_ifname, 1)) { + printf("Connected to interface '%s.\n", ctrl_ifname); + } else { + printf("Could not connect to interface '%s' - re-trying\n", + ctrl_ifname); + } + return 0; +} + + +static int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RECONFIGURE"); +} + + +static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "TERMINATE"); +} + + +static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 1) { + printf("Invalid INTERFACE_ADD command: needs at least one " + "argument (interface name)\n" + "All arguments: ifname confname driver ctrl_interface " + "driver_param bridge_name\n"); + return -1; + } + + /* + * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB + * <driver_param>TAB<bridge_name> + */ + res = os_snprintf(cmd, sizeof(cmd), + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s", + argv[0], + argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", + argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", + argc > 5 ? argv[5] : ""); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid INTERFACE_REMOVE command: needs one argument " + "(interface name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "INTERFACE_LIST"); +} + + +#ifdef CONFIG_AP +static int wpa_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 " + "address, is required.\n"); + return -1; + } + os_snprintf(buf, sizeof(buf), "STA %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, + char *addr, size_t addr_len) +{ + char buf[4096], *pos; + size_t len; + int ret; + + if (ctrl_conn == NULL) { + printf("Not connected to hostapd - command dropped.\n"); + return -1; + } + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + wpa_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + + buf[len] = '\0'; + if (memcmp(buf, "FAIL", 4) == 0) + return -1; + printf("%s", buf); + + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + *pos = '\0'; + os_strlcpy(addr, buf, addr_len); + return 0; +} + + +static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char addr[32], cmd[64]; + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) + return 0; + do { + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); + + return -1; +} +#endif /* CONFIG_AP */ + + +static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SUSPEND"); +} + + +static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RESUME"); +} + + +static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DROP_SA"); +} + + +static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid ROAM command: needs one argument " + "(target AP's BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long ROAM command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +#ifdef CONFIG_P2P + +static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc == 0) + return wpa_ctrl_command(ctrl, "P2P_FIND"); + + if (argc > 1) + res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_STOP_FIND"); +} + + +static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc < 2) { + printf("Invalid P2P_CONNECT command: needs at least two " + "arguments (address and pbc/PIN)\n"); + return -1; + } + + if (argc > 4) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_CONNECT %s %s %s %s %s", + argv[0], argv[1], argv[2], argv[3], + argv[4]); + else if (argc > 3) + res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc == 0) + return wpa_ctrl_command(ctrl, "P2P_LISTEN"); + + res = os_snprintf(cmd, sizeof(cmd), "P2P_LISTEN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid P2P_GROUP_REMOVE command: needs one argument " + "(interface name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc == 0) + return wpa_ctrl_command(ctrl, "P2P_GROUP_ADD"); + + res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 2) { + printf("Invalid P2P_PROV_DISC command: needs two arguments " + "(address and config method\n" + "(display, keypad, or pbc)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_get_passphrase(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_GET_PASSPHRASE"); +} + + +static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 2 && argc != 4) { + printf("Invalid P2P_SERV_DISC_REQ command: needs two " + "arguments (address and TLVs) or four arguments " + "(address, \"upnp\", version, search target " + "(SSDP ST:)\n"); + return -1; + } + + if (argc == 4) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERV_DISC_REQ %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid P2P_SERV_DISC_CANCEL_REQ command: needs one " + "argument (pending request identifier)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_CANCEL_REQ %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 4) { + printf("Invalid P2P_SERV_DISC_RESP command: needs four " + "arguments (freq, address, dialog token, and TLVs)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_service_update(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_SERVICE_UPDATE"); +} + + +static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid P2P_SERV_DISC_EXTERNAL command: needs one " + "argument (external processing: 0/1)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_EXTERNAL %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_SERVICE_FLUSH"); +} + + +static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 3 && argc != 4) { + printf("Invalid P2P_SERVICE_ADD command: needs three or four " + "arguments\n"); + return -1; + } + + if (argc == 4) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERVICE_ADD %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERVICE_ADD %s %s %s", + argv[0], argv[1], argv[2]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 2 && argc != 3) { + printf("Invalid P2P_SERVICE_DEL command: needs two or three " + "arguments\n"); + return -1; + } + + if (argc == 3) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERVICE_DEL %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERVICE_DEL %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid P2P_REJECT command: needs one argument " + "(peer address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_REJECT %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc < 1) { + printf("Invalid P2P_INVITE command: needs at least one " + "argument\n"); + return -1; + } + + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s %s", + argv[0], argv[1], argv[2]); + else if (argc > 1) + res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'p2p_peer' command - exactly one argument, " + "P2P peer device address, is required.\n"); + return -1; + } + os_snprintf(buf, sizeof(buf), "P2P_PEER %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd, + char *addr, size_t addr_len, + int discovered) +{ + char buf[4096], *pos; + size_t len; + int ret; + + if (ctrl_conn == NULL) + return -1; + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + wpa_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + + buf[len] = '\0'; + if (memcmp(buf, "FAIL", 4) == 0) + return -1; + + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + *pos++ = '\0'; + os_strlcpy(addr, buf, addr_len); + if (!discovered || os_strstr(pos, "[PROBE_REQ_ONLY]") == NULL) + printf("%s\n", addr); + return 0; +} + + +static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char addr[32], cmd[64]; + int discovered; + + discovered = argc > 0 && os_strcmp(argv[0], "discovered") == 0; + + if (wpa_ctrl_command_p2p_peer(ctrl, "P2P_PEER FIRST", + addr, sizeof(addr), discovered)) + return 0; + do { + os_snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr); + } while (wpa_ctrl_command_p2p_peer(ctrl, cmd, addr, sizeof(addr), + discovered) == 0); + + return -1; +} + + +static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 2) { + printf("Invalid P2P_SET command: needs two arguments (field, " + "value)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SET %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_FLUSH"); +} + + +static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_CANCEL"); +} + + +static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 1) { + printf("Invalid P2P_UNAUTHORIZE command: needs one argument " + "(peer address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_UNAUTHORIZE %s", argv[0]); + + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 0 && argc != 2 && argc != 4) { + printf("Invalid P2P_PRESENCE_REQ command: needs two arguments " + "(preferred duration, interval; in microsecods).\n" + "Optional second pair can be used to provide " + "acceptable values.\n"); + return -1; + } + + if (argc == 4) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_PRESENCE_REQ %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ"); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 0 && argc != 2) { + printf("Invalid P2P_EXT_LISTEN command: needs two arguments " + "(availability period, availability interval; in " + "millisecods).\n" + "Extended Listen Timing can be cancelled with this " + "command when used without parameters.\n"); + return -1; + } + + if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN"); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + +#endif /* CONFIG_P2P */ + + +static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid STA_AUTOCONNECT command: needs one argument " + "(0/1 = disable/enable automatic reconnection)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "STA_AUTOCONNECT %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long STA_AUTOCONNECT command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid TDLS_DISCOVER command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "TDLS_DISCOVER %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long TDLS_DISCOVER command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid TDLS_SETUP command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "TDLS_SETUP %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long TDLS_SETUP command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid TDLS_TEARDOWN command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "TDLS_TEARDOWN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long TDLS_TEARDOWN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SIGNAL_POLL"); +} + + +enum wpa_cli_cmd_flags { + cli_cmd_flag_none = 0x00, + cli_cmd_flag_sensitive = 0x01 +}; + +struct wpa_cli_cmd { + const char *cmd; + int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); + enum wpa_cli_cmd_flags flags; + const char *usage; +}; + +static struct wpa_cli_cmd wpa_cli_commands[] = { + { "status", wpa_cli_cmd_status, + cli_cmd_flag_none, + "[verbose] = get current WPA/EAPOL/EAP status" }, + { "ping", wpa_cli_cmd_ping, + cli_cmd_flag_none, + "= pings wpa_supplicant" }, + { "relog", wpa_cli_cmd_relog, + cli_cmd_flag_none, + "= re-open log-file (allow rolling logs)" }, + { "note", wpa_cli_cmd_note, + cli_cmd_flag_none, + "<text> = add a note to wpa_supplicant debug log" }, + { "mib", wpa_cli_cmd_mib, + cli_cmd_flag_none, + "= get MIB variables (dot1x, dot11)" }, + { "help", wpa_cli_cmd_help, + cli_cmd_flag_none, + "= show this usage help" }, + { "interface", wpa_cli_cmd_interface, + cli_cmd_flag_none, + "[ifname] = show interfaces/select interface" }, + { "level", wpa_cli_cmd_level, + cli_cmd_flag_none, + "<debug level> = change debug level" }, + { "license", wpa_cli_cmd_license, + cli_cmd_flag_none, + "= show full wpa_cli license" }, + { "quit", wpa_cli_cmd_quit, + cli_cmd_flag_none, + "= exit wpa_cli" }, + { "set", wpa_cli_cmd_set, + cli_cmd_flag_none, + "= set variables (shows list of variables when run without " + "arguments)" }, + { "get", wpa_cli_cmd_get, + cli_cmd_flag_none, + "<name> = get information" }, + { "logon", wpa_cli_cmd_logon, + cli_cmd_flag_none, + "= IEEE 802.1X EAPOL state machine logon" }, + { "logoff", wpa_cli_cmd_logoff, + cli_cmd_flag_none, + "= IEEE 802.1X EAPOL state machine logoff" }, + { "pmksa", wpa_cli_cmd_pmksa, + cli_cmd_flag_none, + "= show PMKSA cache" }, + { "reassociate", wpa_cli_cmd_reassociate, + cli_cmd_flag_none, + "= force reassociation" }, + { "preauthenticate", wpa_cli_cmd_preauthenticate, + cli_cmd_flag_none, + "<BSSID> = force preauthentication" }, + { "identity", wpa_cli_cmd_identity, + cli_cmd_flag_none, + "<network id> <identity> = configure identity for an SSID" }, + { "password", wpa_cli_cmd_password, + cli_cmd_flag_sensitive, + "<network id> <password> = configure password for an SSID" }, + { "new_password", wpa_cli_cmd_new_password, + cli_cmd_flag_sensitive, + "<network id> <password> = change password for an SSID" }, + { "pin", wpa_cli_cmd_pin, + cli_cmd_flag_sensitive, + "<network id> <pin> = configure pin for an SSID" }, + { "otp", wpa_cli_cmd_otp, + cli_cmd_flag_sensitive, + "<network id> <password> = configure one-time-password for an SSID" + }, + { "passphrase", wpa_cli_cmd_passphrase, + cli_cmd_flag_sensitive, + "<network id> <passphrase> = configure private key passphrase\n" + " for an SSID" }, + { "bssid", wpa_cli_cmd_bssid, + cli_cmd_flag_none, + "<network id> <BSSID> = set preferred BSSID for an SSID" }, + { "list_networks", wpa_cli_cmd_list_networks, + cli_cmd_flag_none, + "= list configured networks" }, + { "select_network", wpa_cli_cmd_select_network, + cli_cmd_flag_none, + "<network id> = select a network (disable others)" }, + { "enable_network", wpa_cli_cmd_enable_network, + cli_cmd_flag_none, + "<network id> = enable a network" }, + { "disable_network", wpa_cli_cmd_disable_network, + cli_cmd_flag_none, + "<network id> = disable a network" }, + { "add_network", wpa_cli_cmd_add_network, + cli_cmd_flag_none, + "= add a network" }, + { "remove_network", wpa_cli_cmd_remove_network, + cli_cmd_flag_none, + "<network id> = remove a network" }, + { "set_network", wpa_cli_cmd_set_network, + cli_cmd_flag_sensitive, + "<network id> <variable> <value> = set network variables (shows\n" + " list of variables when run without arguments)" }, + { "get_network", wpa_cli_cmd_get_network, + cli_cmd_flag_none, + "<network id> <variable> = get network variables" }, + { "save_config", wpa_cli_cmd_save_config, + cli_cmd_flag_none, + "= save the current configuration" }, + { "disconnect", wpa_cli_cmd_disconnect, + cli_cmd_flag_none, + "= disconnect and wait for reassociate/reconnect command before\n" + " connecting" }, + { "reconnect", wpa_cli_cmd_reconnect, + cli_cmd_flag_none, + "= like reassociate, but only takes effect if already disconnected" + }, + { "scan", wpa_cli_cmd_scan, + cli_cmd_flag_none, + "= request new BSS scan" }, + { "scan_results", wpa_cli_cmd_scan_results, + cli_cmd_flag_none, + "= get latest scan results" }, + { "bss", wpa_cli_cmd_bss, + cli_cmd_flag_none, + "<<idx> | <bssid>> = get detailed scan result info" }, + { "get_capability", wpa_cli_cmd_get_capability, + cli_cmd_flag_none, + "<eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies" }, + { "reconfigure", wpa_cli_cmd_reconfigure, + cli_cmd_flag_none, + "= force wpa_supplicant to re-read its configuration file" }, + { "terminate", wpa_cli_cmd_terminate, + cli_cmd_flag_none, + "= terminate wpa_supplicant" }, + { "interface_add", wpa_cli_cmd_interface_add, + cli_cmd_flag_none, + "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n" + " <bridge_name> = adds new interface, all parameters but <ifname>\n" + " are optional" }, + { "interface_remove", wpa_cli_cmd_interface_remove, + cli_cmd_flag_none, + "<ifname> = removes the interface" }, + { "interface_list", wpa_cli_cmd_interface_list, + cli_cmd_flag_none, + "= list available interfaces" }, + { "ap_scan", wpa_cli_cmd_ap_scan, + cli_cmd_flag_none, + "<value> = set ap_scan parameter" }, + { "scan_interval", wpa_cli_cmd_scan_interval, + cli_cmd_flag_none, + "<value> = set scan_interval parameter (in seconds)" }, + { "bss_expire_age", wpa_cli_cmd_bss_expire_age, + cli_cmd_flag_none, + "<value> = set BSS expiration age parameter" }, + { "bss_expire_count", wpa_cli_cmd_bss_expire_count, + cli_cmd_flag_none, + "<value> = set BSS expiration scan count parameter" }, + { "stkstart", wpa_cli_cmd_stkstart, + cli_cmd_flag_none, + "<addr> = request STK negotiation with <addr>" }, + { "ft_ds", wpa_cli_cmd_ft_ds, + cli_cmd_flag_none, + "<addr> = request over-the-DS FT with <addr>" }, + { "wps_pbc", wpa_cli_cmd_wps_pbc, + cli_cmd_flag_none, + "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" }, + { "wps_pin", wpa_cli_cmd_wps_pin, + cli_cmd_flag_sensitive, + "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not " + "hardcoded)" }, + { "wps_check_pin", wpa_cli_cmd_wps_check_pin, + cli_cmd_flag_sensitive, + "<PIN> = verify PIN checksum" }, + { "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none, + "Cancels the pending WPS operation" }, +#ifdef CONFIG_WPS_OOB + { "wps_oob", wpa_cli_cmd_wps_oob, + cli_cmd_flag_sensitive, + "<DEV_TYPE> <PATH> <METHOD> [DEV_NAME] = start WPS OOB" }, +#endif /* CONFIG_WPS_OOB */ + { "wps_reg", wpa_cli_cmd_wps_reg, + cli_cmd_flag_sensitive, + "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" }, + { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, + cli_cmd_flag_sensitive, + "[params..] = enable/disable AP PIN" }, + { "wps_er_start", wpa_cli_cmd_wps_er_start, + cli_cmd_flag_none, + "[IP address] = start Wi-Fi Protected Setup External Registrar" }, + { "wps_er_stop", wpa_cli_cmd_wps_er_stop, + cli_cmd_flag_none, + "= stop Wi-Fi Protected Setup External Registrar" }, + { "wps_er_pin", wpa_cli_cmd_wps_er_pin, + cli_cmd_flag_sensitive, + "<UUID> <PIN> = add an Enrollee PIN to External Registrar" }, + { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, + cli_cmd_flag_none, + "<UUID> = accept an Enrollee PBC using External Registrar" }, + { "wps_er_learn", wpa_cli_cmd_wps_er_learn, + cli_cmd_flag_sensitive, + "<UUID> <PIN> = learn AP configuration" }, + { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, + cli_cmd_flag_none, + "<UUID> <network id> = set AP configuration for enrolling" }, + { "wps_er_config", wpa_cli_cmd_wps_er_config, + cli_cmd_flag_sensitive, + "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" }, + { "ibss_rsn", wpa_cli_cmd_ibss_rsn, + cli_cmd_flag_none, + "<addr> = request RSN authentication with <addr> in IBSS" }, +#ifdef CONFIG_AP + { "sta", wpa_cli_cmd_sta, + cli_cmd_flag_none, + "<addr> = get information about an associated station (AP)" }, + { "all_sta", wpa_cli_cmd_all_sta, + cli_cmd_flag_none, + "= get information about all associated stations (AP)" }, +#endif /* CONFIG_AP */ + { "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none, + "= notification of suspend/hibernate" }, + { "resume", wpa_cli_cmd_resume, cli_cmd_flag_none, + "= notification of resume/thaw" }, + { "drop_sa", wpa_cli_cmd_drop_sa, cli_cmd_flag_none, + "= drop SA without deauth/disassoc (test command)" }, + { "roam", wpa_cli_cmd_roam, + cli_cmd_flag_none, + "<addr> = roam to the specified BSS" }, +#ifdef CONFIG_P2P + { "p2p_find", wpa_cli_cmd_p2p_find, cli_cmd_flag_none, + "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" }, + { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, cli_cmd_flag_none, + "= stop P2P Devices search" }, + { "p2p_connect", wpa_cli_cmd_p2p_connect, cli_cmd_flag_none, + "<addr> <\"pbc\"|PIN> = connect to a P2P Devices" }, + { "p2p_listen", wpa_cli_cmd_p2p_listen, cli_cmd_flag_none, + "[timeout] = listen for P2P Devices for up-to timeout seconds" }, + { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, cli_cmd_flag_none, + "<ifname> = remove P2P group interface (terminate group if GO)" }, + { "p2p_group_add", wpa_cli_cmd_p2p_group_add, cli_cmd_flag_none, + "= add a new P2P group (local end as GO)" }, + { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, cli_cmd_flag_none, + "<addr> <method> = request provisioning discovery" }, + { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, + cli_cmd_flag_none, + "= get the passphrase for a group (GO only)" }, + { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req, + cli_cmd_flag_none, + "<addr> <TLVs> = schedule service discovery request" }, + { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req, + cli_cmd_flag_none, + "<id> = cancel pending service discovery request" }, + { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, + cli_cmd_flag_none, + "<freq> <addr> <dialog token> <TLVs> = service discovery response" }, + { "p2p_service_update", wpa_cli_cmd_p2p_service_update, + cli_cmd_flag_none, + "= indicate change in local services" }, + { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, + cli_cmd_flag_none, + "<external> = set external processing of service discovery" }, + { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, + cli_cmd_flag_none, + "= remove all stored service entries" }, + { "p2p_service_add", wpa_cli_cmd_p2p_service_add, + cli_cmd_flag_none, + "<bonjour|upnp> <query|version> <response|service> = add a local " + "service" }, + { "p2p_service_del", wpa_cli_cmd_p2p_service_del, + cli_cmd_flag_none, + "<bonjour|upnp> <query|version> [|service] = remove a local " + "service" }, + { "p2p_reject", wpa_cli_cmd_p2p_reject, + cli_cmd_flag_none, + "<addr> = reject connection attempts from a specific peer" }, + { "p2p_invite", wpa_cli_cmd_p2p_invite, + cli_cmd_flag_none, + "<cmd> [peer=addr] = invite peer" }, + { "p2p_peers", wpa_cli_cmd_p2p_peers, cli_cmd_flag_none, + "[discovered] = list known (optionally, only fully discovered) P2P " + "peers" }, + { "p2p_peer", wpa_cli_cmd_p2p_peer, cli_cmd_flag_none, + "<address> = show information about known P2P peer" }, + { "p2p_set", wpa_cli_cmd_p2p_set, cli_cmd_flag_none, + "<field> <value> = set a P2P parameter" }, + { "p2p_flush", wpa_cli_cmd_p2p_flush, cli_cmd_flag_none, + "= flush P2P state" }, + { "p2p_cancel", wpa_cli_cmd_p2p_cancel, cli_cmd_flag_none, + "= cancel P2P group formation" }, + { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, cli_cmd_flag_none, + "<address> = unauthorize a peer" }, + { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, cli_cmd_flag_none, + "[<duration> <interval>] [<duration> <interval>] = request GO " + "presence" }, + { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none, + "[<period> <interval>] = set extended listen timing" }, +#endif /* CONFIG_P2P */ + { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none, + "<0/1> = disable/enable automatic reconnection" }, + { "tdls_discover", wpa_cli_cmd_tdls_discover, + cli_cmd_flag_none, + "<addr> = request TDLS discovery with <addr>" }, + { "tdls_setup", wpa_cli_cmd_tdls_setup, + cli_cmd_flag_none, + "<addr> = request TDLS setup with <addr>" }, + { "tdls_teardown", wpa_cli_cmd_tdls_teardown, + cli_cmd_flag_none, + "<addr> = tear down TDLS with <addr>" }, + { "signal_poll", wpa_cli_cmd_signal_poll, + cli_cmd_flag_none, + "= get signal parameters" }, + { NULL, NULL, cli_cmd_flag_none, NULL } +}; + + +/* + * Prints command usage, lines are padded with the specified string. + */ +static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad) +{ + char c; + size_t n; + + printf("%s%s ", pad, cmd->cmd); + for (n = 0; (c = cmd->usage[n]); n++) { + printf("%c", c); + if (c == '\n') + printf("%s", pad); + } + printf("\n"); +} + + +static void print_help(void) +{ + int n; + printf("commands:\n"); + for (n = 0; wpa_cli_commands[n].cmd; n++) + print_cmd_help(&wpa_cli_commands[n], " "); +} + + +static int wpa_cli_edit_filter_history_cb(void *ctx, const char *cmd) +{ + const char *c, *delim; + int n; + size_t len; + + delim = os_strchr(cmd, ' '); + if (delim) + len = delim - cmd; + else + len = os_strlen(cmd); + + for (n = 0; (c = wpa_cli_commands[n].cmd); n++) { + if (os_strncasecmp(cmd, c, len) == 0 && len == os_strlen(c)) + return (wpa_cli_commands[n].flags & + cli_cmd_flag_sensitive); + } + return 0; +} + + +static char ** wpa_list_cmd_list(void) +{ + char **res; + int i, count; + + count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]); + res = os_zalloc(count * sizeof(char *)); + if (res == NULL) + return NULL; + + for (i = 0; wpa_cli_commands[i].cmd; i++) { + res[i] = os_strdup(wpa_cli_commands[i].cmd); + if (res[i] == NULL) + break; + } + + return res; +} + + +static char ** wpa_cli_cmd_completion(const char *cmd, const char *str, + int pos) +{ + int i; + + for (i = 0; wpa_cli_commands[i].cmd; i++) { + if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) { + edit_clear_line(); + printf("\r%s\n", wpa_cli_commands[i].usage); + edit_redraw(); + break; + } + } + + return NULL; +} + + +static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos) +{ + char **res; + const char *end; + char *cmd; + + end = os_strchr(str, ' '); + if (end == NULL || str + pos < end) + return wpa_list_cmd_list(); + + cmd = os_malloc(pos + 1); + if (cmd == NULL) + return NULL; + os_memcpy(cmd, str, pos); + cmd[end - str] = '\0'; + res = wpa_cli_cmd_completion(cmd, str, pos); + os_free(cmd); + return res; +} + + +static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + struct wpa_cli_cmd *cmd, *match = NULL; + int count; + int ret = 0; + + count = 0; + cmd = wpa_cli_commands; + while (cmd->cmd) { + if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0) + { + match = cmd; + if (os_strcasecmp(cmd->cmd, argv[0]) == 0) { + /* we have an exact match */ + count = 1; + break; + } + count++; + } + cmd++; + } + + if (count > 1) { + printf("Ambiguous command '%s'; possible commands:", argv[0]); + cmd = wpa_cli_commands; + while (cmd->cmd) { + if (os_strncasecmp(cmd->cmd, argv[0], + os_strlen(argv[0])) == 0) { + printf(" %s", cmd->cmd); + } + cmd++; + } + printf("\n"); + ret = 1; + } else if (count == 0) { + printf("Unknown command '%s'\n", argv[0]); + ret = 1; + } else { + ret = match->handler(ctrl, argc - 1, &argv[1]); + } + + return ret; +} + + +static int str_match(const char *a, const char *b) +{ + return os_strncmp(a, b, os_strlen(b)) == 0; +} + + +static int wpa_cli_exec(const char *program, const char *arg1, + const char *arg2) +{ + char *cmd; + size_t len; + int res; + int ret = 0; + + len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3; + cmd = os_malloc(len); + if (cmd == NULL) + return -1; + res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2); + if (res < 0 || (size_t) res >= len) { + os_free(cmd); + return -1; + } + cmd[len - 1] = '\0'; +#ifndef _WIN32_WCE + if (system(cmd) < 0) + ret = -1; +#endif /* _WIN32_WCE */ + os_free(cmd); + + return ret; +} + + +static void wpa_cli_action_process(const char *msg) +{ + const char *pos; + char *copy = NULL, *id, *pos2; + + pos = msg; + if (*pos == '<') { + /* skip priority */ + pos = os_strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + if (str_match(pos, WPA_EVENT_CONNECTED)) { + int new_id = -1; + os_unsetenv("WPA_ID"); + os_unsetenv("WPA_ID_STR"); + os_unsetenv("WPA_CTRL_DIR"); + + pos = os_strstr(pos, "[id="); + if (pos) + copy = os_strdup(pos + 4); + + if (copy) { + pos2 = id = copy; + while (*pos2 && *pos2 != ' ') + pos2++; + *pos2++ = '\0'; + new_id = atoi(id); + os_setenv("WPA_ID", id, 1); + while (*pos2 && *pos2 != '=') + pos2++; + if (*pos2 == '=') + pos2++; + id = pos2; + while (*pos2 && *pos2 != ']') + pos2++; + *pos2 = '\0'; + os_setenv("WPA_ID_STR", id, 1); + os_free(copy); + } + + os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1); + + if (!wpa_cli_connected || new_id != wpa_cli_last_id) { + wpa_cli_connected = 1; + wpa_cli_last_id = new_id; + wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED"); + } + } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) { + if (wpa_cli_connected) { + wpa_cli_connected = 0; + wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED"); + } + } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, WPS_EVENT_SUCCESS)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, WPS_EVENT_FAIL)) { + 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; + } +} + + +#ifndef CONFIG_ANSI_C_EXTRA +static void wpa_cli_action_cb(char *msg, size_t len) +{ + wpa_cli_action_process(msg); +} +#endif /* CONFIG_ANSI_C_EXTRA */ + + +static void wpa_cli_reconnect(void) +{ + wpa_cli_close_connection(); + wpa_cli_open_connection(ctrl_ifname, 1); +} + + +static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) +{ + if (ctrl_conn == NULL) { + wpa_cli_reconnect(); + return; + } + while (wpa_ctrl_pending(ctrl) > 0) { + char buf[256]; + size_t len = sizeof(buf) - 1; + if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { + buf[len] = '\0'; + if (action_monitor) + wpa_cli_action_process(buf); + else { + if (wpa_cli_show_event(buf)) { + edit_clear_line(); + printf("\r%s\n", buf); + edit_redraw(); + } + } + } else { + printf("Could not read pending message.\n"); + break; + } + } + + if (wpa_ctrl_pending(ctrl) < 0) { + printf("Connection to wpa_supplicant lost - trying to " + "reconnect\n"); + wpa_cli_reconnect(); + } +} + +#define max_args 10 + +static int tokenize_cmd(char *cmd, char *argv[]) +{ + char *pos; + int argc = 0; + + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + if (*pos == '"') { + char *pos2 = os_strrchr(pos, '"'); + if (pos2) + pos = pos2 + 1; + } + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } + + return argc; +} + + +static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx) +{ + if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { + printf("Connection to wpa_supplicant lost - trying to " + "reconnect\n"); + wpa_cli_close_connection(); + } + if (!ctrl_conn) + wpa_cli_reconnect(); + eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL); +} + + +static void wpa_cli_eloop_terminate(int sig, void *signal_ctx) +{ + eloop_terminate(); +} + + +static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + wpa_cli_recv_pending(mon_conn, 0); +} + + +static void wpa_cli_edit_cmd_cb(void *ctx, char *cmd) +{ + char *argv[max_args]; + int argc; + argc = tokenize_cmd(cmd, argv); + if (argc) + wpa_request(ctrl_conn, argc, argv); +} + + +static void wpa_cli_edit_eof_cb(void *ctx) +{ + eloop_terminate(); +} + + +static void wpa_cli_interactive(void) +{ + char *home, *hfile = NULL; + + printf("\nInteractive mode\n\n"); + + home = getenv("HOME"); + if (home) { + const char *fname = ".wpa_cli_history"; + int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; + hfile = os_malloc(hfile_len); + if (hfile) + os_snprintf(hfile, hfile_len, "%s/%s", home, fname); + } + + eloop_register_signal_terminate(wpa_cli_eloop_terminate, NULL); + edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb, + wpa_cli_edit_completion_cb, NULL, hfile); + eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL); + + eloop_run(); + + edit_deinit(hfile, wpa_cli_edit_filter_history_cb); + os_free(hfile); + eloop_cancel_timeout(wpa_cli_ping, NULL, NULL); + wpa_cli_close_connection(); +} + + +static void wpa_cli_action(struct wpa_ctrl *ctrl) +{ +#ifdef CONFIG_ANSI_C_EXTRA + /* TODO: ANSI C version(?) */ + printf("Action processing not supported in ANSI C build.\n"); +#else /* CONFIG_ANSI_C_EXTRA */ + fd_set rfds; + int fd, res; + struct timeval tv; + char buf[256]; /* note: large enough to fit in unsolicited messages */ + size_t len; + + fd = wpa_ctrl_get_fd(ctrl); + + while (!wpa_cli_quit) { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = ping_interval; + tv.tv_usec = 0; + res = select(fd + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + break; + } + + if (FD_ISSET(fd, &rfds)) + wpa_cli_recv_pending(ctrl, 1); + else { + /* verify that connection is still working */ + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, + wpa_cli_action_cb) < 0 || + len < 4 || os_memcmp(buf, "PONG", 4) != 0) { + printf("wpa_supplicant did not reply to PING " + "command - exiting\n"); + break; + } + } + } +#endif /* CONFIG_ANSI_C_EXTRA */ +} + + +static void wpa_cli_cleanup(void) +{ + wpa_cli_close_connection(); + if (pid_file) + os_daemonize_terminate(pid_file); + + os_program_deinit(); +} + +static void wpa_cli_terminate(int sig) +{ + wpa_cli_cleanup(); + exit(0); +} + + +static char * wpa_cli_get_default_ifname(void) +{ + char *ifname = NULL; + +#ifdef CONFIG_CTRL_IFACE_UNIX + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + if (!dir) { +#ifdef ANDROID + char ifprop[PROPERTY_VALUE_MAX]; + if (property_get("wifi.interface", ifprop, NULL) != 0) { + ifname = os_strdup(ifprop); + printf("Using interface '%s'\n", ifname); + return ifname; + } +#endif /* ANDROID */ + return NULL; + } + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* + * Skip the file if it is not a socket. Also accept + * DT_UNKNOWN (0) in case the C library or underlying + * file system does not support d_type. + */ + if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (os_strcmp(dent->d_name, ".") == 0 || + os_strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", dent->d_name); + ifname = os_strdup(dent->d_name); + break; + } + closedir(dir); +#endif /* CONFIG_CTRL_IFACE_UNIX */ + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + char buf[2048], *pos; + size_t len; + struct wpa_ctrl *ctrl; + int ret; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl == NULL) + return NULL; + + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL); + if (ret >= 0) { + buf[len] = '\0'; + pos = os_strchr(buf, '\n'); + if (pos) + *pos = '\0'; + ifname = os_strdup(buf); + } + wpa_ctrl_close(ctrl); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + + return ifname; +} + + +int main(int argc, char *argv[]) +{ + int warning_displayed = 0; + int c; + int daemonize = 0; + int ret = 0; + const char *global = NULL; + + if (os_program_init()) + return -1; + + for (;;) { + c = getopt(argc, argv, "a:Bg:G:hi:p:P:v"); + if (c < 0) + break; + switch (c) { + case 'a': + action_file = optarg; + break; + case 'B': + daemonize = 1; + break; + case 'g': + global = optarg; + break; + case 'G': + ping_interval = atoi(optarg); + break; + case 'h': + usage(); + return 0; + case 'v': + printf("%s\n", wpa_cli_version); + return 0; + case 'i': + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(optarg); + break; + case 'p': + ctrl_iface_dir = optarg; + break; + case 'P': + pid_file = optarg; + break; + default: + usage(); + return -1; + } + } + + interactive = (argc == optind) && (action_file == NULL); + + if (interactive) + printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license); + + if (eloop_init()) + return -1; + + if (global) { +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + ctrl_conn = wpa_ctrl_open(NULL); +#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + ctrl_conn = wpa_ctrl_open(global); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + if (ctrl_conn == NULL) { + perror("Failed to connect to wpa_supplicant - " + "wpa_ctrl_open"); + return -1; + } + } + +#ifndef _WIN32_WCE + signal(SIGINT, wpa_cli_terminate); + signal(SIGTERM, wpa_cli_terminate); +#endif /* _WIN32_WCE */ + + if (ctrl_ifname == NULL) + ctrl_ifname = wpa_cli_get_default_ifname(); + + if (interactive) { + for (; !global;) { + if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) { + if (warning_displayed) + printf("Connection established.\n"); + break; + } + + if (!warning_displayed) { + printf("Could not connect to wpa_supplicant - " + "re-trying\n"); + warning_displayed = 1; + } + os_sleep(1, 0); + continue; + } + } else { + if (!global && + wpa_cli_open_connection(ctrl_ifname, 0) < 0) { + perror("Failed to connect to wpa_supplicant - " + "wpa_ctrl_open"); + return -1; + } + + if (action_file) { + if (wpa_ctrl_attach(ctrl_conn) == 0) { + wpa_cli_attached = 1; + } else { + printf("Warning: Failed to attach to " + "wpa_supplicant.\n"); + return -1; + } + } + } + + if (daemonize && os_daemonize(pid_file)) + return -1; + + if (interactive) + wpa_cli_interactive(); + else if (action_file) + wpa_cli_action(ctrl_conn); + else + ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]); + + os_free(ctrl_ifname); + eloop_destroy(); + wpa_cli_cleanup(); + + return ret; +} + +#else /* CONFIG_CTRL_IFACE */ +int main(int argc, char *argv[]) +{ + printf("CONFIG_CTRL_IFACE not defined - wpa_cli disabled\n"); + return -1; +} +#endif /* CONFIG_CTRL_IFACE */ diff --git a/wpa_supplicant/wpa_gui-qt4/.gitignore b/wpa_supplicant/wpa_gui-qt4/.gitignore new file mode 100644 index 0000000..da818cb --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/.gitignore @@ -0,0 +1,4 @@ +.moc +.obj +.ui +qrc_icons.cpp diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.cpp b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp new file mode 100644 index 0000000..88d4603 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp @@ -0,0 +1,245 @@ +/* + * wpa_gui - AddInterface class + * Copyright (c) 2008, 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. + */ + +#include <cstdio> +#include "common/wpa_ctrl.h" + +#include <QMessageBox> + +#include "wpagui.h" +#include "addinterface.h" + +#ifdef CONFIG_NATIVE_WINDOWS +#include <windows.h> + +#ifndef WPA_KEY_ROOT +#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE +#endif +#ifndef WPA_KEY_PREFIX +#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant") +#endif +#endif /* CONFIG_NATIVE_WINDOWS */ + + +AddInterface::AddInterface(WpaGui *_wpagui, QWidget *parent) + : QDialog(parent), wpagui(_wpagui) +{ + setWindowTitle(tr("Select network interface to add")); + resize(400, 200); + vboxLayout = new QVBoxLayout(this); + + interfaceWidget = new QTreeWidget(this); + interfaceWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + interfaceWidget->setUniformRowHeights(true); + interfaceWidget->setSortingEnabled(true); + interfaceWidget->setColumnCount(3); + interfaceWidget->headerItem()->setText(0, tr("driver")); + interfaceWidget->headerItem()->setText(1, tr("interface")); + interfaceWidget->headerItem()->setText(2, tr("description")); + interfaceWidget->setItemsExpandable(FALSE); + interfaceWidget->setRootIsDecorated(FALSE); + vboxLayout->addWidget(interfaceWidget); + + connect(interfaceWidget, + SIGNAL(itemActivated(QTreeWidgetItem *, int)), this, + SLOT(interfaceSelected(QTreeWidgetItem *))); + + addInterfaces(); +} + + +void AddInterface::addInterfaces() +{ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + char buf[2048]; + size_t len; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl == NULL) + return; + + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACE_LIST", 14, buf, &len, NULL); + if (ret < 0) { + wpa_ctrl_close(ctrl); + return; + } + buf[len] = '\0'; + + wpa_ctrl_close(ctrl); + + QString ifaces(buf); + QStringList lines = ifaces.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + QStringList arg = (*it).split(QChar('\t')); + if (arg.size() < 3) + continue; + QTreeWidgetItem *item = new QTreeWidgetItem(interfaceWidget); + if (!item) + break; + + item->setText(0, arg[0]); + item->setText(1, arg[1]); + item->setText(2, arg[2]); + } + + interfaceWidget->resizeColumnToContents(0); + interfaceWidget->resizeColumnToContents(1); + interfaceWidget->resizeColumnToContents(2); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +} + + +#ifdef CONFIG_NATIVE_WINDOWS +bool AddInterface::addRegistryInterface(const QString &ifname) +{ + HKEY hk, ihk; + LONG ret; + int id, tmp; + TCHAR name[10]; + DWORD val, i; + + ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX TEXT("\\interfaces"), + 0, KEY_ENUMERATE_SUB_KEYS | KEY_CREATE_SUB_KEY, + &hk); + if (ret != ERROR_SUCCESS) + return false; + + id = -1; + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) + break; + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + +#ifdef UNICODE + QString s((QChar *) name, namelen); +#else /* UNICODE */ + QString s(name); +#endif /* UNICODE */ + tmp = s.toInt(); + if (tmp > id) + id = tmp; + } + + id += 1; + +#ifdef UNICODE + wsprintf(name, L"%04d", id); +#else /* UNICODE */ + os_snprintf(name, sizeof(name), "%04d", id); +#endif /* UNICODE */ + ret = RegCreateKeyEx(hk, name, 0, NULL, 0, KEY_WRITE, NULL, &ihk, + NULL); + RegCloseKey(hk); + if (ret != ERROR_SUCCESS) + return false; + +#ifdef UNICODE + RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ, + (LPBYTE) ifname.unicode(), + (ifname.length() + 1) * sizeof(TCHAR)); + +#else /* UNICODE */ + RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ, + (LPBYTE) ifname.toLocal8Bit(), ifname.length() + 1); +#endif /* UNICODE */ + RegSetValueEx(ihk, TEXT("config"), 0, REG_SZ, + (LPBYTE) TEXT("default"), 8 * sizeof(TCHAR)); + RegSetValueEx(ihk, TEXT("ctrl_interface"), 0, REG_SZ, + (LPBYTE) TEXT(""), 1 * sizeof(TCHAR)); + val = 1; + RegSetValueEx(ihk, TEXT("skip_on_error"), 0, REG_DWORD, (LPBYTE) &val, + sizeof(val)); + + RegCloseKey(ihk); + return true; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +void AddInterface::interfaceSelected(QTreeWidgetItem *sel) +{ + if (!sel) + return; + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + char buf[20], cmd[256]; + size_t len; + + /* + * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB + * <driver_param>TAB<bridge_name> + */ + snprintf(cmd, sizeof(cmd), + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s", + sel->text(1).toAscii().constData(), + "default", + sel->text(0).toAscii().constData(), + "yes", "", ""); + cmd[sizeof(cmd) - 1] = '\0'; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl == NULL) + return; + + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL); + wpa_ctrl_close(ctrl); + + if (ret < 0) { + QMessageBox::warning(this, "wpa_gui", + tr("Add interface command could not be " + "completed.")); + return; + } + + buf[len] = '\0'; + if (buf[0] != 'O' || buf[1] != 'K') { + QMessageBox::warning(this, "wpa_gui", + tr("Failed to add the interface.")); + return; + } + +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + +#ifdef CONFIG_NATIVE_WINDOWS + if (!addRegistryInterface(sel->text(1))) { + QMessageBox::information(this, "wpa_gui", + tr("Failed to add the interface into " + "registry.")); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + wpagui->selectAdapter(sel->text(1)); + close(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.h b/wpa_supplicant/wpa_gui-qt4/addinterface.h new file mode 100644 index 0000000..9d9476a --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/addinterface.h @@ -0,0 +1,45 @@ +/* + * wpa_gui - AddInterface class + * Copyright (c) 2008, 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. + */ + +#ifndef ADDINTERFACE_H +#define ADDINTERFACE_H + +#include <QObject> + +#include <QtGui/QDialog> +#include <QtGui/QTreeWidget> +#include <QtGui/QVBoxLayout> + +class WpaGui; + +class AddInterface : public QDialog +{ + Q_OBJECT + +public: + AddInterface(WpaGui *_wpagui, QWidget *parent = 0); + +public slots: + virtual void interfaceSelected(QTreeWidgetItem *sel); + +private: + void addInterfaces(); + bool addRegistryInterface(const QString &ifname); + + QVBoxLayout *vboxLayout; + QTreeWidget *interfaceWidget; + WpaGui *wpagui; +}; + +#endif /* ADDINTERFACE_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp new file mode 100644 index 0000000..1eb0b7b --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp @@ -0,0 +1,130 @@ +/* + * wpa_gui - EventHistory class + * Copyright (c) 2005-2006, 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. + */ + +#include <QHeaderView> +#include <QScrollBar> + +#include "eventhistory.h" + + +int EventListModel::rowCount(const QModelIndex &) const +{ + return msgList.count(); +} + + +int EventListModel::columnCount(const QModelIndex &) const +{ + return 2; +} + + +QVariant EventListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole) + if (index.column() == 0) { + if (index.row() >= timeList.size()) + return QVariant(); + return timeList.at(index.row()); + } else { + if (index.row() >= msgList.size()) + return QVariant(); + return msgList.at(index.row()); + } + else + return QVariant(); +} + + +QVariant EventListModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return QString(tr("Timestamp")); + case 1: + return QString(tr("Message")); + default: + return QVariant(); + } + } else + return QString("%1").arg(section); +} + + +void EventListModel::addEvent(QString time, QString msg) +{ + beginInsertRows(QModelIndex(), msgList.size(), msgList.size() + 1); + timeList << time; + msgList << msg; + endInsertRows(); +} + + +EventHistory::EventHistory(QWidget *parent, const char *, bool, Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + + eventListView->setItemsExpandable(FALSE); + eventListView->setRootIsDecorated(FALSE); + elm = new EventListModel(parent); + eventListView->setModel(elm); +} + + +EventHistory::~EventHistory() +{ + destroy(); + delete elm; +} + + +void EventHistory::languageChange() +{ + retranslateUi(this); +} + + +void EventHistory::addEvents(WpaMsgList msgs) +{ + WpaMsgList::iterator it; + for (it = msgs.begin(); it != msgs.end(); it++) + addEvent(*it); +} + + +void EventHistory::addEvent(WpaMsg msg) +{ + bool scroll = true; + + if (eventListView->verticalScrollBar()->value() < + eventListView->verticalScrollBar()->maximum()) + scroll = false; + + elm->addEvent(msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"), + msg.getMsg()); + + if (scroll) + eventListView->scrollToBottom(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.h b/wpa_supplicant/wpa_gui-qt4/eventhistory.h new file mode 100644 index 0000000..40dff6d --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.h @@ -0,0 +1,63 @@ +/* + * wpa_gui - EventHistory class + * Copyright (c) 2005-2006, 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. + */ + +#ifndef EVENTHISTORY_H +#define EVENTHISTORY_H + +#include <QObject> +#include "ui_eventhistory.h" + + +class EventListModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + EventListModel(QObject *parent = 0) + : QAbstractTableModel(parent) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + void addEvent(QString time, QString msg); + +private: + QStringList timeList; + QStringList msgList; +}; + + +class EventHistory : public QDialog, public Ui::EventHistory +{ + Q_OBJECT + +public: + EventHistory(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~EventHistory(); + +public slots: + virtual void addEvents(WpaMsgList msgs); + virtual void addEvent(WpaMsg msg); + +protected slots: + virtual void languageChange(); + +private: + EventListModel *elm; +}; + +#endif /* EVENTHISTORY_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.ui b/wpa_supplicant/wpa_gui-qt4/eventhistory.ui new file mode 100644 index 0000000..afe9149 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.ui @@ -0,0 +1,61 @@ +<ui version="4.0" > + <class>EventHistory</class> + <widget class="QDialog" name="EventHistory" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>533</width> + <height>285</height> + </rect> + </property> + <property name="windowTitle" > + <string>Event history</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="2" > + <widget class="QTreeView" name="eventListView" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="verticalScrollBarPolicy" > + <enum>Qt::ScrollBarAlwaysOn</enum> + </property> + <property name="selectionMode" > + <enum>QAbstractItemView::NoSelection</enum> + </property> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="closeButton" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <includes> + <include location="local" >wpamsg.h</include> + </includes> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/icons.qrc b/wpa_supplicant/wpa_gui-qt4/icons.qrc new file mode 100644 index 0000000..dd72c7e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/icons" > + <file alias="wpa_gui.svg">icons/wpa_gui.svg</file> + <file alias="ap.svg">icons/ap.svg</file> + <file alias="laptop.svg">icons/laptop.svg</file> + <file alias="group.svg">icons/group.svg</file> + <file alias="invitation.svg">icons/invitation.svg</file> + </qresource> +</RCC> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/Makefile b/wpa_supplicant/wpa_gui-qt4/icons/Makefile new file mode 100644 index 0000000..709514c --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/Makefile @@ -0,0 +1,23 @@ +#!/usr/bin/make -f + +NAMES := wpa_gui ap laptop group invitation +SIZES := 16x16 22x22 32x32 48x48 64x64 128x128 +ICONS := $(addsuffix .png, $(foreach name, $(NAMES), $(foreach size, $(SIZES), $(size)/$(name)))) +ICONS += $(addsuffix .xpm, $(NAMES)) + +all: $(ICONS) + +%.png: + mkdir -p hicolor/$(word 1, $(subst /, ,$(@)))/apps/ + inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) --without-gui \ + --export-width=$(word 1, $(subst x, , $(@))) \ + --export-height=$(word 2, $(subst x, , $(subst /, , $(@)))) \ + --export-png=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@)) + +%.xpm: + mkdir -p pixmaps/ + convert hicolor/16x16/apps/$(@:.xpm=.png) pixmaps/$(@:.xpm=-16.xpm) + convert hicolor/32x32/apps/$(@:.xpm=.png) pixmaps/$@ + +clean: + $(RM) -r pixmaps hicolor diff --git a/wpa_supplicant/wpa_gui-qt4/icons/README b/wpa_supplicant/wpa_gui-qt4/icons/README new file mode 100644 index 0000000..3953238 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/README @@ -0,0 +1,74 @@ +wpa_gui icon files + +To convert the svg icons to other formats, make sure inkscape and imagemagick +are installed and use `make' to create various sized png and xpm icons. + + +wpa_gui.svg +----------- + +Copyright (c) 2008 Bernard Gray <bernard.gray@gmail.com> + +The wpa_gui icon is licensed under the GPL version 2. Alternatively, the icon +may be distributed under the terms of BSD license. + + +ap.svg +------ + +mystica_Wireless_Router.svg + +http://openclipart.org/media/files/mystica/8390 +Wireless Router +by: mystica +last change: April 20, 2008 10:32 pm (File added) +date: April 20, 2008 10:31 pm +license: PD + + +laptop.svg +---------- + +metalmarious_Laptop.svg + +http://openclipart.org/media/files/metalmarious/4056 +Laptop +by: metalmarious +last change: May 18, 2008 07:04 pm (File added) +date: August 27, 2007 04:44 am +license: PD + + +group.svg +--------- + +http://www.openclipart.org/detail/25428 +http://www.openclipart.org/people/Anonymous/Anonymous_Network.svg +Uploader: + Anonymous +Drawn by: + Andrew Fitzsimon / Anonymous +Created: + 2009-04-29 04:07:37 +Description: + A network icon by Andrew Fitzsimon. Etiquette Icon set. + From 0.18 OCAL database. + +Public Domain + + + +invitation.svg +-------------- + +http://www.openclipart.org/detail/974 +http://www.openclipart.org/people/jean_victor_balin/jean_victor_balin_unknown_green.svg +Uploader: + jean_victor_balin +Drawn by: + jean_victor_balin +Created: + 2006-10-27 02:12:13 +Description: + +Public Domain diff --git a/wpa_supplicant/wpa_gui-qt4/icons/ap.svg b/wpa_supplicant/wpa_gui-qt4/icons/ap.svg new file mode 100644 index 0000000..51cc8ce --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/ap.svg @@ -0,0 +1,832 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="546" + height="482.67157" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1+0.46pre1+devel" + sodipodi:docname="Wireless Router.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0" + inkscape:export-filename="C:\Documents and Settings\Dan\Skrivbord\Clipart egna (InkScape)\Original\Kanske Upload\Wireless Router.png" + inkscape:export-xdpi="310" + inkscape:export-ydpi="310"> + <defs + id="defs4"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-50 : 600 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="700 : 600 : 1" + inkscape:persp3d-origin="300 : 400 : 1" + id="perspective148" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-50 : 600 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="700 : 600 : 1" + inkscape:persp3d-origin="300 : 400 : 1" + id="perspective138" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-50 : 600 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="700 : 600 : 1" + inkscape:persp3d-origin="300 : 400 : 1" + id="perspective10" /> + <inkscape:perspective + id="perspective2395" + inkscape:persp3d-origin="300 : 400 : 1" + inkscape:vp_z="700 : 600 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="-50 : 600 : 1" + sodipodi:type="inkscape:persp3d" /> + <filter + inkscape:collect="always" + id="filter3304"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.27999283" + id="feGaussianBlur3306" /> + </filter> + <filter + inkscape:collect="always" + id="filter3336"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.5315371" + id="feGaussianBlur3338" /> + </filter> + <filter + inkscape:collect="always" + id="filter3368"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.26473573" + id="feGaussianBlur3370" /> + </filter> + <filter + inkscape:collect="always" + id="filter3564" + x="-0.37202433" + width="1.7440487" + y="-0.43252525" + height="1.8650506"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.1017616" + id="feGaussianBlur3566" /> + </filter> + <filter + inkscape:collect="always" + id="filter3748" + x="-0.41952851" + width="1.839057" + y="-0.39121628" + height="1.7824326"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.1235829" + id="feGaussianBlur3750" /> + </filter> + <filter + inkscape:collect="always" + id="filter3862" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3864" /> + </filter> + <filter + inkscape:collect="always" + id="filter3866" + x="-0.33298156" + width="1.6659631" + y="-0.20756502" + height="1.4151301"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3868" /> + </filter> + <filter + inkscape:collect="always" + id="filter3870" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3872" /> + </filter> + <filter + inkscape:collect="always" + id="filter3874" + x="-0.3380883" + width="1.6761765" + y="-0.21154897" + height="1.4230978"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3876" /> + </filter> + <filter + inkscape:collect="always" + id="filter3878" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3880" /> + </filter> + <filter + inkscape:collect="always" + id="filter3882" + x="-0.36018598" + width="1.720372" + y="-0.20953795" + height="1.4190758"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3884" /> + </filter> + <filter + inkscape:collect="always" + id="filter3886" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3888" /> + </filter> + <filter + inkscape:collect="always" + id="filter3890" + x="-0.35439494" + width="1.7087899" + y="-0.20953795" + height="1.4190758"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3892" /> + </filter> + <filter + inkscape:collect="always" + id="filter3894" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3896" /> + </filter> + <filter + inkscape:collect="always" + id="filter3898" + x="-0.38537359" + width="1.7707472" + y="-0.20562869" + height="1.4112574"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3900" /> + </filter> + <filter + inkscape:collect="always" + id="filter3902" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3904" /> + </filter> + <filter + inkscape:collect="always" + id="filter3906" + x="-0.38537359" + width="1.7707472" + y="-0.21359873" + height="1.4271975"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3908" /> + </filter> + <filter + inkscape:collect="always" + id="filter3910" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3912" /> + </filter> + <filter + inkscape:collect="always" + id="filter3914" + x="-0.36018598" + width="1.720372" + y="-0.20562869" + height="1.4112574"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3916" /> + </filter> + <filter + inkscape:collect="always" + id="filter3918" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3920" /> + </filter> + <filter + inkscape:collect="always" + id="filter3922" + x="-0.36616942" + width="1.7323389" + y="-0.20953795" + height="1.4190758"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3924" /> + </filter> + <filter + inkscape:collect="always" + id="filter3926" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3928" /> + </filter> + <filter + inkscape:collect="always" + id="filter3930" + x="-0.34878778" + width="1.6975756" + y="-0.20186263" + height="1.4037253"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3932" /> + </filter> + <filter + inkscape:collect="always" + id="filter3934" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3936" /> + </filter> + <filter + inkscape:collect="always" + id="filter3938" + x="-0.32802677" + width="1.6560535" + y="-0.21568884" + height="1.4313776"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3940" /> + </filter> + <filter + inkscape:collect="always" + id="filter3942" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3944" /> + </filter> + <filter + inkscape:collect="always" + id="filter3946" + x="-0.32321677" + width="1.6464336" + y="-0.21568884" + height="1.4313776"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3948" /> + </filter> + <filter + inkscape:collect="always" + id="filter3950" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3952" /> + </filter> + <filter + inkscape:collect="always" + id="filter3954" + x="-0.3185463" + width="1.6370926" + y="-0.21359873" + height="1.4271975"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3956" /> + </filter> + <filter + inkscape:collect="always" + id="filter3958" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3960" /> + </filter> + <filter + inkscape:collect="always" + id="filter3962" + x="-0.28553614" + width="1.5710723" + y="-0.21568884" + height="1.4313776"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3964" /> + </filter> + <filter + inkscape:collect="always" + id="filter3982" + x="-0.0048889387" + width="1.0097779" + y="-0.26385465" + height="1.5277092"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.0547249" + id="feGaussianBlur3984" /> + </filter> + <filter + inkscape:collect="always" + id="filter3996"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.8032234" + id="feGaussianBlur3998" /> + </filter> + <filter + inkscape:collect="always" + id="filter3517" + x="-0.25713229" + width="1.5142646" + y="-0.087099633" + height="1.1741993"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.33984317" + id="feGaussianBlur3519" /> + </filter> + <filter + inkscape:collect="always" + id="filter3329" + x="-0.18025071" + width="1.3605014" + y="-1.1780664" + height="3.3561328"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.49086099" + id="feGaussianBlur3331" /> + </filter> + <filter + inkscape:collect="always" + id="filter3333" + x="-0.15131117" + width="1.3026223" + y="-0.1853139" + height="1.3706278"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.49086099" + id="feGaussianBlur3335" /> + </filter> + <filter + inkscape:collect="always" + id="filter3337" + x="-0.14412392" + width="1.2882478" + y="-0.18013415" + height="1.3602683"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.49086099" + id="feGaussianBlur3339" /> + </filter> + <filter + inkscape:collect="always" + id="filter3341" + x="-1.1780664" + width="3.3561328" + y="-0.23067047" + height="1.4613409"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.49086099" + id="feGaussianBlur3343" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.98994949" + inkscape:cx="233.05018" + inkscape:cy="176.49031" + inkscape:document-units="px" + inkscape:current-layer="layer2" + showgrid="false" + inkscape:window-width="1152" + inkscape:window-height="838" + inkscape:window-x="0" + inkscape:window-y="0" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="layer2" + transform="translate(-45.788597,-496.6196)"> + <path + style="fill:#606060;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 75.574315,977.86259 L 535.56828,979.20564 L 564.86002,979.29116 C 564.86002,979.29116 573.43146,977.86259 573.43146,973.57687 C 573.43146,969.29116 574.14573,959.29117 574.14573,959.29117 L 566.03832,959.7296 L 72.464255,956.66717 L 58.640665,955.63307 C 58.640665,955.63307 59.860025,973.57688 65.574315,975.71973 C 71.288595,977.86259 75.574315,977.14831 75.574315,977.86259 z" + id="path2402" + sodipodi:nodetypes="cccsccccsc" /> + <path + style="fill:#dddddd;fill-opacity:1;fill-rule:evenodd;stroke:#b2b2b2;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 67.002885,957.14831 L 566.28859,960.00545 C 566.28859,960.00545 577.71717,959.29116 583.43146,955.71973 C 589.14574,952.14831 588.43146,942.86259 588.43146,942.86259 C 588.43146,942.86259 591.28859,907.14831 591.28859,902.14831 C 591.28859,897.14831 574.86002,839.29116 572.00288,827.86259 C 569.14574,816.43402 557.00288,757.1483 557.00288,757.1483 C 557.00288,757.1483 555.57431,749.29116 552.71717,748.57688 C 549.86002,747.86259 548.43146,748.57688 548.43146,748.57688 L 558.43146,797.86259 L 577.71717,880.00545 L 578.07431,881.34473 L 579.05004,882.28742 L 584.86002,897.14831 C 584.86002,897.14831 584.93925,904.12528 581.70701,906.43402 C 576.70701,910.00545 557.71717,910.71973 557.71717,910.71973 L 68.431455,907.86259 C 68.431455,907.86259 57.002885,906.43402 54.145745,903.57688 C 51.288595,900.71973 52.298745,895.51053 52.298745,895.51053 L 94.860025,745.00545 C 94.860025,745.00545 86.288605,747.86259 84.145745,752.1483 C 82.002885,756.43402 71.288595,811.43402 68.431455,820.00545 C 65.574315,828.57688 47.002885,893.57688 47.002885,893.57688 C 47.002885,893.57688 46.288597,900.00545 46.288597,903.57688 C 46.288597,907.14831 48.431455,946.43402 48.431455,946.43402 C 48.431455,946.43402 52.002885,953.57688 55.574315,955.00545 C 59.145745,956.43402 68.431455,957.14831 67.002885,957.14831 z" + id="path2404" + sodipodi:nodetypes="ccscsscsccccccsccsccsscscsc" /> + <path + style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3336)" + d="M 562.71717,958.57688 C 562.71717,958.57688 572.00288,957.86259 572.00288,951.43402 C 572.00288,945.00545 575.57431,922.14831 572.00288,918.57688 C 568.43146,915.00545 564.86002,912.86259 564.86002,912.86259 C 564.86002,912.86259 586.28859,903.57688 585.57431,907.86259 C 584.86002,912.14831 581.28859,914.29116 581.28859,920.00545 C 581.28859,925.71973 580.57431,948.57688 580.57431,948.57688 C 580.57431,948.57688 578.43146,952.86259 581.28859,953.57688 C 584.14574,954.29116 582.71717,955.71973 582.71717,955.71973 L 576.28859,957.86259 L 570.57431,958.57688 L 562.71717,958.57688 z" + id="path2406" /> + <path + style="fill:#ededed;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 75.574315,913.57688 L 560.57431,915.71973 L 557.71717,955.71973 L 77.002885,954.29116 L 75.574315,913.57688 z" + id="path2408" /> + <path + style="fill:#020202;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 100.57432,925.00545 L 541.28859,927.86259 C 541.28859,927.86259 548.43146,932.14831 548.43146,936.43402 C 548.43146,940.71973 540.57431,945.71973 540.57431,945.71973 L 98.431455,942.86259 C 98.431455,942.86259 89.145745,938.57688 89.860025,932.86259 C 90.574315,927.14831 101.2886,924.29116 100.57432,925.00545 z" + id="path2410" /> + <path + style="fill:#121212;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 71.497795,907.90591 L 557.85419,910.32545 L 573.61002,909.11259 C 573.61002,909.11259 581.28967,908.48718 583.43146,904.46973 C 584.17421,903.07651 585.22722,901.79095 585.2275,899.8245 C 585.22862,892.20247 577.89573,880.63045 577.89573,880.63045 C 577.89573,880.63045 578.20783,882.29016 577.30114,882.74322 C 575.50038,883.64304 573.664,884.53037 571.28859,885.00545 C 567.71717,885.71973 66.036055,879.91879 66.036055,879.91879 L 59.860025,880.00545 L 56.288605,877.68402 L 52.002885,896.43402 C 52.002885,896.43402 51.828665,903.29437 57.673845,905.51053 C 62.003235,907.15199 72.212085,908.6202 71.497795,907.90591 z" + id="path2412" + sodipodi:nodetypes="cccsscssccccsc" /> + <path + style="fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:#252525;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 93.431455,743.57688 L 59.145745,868.57688 C 59.145745,868.57688 57.538605,871.96973 60.574315,873.57688 C 65.381975,876.12212 74.681455,875.18402 74.681455,875.18402 C 74.681455,875.18402 567.00288,879.29116 569.86002,878.57688 C 572.71717,877.86259 575.03859,876.96974 575.03859,876.96974 L 575.21717,868.75545 C 575.21717,868.75545 579.61685,882.27622 576.28859,883.75545 C 573.07431,885.18402 566.28859,885.00545 566.28859,885.00545 L 64.860025,880.71973 L 58.431455,879.29116 L 56.645745,876.79116 L 57.861935,870.85997 L 93.431455,743.57688 z" + id="path2414" + sodipodi:nodetypes="ccscsccscccccc" /> + <path + style="fill:#d5d5d5;fill-opacity:1;fill-rule:evenodd;stroke:#d1d1d1;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3748)" + d="M 62.002885,879.82688 L 62.181455,874.29116 L 60.217165,873.93402 L 57.360025,874.82688 L 56.824315,876.25545 C 56.824315,876.25545 56.467165,877.86259 57.360025,878.21973 C 58.252885,878.57688 59.860025,879.11259 59.860025,879.11259 L 62.002885,879.82688 z" + id="path2416" /> + <path + style="fill:#d5d5d5;fill-opacity:1;fill-rule:evenodd;stroke:#dadada;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3564)" + d="M 571.82431,883.04116 C 571.82431,882.32688 570.57431,879.82688 572.18146,878.93402 C 573.78859,878.04116 575.03859,878.57688 575.03859,878.57688 C 575.03859,878.57688 578.07431,881.25545 577.36002,881.43402 C 576.64574,881.61259 576.46717,882.50545 574.68146,883.21973 C 572.89574,883.93402 572.36002,883.21973 571.82431,883.04116 z" + id="path2418" /> + <path + style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2aea00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3341)" + d="M 194.42891,930.61513 L 194.68145,934.46973" + id="path2420" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2aea00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3337)" + d="M 192.18145,932.32688 C 192.18145,932.32688 189.18906,937.68402 194.50288,937.86259 C 200.57352,938.06659 197.36003,932.50545 197.36003,932.50545" + id="path2422" + sodipodi:nodetypes="csc" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.92299998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2424" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="translate(-57.282828,191.92898)" /> + <path + sodipodi:type="arc" + style="fill:#101010;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.92626119;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2439" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="matrix(0.4791666,0,0,0.4791666,93.755115,578.94427)" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3211" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="matrix(0.5109914,0,0,0.5109914,116.22806,556.99816)" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3213" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="matrix(0.5109914,0,0,0.5109914,122.47805,556.99816)" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3215" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="matrix(0.5109914,0,0,0.5109914,119.44234,552.71244)" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#606060;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 308.07431,934.29116 C 308.07431,934.29116 307.00288,935.71973 306.28859,936.61259 C 305.57431,937.50544 305.57431,939.11259 304.32431,938.57688 C 303.07431,938.04116 301.11002,936.07688 301.28859,934.64831 C 301.46717,933.21974 304.32431,931.07688 304.32431,931.07688 C 304.32431,931.07688 305.03859,932.1483 306.11002,932.86259 C 307.18145,933.57688 307.89574,934.11259 308.07431,934.29116 z" + id="path3217" + sodipodi:nodetypes="cssscsc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3333)" + d="M 336.28859,931.43402 L 340.57431,931.43402 L 341.82431,932.68402 L 341.82431,935.00545 L 340.93145,936.79116 L 336.46717,936.79116 L 335.03859,935.71973 L 335.03859,932.50545 L 336.28859,931.43402 z" + id="path3221" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3329)" + d="M 335.75288,938.57688 L 341.28859,938.57688" + id="path3223" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 369.86002,931.43402 L 374.14574,931.43402 L 375.39574,932.68402 L 375.39574,935.00545 L 374.50288,936.79116 L 370.0386,936.79116 L 368.61002,935.71973 L 368.61002,932.50545 L 369.86002,931.43402 z" + id="path3225" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 369.32431,938.57688 L 374.86002,938.57688" + id="path3227" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 407.00288,931.79116 L 411.2886,931.79116 L 412.5386,933.04116 L 412.5386,935.36259 L 411.64574,937.1483 L 407.18147,937.1483 L 405.75288,936.07687 L 405.75288,932.86259 L 407.00288,931.79116 z" + id="path3229" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 406.46717,938.93402 L 412.00288,938.93402" + id="path3231" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 444.14574,931.96973 L 448.43145,931.96973 L 449.68145,933.21973 L 449.68145,935.54116 L 448.78859,937.32687 L 444.32431,937.32687 L 442.89574,936.25544 L 442.89574,933.04116 L 444.14574,931.96973 z" + id="path3233" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 443.61002,939.11259 L 449.14574,939.11259" + id="path3235" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican;filter:url(#filter3517)" + d="M 329.05831,937.86898 L 329.10629,932.30075 L 328.72213,932.54098 C 328.65816,932.59689 328.60226,932.62485 328.55441,932.62484 C 328.52242,932.62485 328.47243,932.59085 328.40444,932.52285 C 328.33646,932.45487 328.30245,932.38486 328.30245,932.31283 C 328.30245,932.22495 328.34841,932.12497 328.44034,932.0129 C 328.53225,931.90085 328.62618,931.78879 328.72213,931.67672 L 329.68234,930.33273 L 330.47445,930.78903 L 330.46236,938.5527 C 330.46236,938.64889 330.38631,938.69699 330.23421,938.69699 C 330.1783,938.69699 330.13437,938.69296 330.10238,938.6849 C 329.86239,938.62094 329.6224,938.56088 329.38241,938.50472 C 329.16634,938.40072 329.05831,938.18881 329.05831,937.86898 L 329.05831,937.86898 z" + id="text3237" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican" + d="M 364.94248,932.84854 C 364.94246,933.5126 364.7065,934.32059 364.23458,935.27249 C 363.84249,936.05667 363.33443,936.86868 362.71041,937.70852 L 362.50643,937.97256 L 363.8984,937.94876 C 364.14644,937.94876 364.37044,937.97476 364.5704,938.02676 C 364.77034,938.07876 364.91036,938.16067 364.99044,938.27249 C 365.16646,938.51248 365.25447,938.70449 365.25448,938.84854 C 365.25447,938.98452 365.19448,939.06851 365.07449,939.10049 C 364.95449,939.13247 364.85042,939.14846 364.76229,939.14846 L 360.97054,939.34072 L 360.5743,937.93667 C 360.83846,937.62466 361.10653,937.31265 361.3785,937.00064 C 361.96248,936.20865 362.44246,935.46463 362.81844,934.76858 C 363.3304,933.83255 363.58638,933.12857 363.58639,932.65664 C 363.58638,932.55264 363.5784,932.46866 363.5624,932.40469 C 363.54641,932.34073 363.51046,932.30875 363.45455,932.30874 C 363.30245,932.30875 362.99044,932.50064 362.51852,932.88442 C 362.21456,933.13248 361.81051,933.50052 361.30636,933.98855 C 360.90646,934.38065 360.70248,934.58462 360.69442,934.60049 L 360.02242,933.65273 C 360.02242,933.58853 360.19845,933.3605 360.5505,932.96865 C 360.95846,932.51261 361.36642,932.1326 361.77438,931.82864 C 362.32638,931.42069 362.79439,931.21671 363.17843,931.2167 C 363.40255,931.21671 363.59053,931.27664 363.74239,931.39651 C 364.29439,931.80448 364.60237,932.04849 364.66634,932.12856 C 364.85042,932.35269 364.94246,932.59268 364.94248,932.84854 L 364.94248,932.84854 z" + id="text3241" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican" + d="M 398.55154,939.8793 L 397.90371,938.83523 L 398.62368,938.71511 C 399.16762,938.61917 399.70754,938.33914 400.24343,937.87502 C 400.73952,937.44314 400.98757,937.09915 400.98757,936.84304 C 400.98757,936.69119 400.84755,936.55526 400.56753,936.43527 C 400.2875,936.31527 399.97951,936.25528 399.64358,936.25527 C 399.42751,936.25528 399.23348,936.28128 399.06149,936.33328 C 398.88949,936.38528 398.74355,936.47525 398.62368,936.60317 L 398.17947,936.27908 L 398.17947,935.23501 L 398.50356,935.079 C 399.04751,934.83902 399.59548,934.46304 400.14748,933.95107 C 400.69948,933.43912 400.97548,933.05521 400.97549,932.79934 C 400.97548,932.7432 400.95547,932.6951 400.91544,932.65505 C 400.81948,932.55911 400.65957,932.51114 400.43569,932.51113 C 400.10756,932.51114 399.69155,932.61112 399.18765,932.81106 C 398.7797,932.97122 398.5436,933.08328 398.47939,933.14724 L 397.97549,932.00723 C 398.06363,931.91129 398.32363,931.77127 398.75552,931.58718 C 399.29165,931.36307 399.75564,931.25101 400.14748,931.251 C 400.25955,931.25101 400.3716,931.26309 400.48367,931.28726 C 400.85965,931.37516 401.16762,931.52713 401.40762,931.74319 C 401.59169,931.90311 401.78371,932.15506 401.98367,932.49905 C 402.03151,932.57913 402.06149,932.6572 402.07357,932.73324 C 402.08565,932.8093 402.09169,932.88333 402.0917,932.95535 C 402.09169,933.25125 401.98366,933.5712 401.76761,933.91519 C 401.55154,934.25919 401.28347,934.56717 400.9634,934.83914 L 400.50747,935.21121 C 400.8915,935.21121 401.28951,935.43722 401.7015,935.88925 C 402.11348,936.34128 402.31948,936.8273 402.31948,937.34731 C 402.31948,937.65127 402.24947,937.91726 402.10947,938.14529 C 401.96944,938.37332 401.76748,938.6033 401.50356,938.83523 C 401.16762,939.13113 400.79567,939.37112 400.38772,939.5552 C 399.88357,939.78713 399.3915,939.9031 398.91152,939.9031 C 398.84756,939.9031 398.78158,939.89913 398.71359,939.8912 C 398.64559,939.88326 398.59157,939.8793 398.55154,939.8793 L 398.55154,939.8793 z" + id="text3245" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican" + d="M 433.97515,936.58822 L 436.87884,931.5964 L 438.17486,932.11239 L 438.01885,936.03634 L 438.42718,936.13229 C 438.62712,936.1882 438.73503,936.22018 438.75091,936.22824 C 438.799,936.26022 438.83904,936.31222 438.87103,936.38424 C 438.93499,936.52829 438.98094,936.63431 439.0089,936.7023 C 439.03686,936.77029 439.05083,936.84823 439.05083,936.93612 C 439.05083,937.00033 439.03887,937.06442 439.01495,937.12838 L 437.92291,937.20016 L 437.93499,939.73214 C 437.93499,939.94015 437.86296,940.03219 437.71893,940.00826 C 437.31902,939.95236 437.08709,939.91232 437.02312,939.88815 C 436.78313,939.78414 436.66314,939.57223 436.66314,939.25241 L 436.66314,937.28439 L 434.71893,937.28439 L 433.97515,936.58822 z M 435.48687,936.08431 L 436.80706,936.10812 L 437.01104,933.13229 L 435.48687,936.08431 z" + id="text3249" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#979797;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican" + d="M 109.34409,932.3674 C 109.08016,933.58322 108.9001,934.43124 108.80392,934.91146 C 108.65206,935.67123 108.49617,936.52719 108.33628,937.47933 L 107.04026,936.98715 L 106.27194,932.81124 L 105.39597,937.73129 L 104.01609,937.28744 L 103.3082,929.55929 C 103.3082,929.47141 103.38021,929.42746 103.52426,929.42745 C 103.63608,929.43527 103.71604,929.43918 103.76413,929.43917 C 103.91622,929.43918 104.04623,929.46518 104.15414,929.51718 C 104.26206,929.56919 104.32004,929.65927 104.3281,929.78744 L 104.77194,935.69113 L 105.64792,929.97933 L 107.04026,930.03939 L 107.65219,935.5234 C 107.76425,934.39523 107.95627,933.16318 108.22824,931.82723 C 108.35617,931.17124 108.46811,930.65927 108.56405,930.29135 C 108.63607,930.00327 108.78011,929.49521 108.99618,928.76718 C 109.02011,928.7352 109.04806,928.69919 109.08005,928.65914 C 109.15206,928.58713 109.23213,928.55918 109.32028,928.57528 C 109.55221,928.61533 109.73819,928.69132 109.87821,928.80325 C 110.0182,928.91519 110.08821,929.06723 110.08822,929.25936 C 109.91219,929.97129 109.78413,930.48728 109.70407,930.80734 C 109.57613,931.32736 109.45614,931.84738 109.34409,932.3674 L 109.34409,932.3674 z M 111.18136,931.43136 L 111.18136,930.65133 C 111.18136,930.49924 111.22733,930.36521 111.31924,930.24923 C 111.41116,930.13327 111.52914,930.07529 111.67319,930.07528 C 111.80917,930.07529 111.9332,930.12729 112.04525,930.23129 C 112.15732,930.3353 112.21335,930.45932 112.21335,930.60336 L 112.21335,931.68331 C 112.21335,931.79538 112.16135,931.87741 112.05734,931.92941 C 111.95334,931.98141 111.82932,932.00742 111.68527,932.00741 C 111.54122,932.00742 111.42123,931.95139 111.32529,931.83932 C 111.22933,931.72726 111.18136,931.59128 111.18136,931.43136 L 111.18136,931.43136 z M 111.13339,937.22738 L 111.13339,932.83541 C 111.13339,932.64328 111.24533,932.54721 111.46921,932.54721 C 111.74117,932.54721 111.93515,932.58121 112.05112,932.64919 C 112.16708,932.71719 112.22507,932.81125 112.22507,932.93136 L 112.1174,937.67123 C 112.1174,937.76718 112.04538,937.81515 111.90134,937.81515 C 111.82126,937.81515 111.75118,937.80715 111.69112,937.79116 C 111.63107,937.77517 111.58505,937.76327 111.55307,937.75546 C 111.27329,937.66732 111.13339,937.4913 111.13339,937.22738 L 111.13339,937.22738 z M 113.68845,932.8713 L 113.79648,932.73947 L 114.0726,932.73947 C 114.13657,932.73947 114.21053,932.77743 114.29452,932.85336 C 114.37851,932.92929 114.44455,932.97531 114.49265,932.99142 C 114.60446,933.02341 114.69638,932.93741 114.76839,932.73342 C 114.84042,932.52945 114.92641,932.41342 115.0264,932.38534 C 115.12636,932.35727 115.19638,932.34323 115.23641,932.34323 C 115.38852,932.34323 115.52255,932.39725 115.63852,932.50527 C 115.75449,932.61331 115.81247,932.74326 115.81247,932.89511 L 115.80039,933.36312 C 115.80038,933.5953 115.77237,933.80539 115.71634,933.99337 C 115.6603,934.18136 115.57236,934.27536 115.45248,934.27535 C 115.26839,934.27536 115.14437,934.0953 115.08042,933.73519 C 115.04843,933.55917 114.9924,933.47116 114.91233,933.47116 C 114.84054,933.47116 114.78261,933.53116 114.73855,933.65115 C 114.69449,933.77115 114.67246,934.0112 114.67246,934.3713 C 114.67246,934.48337 114.67246,934.58139 114.67246,934.66537 C 114.67246,934.74936 114.67246,934.81527 114.67246,934.86312 L 114.67246,937.79135 C 114.58456,937.83138 114.48459,937.8514 114.37252,937.8514 C 114.30051,937.8514 114.2125,937.83938 114.10849,937.81533 C 114.00449,937.79128 113.90451,937.73324 113.80857,937.6412 C 113.71261,937.54916 113.66464,937.43124 113.66464,937.28744 L 113.68845,932.8713 z M 119.45774,933.78317 L 119.52951,935.22311 L 117.82552,935.49923 C 117.68173,935.53122 117.58579,935.56723 117.53769,935.60726 C 117.43368,935.68734 117.38168,935.7994 117.38169,935.94345 L 117.40548,936.49533 C 117.40548,936.6152 117.44351,936.72518 117.51955,936.82528 C 117.59561,936.92538 117.68563,936.9714 117.78964,936.96334 C 118.01376,936.93136 118.29379,936.75534 118.62973,936.43527 C 118.96567,936.1152 119.11764,935.91916 119.08567,935.84713 C 119.11764,935.83126 119.16965,935.82333 119.24167,935.82333 C 119.37765,935.82333 119.47763,935.86734 119.5416,935.95535 C 119.60556,936.04336 119.63754,936.13534 119.63755,936.23129 C 119.63754,936.27133 119.62557,936.32333 119.60165,936.38729 C 119.55356,936.57138 119.48556,936.73141 119.39767,936.8674 C 119.35763,936.93136 119.24556,937.07138 119.0615,937.28744 C 118.90964,937.46322 118.75571,937.59115 118.59969,937.67123 C 118.44369,937.75131 118.25765,937.79135 118.0416,937.79135 C 117.89755,937.79135 117.77956,937.77731 117.68766,937.74923 C 117.59573,937.72116 117.50168,937.6672 117.40548,937.58737 L 116.78182,937.0234 C 116.75765,937.00729 116.73361,936.97122 116.70969,936.91519 C 116.68576,936.85916 116.66574,936.81515 116.64963,936.78317 C 116.59372,936.63937 116.56576,936.48739 116.56576,936.32723 L 116.56576,935.3912 C 116.56576,934.78329 116.69974,934.1553 116.96768,933.50723 C 117.23562,932.85916 117.52158,932.53513 117.82552,932.53512 C 117.87363,932.53513 117.90964,932.53915 117.93357,932.54721 C 118.08566,932.59531 118.27969,932.63132 118.51566,932.65524 C 118.75162,932.67917 118.93563,932.76315 119.06771,932.90719 C 119.19979,933.05124 119.29782,933.18924 119.36178,933.32119 C 119.42575,933.45315 119.45773,933.60715 119.45774,933.78317 L 119.45774,933.78317 z M 118.58176,933.77145 C 118.58176,933.63547 118.55576,933.51547 118.50376,933.41146 C 118.45175,933.30746 118.36972,933.23935 118.25765,933.20712 C 118.13754,933.1832 118.0256,933.31125 117.92185,933.59127 C 117.84982,933.79123 117.80173,933.93124 117.77756,934.01132 C 117.71358,934.17929 117.66562,934.32327 117.63363,934.44326 C 117.60165,934.56326 117.58566,934.69125 117.58567,934.82723 L 118.58176,934.59945 L 118.58176,933.77145 z M 120.69589,937.04721 L 120.80356,929.73947 C 120.80356,929.64328 120.85361,929.5712 120.95371,929.52322 C 121.0538,929.47525 121.16379,929.46323 121.28366,929.48715 C 121.45163,929.51914 121.56564,929.57517 121.6257,929.65524 C 121.68576,929.73532 121.71579,929.85129 121.71579,930.00314 L 121.53562,937.61117 C 121.53561,937.6993 121.45969,937.74337 121.30783,937.74337 C 121.25168,937.74337 121.19162,937.73532 121.12766,937.7192 C 120.91965,937.62326 120.79568,937.5433 120.75576,937.47933 C 120.71585,937.41537 120.69589,937.27133 120.69589,937.04721 L 120.69589,937.04721 z M 125.71554,933.78317 L 125.78733,935.22311 L 124.08335,935.49923 C 123.93955,935.53122 123.84359,935.56723 123.7955,935.60726 C 123.6915,935.68734 123.6395,935.7994 123.6395,935.94345 L 123.6633,936.49533 C 123.6633,936.6152 123.70132,936.72518 123.77738,936.82528 C 123.85343,936.92538 123.94344,936.9714 124.04746,936.96334 C 124.27158,936.93136 124.5516,936.75534 124.88755,936.43527 C 125.22348,936.1152 125.37546,935.91916 125.34348,935.84713 C 125.37546,935.83126 125.42746,935.82333 125.49948,935.82333 C 125.63547,935.82333 125.73543,935.86734 125.79941,935.95535 C 125.86337,936.04336 125.89534,936.13534 125.89535,936.23129 C 125.89534,936.27133 125.88339,936.32333 125.85947,936.38729 C 125.81136,936.57138 125.74338,936.73141 125.65548,936.8674 C 125.61544,936.93136 125.50339,937.07138 125.31931,937.28744 C 125.16745,937.46322 125.01352,937.59115 124.85752,937.67123 C 124.7015,937.75131 124.51547,937.79135 124.29941,937.79135 C 124.15537,937.79135 124.03737,937.77731 123.94547,937.74923 C 123.85354,937.72116 123.75949,937.6672 123.6633,937.58737 L 123.03964,937.0234 C 123.01547,937.00729 122.99142,936.97122 122.9675,936.91519 C 122.94357,936.85916 122.92355,936.81515 122.90743,936.78317 C 122.85153,936.63937 122.82358,936.48739 122.82358,936.32723 L 122.82358,935.3912 C 122.82358,934.78329 122.95755,934.1553 123.22549,933.50723 C 123.49344,932.85916 123.77938,932.53513 124.08335,932.53512 C 124.13144,932.53513 124.16745,932.53915 124.19137,932.54721 C 124.34348,932.59531 124.53751,932.63132 124.77347,932.65524 C 125.00942,932.67917 125.19344,932.76315 125.32552,932.90719 C 125.45761,933.05124 125.55562,933.18924 125.6196,933.32119 C 125.68356,933.45315 125.71554,933.60715 125.71554,933.78317 L 125.71554,933.78317 z M 124.83956,933.77145 C 124.83956,933.63547 124.81357,933.51547 124.76157,933.41146 C 124.70955,933.30746 124.62752,933.23935 124.51547,933.20712 C 124.39535,933.1832 124.28341,933.31125 124.17965,933.59127 C 124.10764,933.79123 124.05954,933.93124 124.03537,934.01132 C 123.97141,934.17929 123.92343,934.32327 123.89145,934.44326 C 123.85947,934.56326 123.84348,934.69125 123.84348,934.82723 L 124.83956,934.59945 L 124.83956,933.77145 z M 126.83358,937.26327 C 126.73764,937.19931 126.68966,937.09933 126.68966,936.96334 C 126.68966,936.88327 126.71366,936.80722 126.76163,936.73519 C 126.8096,936.66317 126.86758,936.60916 126.93558,936.57315 C 127.00357,936.53714 127.06955,936.53524 127.13352,936.56747 C 127.34152,936.70346 127.49752,936.80343 127.60153,936.8674 C 127.80148,936.99533 127.94942,937.05929 128.04538,937.05929 C 128.14937,937.05929 128.2134,937.02127 128.23746,936.94522 C 128.2615,936.86917 128.27353,936.81918 128.27353,936.79525 C 128.27353,936.75521 128.27353,936.72726 128.27353,936.71139 C 128.27353,936.54342 128.18149,936.37936 127.99741,936.2192 C 127.82138,936.08322 127.64737,935.94522 127.47537,935.8052 C 127.30338,935.66519 127.17141,935.52719 127.0795,935.3912 C 126.98757,935.25522 126.92362,935.09921 126.88761,934.92318 C 126.85159,934.74716 126.83358,934.59518 126.83358,934.46725 C 126.83358,934.05929 126.94956,933.6893 127.18149,933.35726 C 127.41342,933.02524 127.70944,932.85922 128.06955,932.85922 C 128.17355,932.85922 128.23752,932.85922 128.26145,932.85922 C 128.3176,932.85922 128.35764,932.86728 128.38155,932.88339 L 128.95761,933.23129 C 129.08553,933.31137 129.17355,933.42343 129.22164,933.56747 C 129.24556,933.63144 129.2695,933.78732 129.29343,934.03512 C 129.29343,934.21921 129.28341,934.37527 129.2634,934.50332 C 129.24338,934.63138 129.20139,934.76742 129.13742,934.91146 L 128.5134,934.91146 C 128.48946,934.84726 128.4515,934.79318 128.39951,934.74923 C 128.3475,934.70529 128.32149,934.48337 128.32151,934.08346 C 128.32149,933.9714 128.32149,933.85531 128.32151,933.73519 C 128.29757,933.67123 128.26157,933.63925 128.21347,933.63925 C 128.13363,933.63925 128.03366,933.73526 127.91354,933.92727 C 127.79343,934.11929 127.72141,934.28732 127.69747,934.43136 C 127.67355,934.6152 127.71761,934.79123 127.82968,934.95944 C 127.90951,935.07931 128.05344,935.22323 128.26145,935.3912 C 128.46945,935.55917 128.68552,935.72714 128.90964,935.89511 C 129.16549,936.12728 129.29343,936.3674 129.29343,936.61544 C 129.29343,936.92745 129.15347,937.21145 128.87357,937.46743 C 128.59366,937.72341 128.32968,937.8514 128.08164,937.8514 C 127.89755,937.8514 127.72549,937.81539 127.56546,937.74337 C 127.40543,937.67135 127.16146,937.51132 126.83358,937.26327 L 126.83358,937.26327 z M 130.2789,937.26327 C 130.18295,937.19931 130.13497,937.09933 130.13497,936.96334 C 130.13497,936.88327 130.15896,936.80722 130.20694,936.73519 C 130.25492,936.66317 130.31289,936.60916 130.38089,936.57315 C 130.44888,936.53714 130.51486,936.53524 130.57882,936.56747 C 130.78683,936.70346 130.94284,936.80343 131.04685,936.8674 C 131.24679,936.99533 131.39475,937.05929 131.49069,937.05929 C 131.5947,937.05929 131.65872,937.02127 131.68277,936.94522 C 131.70682,936.86917 131.71884,936.81918 131.71884,936.79525 C 131.71884,936.75521 131.71884,936.72726 131.71884,936.71139 C 131.71884,936.54342 131.6268,936.37936 131.44271,936.2192 C 131.26668,936.08322 131.09268,935.94522 130.92068,935.8052 C 130.74868,935.66519 130.61673,935.52719 130.52481,935.3912 C 130.43288,935.25522 130.36893,935.09921 130.33292,934.92318 C 130.29691,934.74716 130.2789,934.59518 130.2789,934.46725 C 130.2789,934.05929 130.39487,933.6893 130.6268,933.35726 C 130.85873,933.02524 131.15475,932.85922 131.51486,932.85922 C 131.61886,932.85922 131.68283,932.85922 131.70676,932.85922 C 131.76291,932.85922 131.80294,932.86728 131.82688,932.88339 L 132.40293,933.23129 C 132.53084,933.31137 132.61886,933.42343 132.66696,933.56747 C 132.69089,933.63144 132.71481,933.78732 132.73873,934.03512 C 132.73873,934.21921 132.72872,934.37527 132.70871,934.50332 C 132.68868,934.63138 132.64669,934.76742 132.58274,934.91146 L 131.95871,934.91146 C 131.93478,934.84726 131.89682,934.79318 131.84482,934.74923 C 131.79282,934.70529 131.76682,934.48337 131.76682,934.08346 C 131.76682,933.9714 131.76682,933.85531 131.76682,933.73519 C 131.74289,933.67123 131.70687,933.63925 131.65878,933.63925 C 131.57895,933.63925 131.47897,933.73526 131.35886,933.92727 C 131.23873,934.11929 131.16672,934.28732 131.14279,934.43136 C 131.11886,934.6152 131.16293,934.79123 131.275,934.95944 C 131.35483,935.07931 131.49875,935.22323 131.70676,935.3912 C 131.91476,935.55917 132.13083,935.72714 132.35495,935.89511 C 132.6108,936.12728 132.73873,936.3674 132.73873,936.61544 C 132.73873,936.92745 132.59878,937.21145 132.31887,937.46743 C 132.03897,937.72341 131.77499,937.8514 131.52695,937.8514 C 131.34287,937.8514 131.17081,937.81539 131.01077,937.74337 C 130.85074,937.67135 130.60677,937.51132 130.2789,937.26327 L 130.2789,937.26327 z M 145.64951,931.63534 C 145.6014,932.07529 145.41745,932.48727 145.09762,932.8713 C 144.84152,933.17526 144.45345,933.50326 143.93344,933.85531 L 143.41745,934.20321 L 145.36167,937.13143 C 145.37752,937.21127 145.38546,937.2712 145.38547,937.31124 C 145.38546,937.51119 145.29745,937.6672 145.12143,937.77926 C 145.03354,937.82736 144.94955,937.8514 144.86947,937.8514 C 144.74959,937.8514 144.63961,937.82137 144.53951,937.76132 C 144.43941,937.70126 144.34945,937.6152 144.26962,937.50314 L 141.84567,934.22738 L 141.78562,937.58737 C 141.78561,937.70724 141.68955,937.76718 141.49741,937.76718 C 141.38558,937.76718 141.29757,937.75118 141.23336,937.7192 C 141.0815,937.67135 140.94956,937.62539 140.8375,937.58132 C 140.72543,937.53726 140.6694,937.41525 140.66941,937.2153 L 140.69358,930.0632 C 140.69357,929.91135 140.71554,929.7814 140.75949,929.67336 C 140.80344,929.56534 140.8895,929.48337 141.01766,929.42745 C 141.08164,929.40329 141.14963,929.39121 141.22164,929.3912 C 141.39767,929.39121 141.53769,929.45529 141.64169,929.58346 C 141.84969,929.47946 142.03769,929.41342 142.20566,929.38534 C 142.37363,929.35727 142.60556,929.34323 142.90145,929.34323 L 143.52548,929.34323 C 144.03744,929.34323 144.52144,929.56723 144.97751,930.01522 C 145.43356,930.46323 145.66159,930.95126 145.66159,931.47933 C 145.65353,931.55136 145.6495,931.60336 145.64951,931.63534 L 145.64951,931.63534 z M 141.86947,933.48324 C 142.29354,933.37924 142.61758,933.24124 142.84159,933.06924 C 143.06557,932.89725 143.24959,932.75522 143.39364,932.64315 C 143.70566,932.39535 143.95163,932.11942 144.13155,931.81533 C 144.31149,931.51126 144.40145,931.19919 144.40145,930.87911 C 144.40145,930.68723 144.30544,930.54129 144.11344,930.44131 C 143.92141,930.34134 143.68538,930.29135 143.40537,930.29135 C 143.07747,930.29135 142.76552,930.34336 142.46952,930.44735 C 142.17348,930.55136 141.99752,930.67929 141.94162,930.83114 L 141.86947,933.48324 z M 147.50216,937.62325 C 147.1423,937.47921 146.87833,937.25119 146.71024,936.93917 C 146.54215,936.62716 146.4581,936.25521 146.4581,935.82333 C 146.4581,935.71933 146.4581,935.63131 146.4581,935.55929 C 146.4581,935.48727 146.47409,935.36325 146.50608,935.18722 C 146.52219,934.89132 146.53817,934.65133 146.55405,934.46725 C 146.61826,933.83517 146.87436,933.17917 147.32236,932.49923 C 147.3702,932.41916 147.4421,932.37912 147.53806,932.37911 C 147.59421,932.37912 147.7103,932.41513 147.88631,932.48715 C 148.06234,932.55917 148.19833,932.59518 148.29427,932.59518 C 148.75034,932.59518 149.09432,932.85922 149.32627,933.38729 C 149.52621,933.84335 149.62618,934.42331 149.62618,935.12716 C 149.62618,935.84713 149.53414,936.43112 149.35007,936.87911 C 149.12618,937.4233 148.7822,937.75533 148.31808,937.87521 C 148.23825,937.89132 148.18625,937.89938 148.16208,937.89938 C 148.09811,937.89938 148.03817,937.88339 147.98227,937.8514 C 147.92636,937.81942 147.87033,937.78744 147.81418,937.75546 L 147.50216,937.62325 z M 148.03025,933.43527 C 147.91012,933.69919 147.79208,933.96518 147.67612,934.23324 C 147.56015,934.50131 147.50216,934.83127 147.50216,935.22311 C 147.50216,935.34323 147.49019,935.51931 147.46627,935.75137 C 147.44235,935.98343 147.43039,936.15145 147.43039,936.25546 C 147.43039,936.45541 147.46035,936.62539 147.5203,936.76541 C 147.58023,936.90542 147.69016,937.01937 147.85007,937.10726 C 147.97824,937.17929 148.11826,937.01132 148.27011,936.60336 C 148.38217,936.29135 148.47421,935.91134 148.54623,935.46334 C 148.57015,935.3193 148.58212,935.16732 148.58212,935.00741 C 148.58212,934.62338 148.53011,934.25137 148.42612,933.89138 C 148.3221,933.5314 148.22214,933.35141 148.12618,933.3514 C 148.08615,933.35141 148.05417,933.37936 148.03025,933.43527 L 148.03025,933.43527 z M 151.76157,937.77926 C 151.64145,937.77926 151.54147,937.75521 151.46164,937.70712 L 151.05331,937.45516 C 150.98154,937.41513 150.92362,937.36715 150.87955,937.31124 C 150.83548,937.25534 150.78146,937.16732 150.7175,937.04721 C 150.6135,936.84725 150.56149,936.64328 150.5615,936.43527 L 150.72959,933.02731 C 150.72959,932.94723 150.78159,932.9072 150.88558,932.90719 C 150.93344,932.9072 150.99741,932.91721 151.07748,932.93722 C 151.15756,932.95724 151.23959,932.97726 151.32358,932.99728 C 151.40756,933.0173 151.50949,933.07529 151.62937,933.17123 L 151.46164,935.79916 C 151.46164,936.23935 151.48557,936.56344 151.53341,936.77145 C 151.58956,936.84323 151.6456,936.91122 151.7015,936.97543 C 151.97348,936.78329 152.14945,936.61526 152.2294,936.47134 C 152.30936,936.32742 152.34932,936.17538 152.34933,936.01522 L 152.4094,932.67941 L 152.72141,932.64315 C 153.01755,932.64316 153.27353,932.74716 153.48935,932.95516 L 153.47763,937.83932 C 153.40561,937.8713 153.32149,937.88729 153.2253,937.88729 C 153.20138,937.88729 153.14742,937.87728 153.06345,937.85726 C 152.97946,937.83724 152.90347,937.80721 152.83548,937.76718 C 152.76748,937.72714 152.72946,937.6672 152.72141,937.58737 L 152.64963,937.08346 L 152.13363,937.63534 C 152.03744,937.73129 151.91342,937.77926 151.76157,937.77926 L 151.76157,937.77926 z M 155.04757,933.75936 L 154.85567,933.66342 L 154.66342,933.66342 L 154.48361,933.43527 L 154.45944,932.85922 L 155.07174,932.72738 L 155.13144,930.44735 C 155.2596,930.41538 155.38363,930.39939 155.50351,930.39938 C 155.57552,930.39939 155.66153,930.40738 155.7615,930.42336 C 155.86148,930.43936 155.99545,930.4874 156.16342,930.56747 L 156.13961,932.60726 L 156.6677,932.60726 L 156.83542,932.91928 L 156.8237,933.49533 L 156.0195,933.57919 L 156.0195,937.75546 C 155.93161,937.77938 155.85959,937.79135 155.80344,937.79135 C 155.74752,937.79135 155.68154,937.78134 155.60549,937.76132 C 155.52945,937.7413 155.45553,937.72323 155.38376,937.70712 C 155.10372,937.62728 154.96372,937.4714 154.96372,937.23947 L 155.04757,933.75936 z M 160.42649,933.78317 L 160.49826,935.22311 L 158.79427,935.49923 C 158.65048,935.53122 158.55454,935.56723 158.50644,935.60726 C 158.40243,935.68734 158.35043,935.7994 158.35044,935.94345 L 158.37423,936.49533 C 158.37423,936.6152 158.41226,936.72518 158.4883,936.82528 C 158.56436,936.92538 158.65438,936.9714 158.75839,936.96334 C 158.98251,936.93136 159.26254,936.75534 159.59848,936.43527 C 159.93442,936.1152 160.08639,935.91916 160.05442,935.84713 C 160.08639,935.83126 160.1384,935.82333 160.21042,935.82333 C 160.3464,935.82333 160.44638,935.86734 160.51035,935.95535 C 160.57431,936.04336 160.60629,936.13534 160.6063,936.23129 C 160.60629,936.27133 160.59432,936.32333 160.5704,936.38729 C 160.52231,936.57138 160.45431,936.73141 160.36642,936.8674 C 160.32638,936.93136 160.21431,937.07138 160.03025,937.28744 C 159.87839,937.46322 159.72446,937.59115 159.56844,937.67123 C 159.41244,937.75131 159.2264,937.79135 159.01035,937.79135 C 158.8663,937.79135 158.74831,937.77731 158.65641,937.74923 C 158.56448,937.72116 158.47043,937.6672 158.37423,937.58737 L 157.75057,937.0234 C 157.7264,937.00729 157.70236,936.97122 157.67844,936.91519 C 157.65451,936.85916 157.63449,936.81515 157.61838,936.78317 C 157.56247,936.63937 157.53451,936.48739 157.53451,936.32723 L 157.53451,935.3912 C 157.53451,934.78329 157.66849,934.1553 157.93643,933.50723 C 158.20437,932.85916 158.49033,932.53513 158.79427,932.53512 C 158.84238,932.53513 158.87839,932.53915 158.90232,932.54721 C 159.05441,932.59531 159.24844,932.63132 159.48441,932.65524 C 159.72037,932.67917 159.90438,932.76315 160.03646,932.90719 C 160.16854,933.05124 160.26657,933.18924 160.33053,933.32119 C 160.3945,933.45315 160.42648,933.60715 160.42649,933.78317 L 160.42649,933.78317 z M 159.55051,933.77145 C 159.55051,933.63547 159.52451,933.51547 159.47251,933.41146 C 159.4205,933.30746 159.33847,933.23935 159.2264,933.20712 C 159.10629,933.1832 158.99435,933.31125 158.8906,933.59127 C 158.81857,933.79123 158.77048,933.93124 158.74631,934.01132 C 158.68233,934.17929 158.63437,934.32327 158.60238,934.44326 C 158.5704,934.56326 158.55441,934.69125 158.55442,934.82723 L 159.55051,934.59945 L 159.55051,933.77145 z M 161.68845,932.8713 L 161.79648,932.73947 L 162.0726,932.73947 C 162.13657,932.73947 162.21053,932.77743 162.29452,932.85336 C 162.37851,932.92929 162.44455,932.97531 162.49265,932.99142 C 162.60446,933.02341 162.69638,932.93741 162.76839,932.73342 C 162.84042,932.52945 162.92641,932.41342 163.0264,932.38534 C 163.12636,932.35727 163.19638,932.34323 163.23641,932.34323 C 163.38852,932.34323 163.52255,932.39725 163.63852,932.50527 C 163.75449,932.61331 163.81247,932.74326 163.81247,932.89511 L 163.80039,933.36312 C 163.80038,933.5953 163.77237,933.80539 163.71634,933.99337 C 163.6603,934.18136 163.57236,934.27536 163.45248,934.27535 C 163.26839,934.27536 163.14437,934.0953 163.08042,933.73519 C 163.04843,933.55917 162.9924,933.47116 162.91233,933.47116 C 162.84054,933.47116 162.78261,933.53116 162.73855,933.65115 C 162.69449,933.77115 162.67246,934.0112 162.67246,934.3713 C 162.67246,934.48337 162.67246,934.58139 162.67246,934.66537 C 162.67246,934.74936 162.67246,934.81527 162.67246,934.86312 L 162.67246,937.79135 C 162.58456,937.83138 162.48459,937.8514 162.37252,937.8514 C 162.30051,937.8514 162.2125,937.83938 162.10849,937.81533 C 162.00449,937.79128 161.90451,937.73324 161.80857,937.6412 C 161.71261,937.54916 161.66464,937.43124 161.66464,937.28744 L 161.68845,932.8713 z" + id="text3253" /> + <path + style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3304)" + d="M 62.895745,955.18402 C 62.360025,954.46973 57.538595,951.43402 57.181455,948.04116 C 56.824315,944.6483 57.895745,913.75545 57.895745,913.75545 C 57.895745,913.75545 60.217165,909.11259 62.181455,908.57688 C 64.145745,908.04116 65.217165,907.50545 65.217165,907.50545 L 53.610025,904.82688 L 50.931455,901.43402 L 51.467165,913.93402 L 52.360025,944.6483 C 52.360025,944.6483 51.645745,947.68402 50.395745,948.04116 C 49.145745,948.3983 53.074315,953.57688 53.967165,953.93402 C 54.860025,954.29116 62.895745,955.54116 62.895745,955.18402 z" + id="path3282" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#fdfdfd;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3368)" + d="M 47.895745,901.79116 C 47.895745,901.79116 51.288595,907.68402 54.502885,908.04116 C 57.717165,908.3983 64.681455,909.6483 68.967165,909.6483 C 73.252885,909.6483 73.252885,909.6483 73.252885,909.6483" + id="path3284" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#fdfdfd;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 76.467165,910.00545 L 264.32431,909.29116" + id="path3286" /> + <path + style="fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 59.598995,868.26188 L 91.671345,744.01311 C 91.671345,744.01311 93.439105,737.19458 100.76272,733.65905 C 108.08633,730.12352 119.45054,729.61844 119.45054,729.61844 C 119.45054,729.61844 522.5014,731.13367 523.51155,731.13367 C 524.5217,731.13367 538.91638,733.91159 542.19938,737.19458 C 545.48237,740.47758 548.51283,747.54865 548.51283,747.54865 L 575.02933,869.52457 C 575.02933,869.52457 576.03949,876.3431 573.76664,877.35325 C 571.4938,878.3634 564.92782,878.3634 564.92782,878.3634 L 65.407375,874.82786 C 65.407375,874.82786 60.609145,873.56518 59.851535,872.30249 C 59.093915,871.0398 59.851535,868.51442 59.598995,868.26188 z" + id="path3752" + sodipodi:nodetypes="ccscssccsccsc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#868686;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3982)" + d="M 59.346455,869.77711 C 59.346455,869.77711 59.598995,872.30249 61.366765,873.0601 C 63.134535,873.81772 67.175145,873.81772 67.175145,873.81772 L 570.23111,877.85833 C 570.23111,877.85833 573.00902,878.11086 574.52425,876.3431 C 576.03949,874.57533 575.53441,870.02964 575.53441,870.02964" + id="path3754" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3962)" + d="M 163.96716,740.00545 L 162.18145,748.13045 L 166.02075,748.13045 L 168.07432,740.18402 L 163.96716,740.00545 z" + id="path3810" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3958)" + d="M 168.69932,739.20188 C 168.25288,739.20188 163.7886,739.0233 163.7886,739.0233" + id="path3812" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3954)" + d="M 189.36894,739.91617 L 188.29753,748.13046 L 192.13682,748.13046 L 193.4761,740.09474 L 189.36894,739.91617 z" + id="path3814" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3950)" + d="M 193.92253,739.1126 C 193.4761,739.1126 189.01181,738.93402 189.01181,738.93402" + id="path3816" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3946)" + d="M 215.44037,739.46974 L 214.45824,747.59474 L 218.29752,747.59474 L 219.54752,739.64831 L 215.44037,739.46974 z" + id="path3818" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3942)" + d="M 220.17252,738.66617 C 219.7261,738.66617 215.26181,738.48759 215.26181,738.48759" + id="path3820" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3938)" + d="M 241.06539,740.00545 L 240.17254,748.13045 L 244.01183,748.13045 L 245.17254,740.18402 L 241.06539,740.00545 z" + id="path3822" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3934)" + d="M 245.79754,739.20188 C 245.3511,739.20188 240.88681,739.0233 240.88681,739.0233" + id="path3824" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3930)" + d="M 266.33324,739.64831 L 265.79752,748.39831 L 269.63681,748.39831 L 270.44038,739.82688 L 266.33324,739.64831 z" + id="path3826" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3926)" + d="M 271.06538,738.84474 C 270.61895,738.84474 266.15466,738.66616 266.15466,738.66616" + id="path3828" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3922)" + d="M 291.95824,740.36259 L 291.69039,748.75545 L 295.52968,748.75545 L 296.06539,740.54116 L 291.95824,740.36259 z" + id="path3830" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3918)" + d="M 296.69039,739.55902 C 296.24397,739.55902 291.77967,739.38044 291.77967,739.38044" + id="path3832" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3914)" + d="M 318.11895,740.63045 L 317.76181,749.20188 L 321.6011,749.20188 L 322.2261,740.80902 L 318.11895,740.63045 z" + id="path3834" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3910)" + d="M 322.8511,739.82688 C 322.40467,739.82688 317.94038,739.6483 317.94038,739.6483" + id="path3836" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3906)" + d="M 343.56537,740.63045 L 343.65466,748.84474 L 347.49395,748.84474 L 347.67252,740.80902 L 343.56537,740.63045 z" + id="path3838" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3902)" + d="M 348.29752,739.82688 C 347.85109,739.82688 343.3868,739.6483 343.3868,739.6483" + id="path3840" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3898)" + d="M 369.81537,740.36259 L 369.81537,748.93402 L 373.65466,748.93402 L 373.92252,740.54116 L 369.81537,740.36259 z" + id="path3842" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3894)" + d="M 374.54752,739.55902 C 374.10109,739.55902 369.6368,739.38044 369.6368,739.38044" + id="path3844" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3890)" + d="M 394.27966,740.98759 L 394.99395,749.38045 L 398.83324,749.38045 L 398.38681,741.16616 L 394.27966,740.98759 z" + id="path3846" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3886)" + d="M 399.01181,740.18402 C 398.56538,740.18402 394.10109,740.00544 394.10109,740.00544" + id="path3848" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3882)" + d="M 420.61895,740.63045 L 421.24395,749.02331 L 425.08324,749.02331 L 424.7261,740.80902 L 420.61895,740.63045 z" + id="path3850" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3878)" + d="M 425.3511,739.82688 C 424.90467,739.82688 420.44038,739.6483 420.44038,739.6483" + id="path3852" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3874)" + d="M 446.15466,741.34473 L 447.13681,749.6483 L 450.9761,749.6483 L 450.26181,741.5233 L 446.15466,741.34473 z" + id="path3854" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3870)" + d="M 450.88681,740.54116 C 450.44038,740.54116 445.97609,740.36258 445.97609,740.36258" + id="path3856" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3866)" + d="M 472.1368,741.07688 L 473.20823,749.55902 L 477.04752,749.55902 L 476.24396,741.25545 L 472.1368,741.07688 z" + id="path3858" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3862)" + d="M 476.86896,740.27331 C 476.42252,740.27331 471.95823,740.09473 471.95823,740.09473" + id="path3860" /> + <path + style="fill:#010101;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3996)" + d="M 457.00288,731.43402 L 458.43145,635.00545 L 457.71716,507.86259 C 457.71716,507.86259 460.10513,497.86259 471.28859,497.1483 C 478.45249,496.69074 482.71717,509.29116 482.71717,509.29116 L 482.71717,651.43402 L 484.14574,731.43402 L 457.00288,731.43402 z" + id="path3986" + sodipodi:nodetypes="cccscccc" /> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/group.svg b/wpa_supplicant/wpa_gui-qt4/icons/group.svg new file mode 100644 index 0000000..4ea959b --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/group.svg @@ -0,0 +1,616 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + height="160.00000" + id="Andysvg" + inkscape:version="0.46" + sodipodi:docbase="/home/andy/Desktop/etiquette-icons-0.4/scalable/filesystems" + sodipodi:docname="gnome-fs-network.svg" + sodipodi:version="0.32" + version="1.0" + width="160.00000" + x="0.00000000" + y="0.00000000" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="C:\Documents and Settings\All Users\Documents\Ubuntu Brig\Andy Fitzsimon\gnome-fs-network.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <metadata + id="metadata3"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:title>Etiquette Icons</dc:title> + <dc:description /> + <dc:subject> + <rdf:Bag> + <rdf:li>hash</rdf:li> + <rdf:li /> + <rdf:li>filesystem</rdf:li> + <rdf:li>computer</rdf:li> + <rdf:li>icons</rdf:li> + </rdf:Bag> + </dc:subject> + <dc:publisher> + <cc:Agent + rdf:about="http://www.openclipart.org"> + <dc:title>Andy Fitzsimon</dc:title> + </cc:Agent> + </dc:publisher> + <dc:creator> + <cc:Agent> + <dc:title>Andy Fitzsimon</dc:title> + </cc:Agent> + </dc:creator> + <dc:rights> + <cc:Agent> + <dc:title>Andy Fitzsimon</dc:title> + </cc:Agent> + </dc:rights> + <dc:date /> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <cc:license + rdf:resource="http://web.resource.org/cc/PublicDomain" /> + <dc:language>en</dc:language> + </cc:Work> + <cc:License + rdf:about="http://web.resource.org/cc/PublicDomain"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + </cc:License> + </rdf:RDF> + </metadata> + <defs + id="defs3"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 80 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="160 : 80 : 1" + inkscape:persp3d-origin="80 : 53.333333 : 1" + id="perspective97" /> + <linearGradient + id="linearGradient4894"> + <stop + id="stop4895" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop4896" + offset="0.47000000" + style="stop-color:#ffffff;stop-opacity:0.85567009;" /> + <stop + id="stop4897" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:0.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1853"> + <stop + id="stop1854" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop1855" + offset="0.47000000" + style="stop-color:#ffffff;stop-opacity:0.85567009;" /> + <stop + id="stop1856" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:0.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1806"> + <stop + id="stop1807" + offset="0.0000000" + style="stop-color:#000000;stop-opacity:0.35051546;" /> + <stop + id="stop3276" + offset="0.64999998" + style="stop-color:#000000;stop-opacity:0.13402061;" /> + <stop + id="stop1808" + offset="1.0000000" + style="stop-color:#000000;stop-opacity:0.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient893"> + <stop + id="stop895" + offset="0" + style="stop-color:#000;stop-opacity:1;" /> + <stop + id="stop896" + offset="1" + style="stop-color:#fff;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient1317"> + <stop + id="stop1318" + offset="0.00000000" + style="stop-color:#000000;stop-opacity:0.52892560;" /> + <stop + id="stop1320" + offset="0.50000000" + style="stop-color:#000000;stop-opacity:0.17355372;" /> + <stop + id="stop1319" + offset="1.0000000" + style="stop-color:#000000;stop-opacity:0.00000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1133"> + <stop + id="stop1134" + offset="0.00000000" + style="stop-color:#8bb7df;stop-opacity:1.0000000;" /> + <stop + id="stop1136" + offset="0.76209301" + style="stop-color:#2a6092;stop-opacity:1.0000000;" /> + <stop + id="stop1135" + offset="1.0000000" + style="stop-color:#375e82;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1098"> + <stop + id="stop1099" + offset="0.00000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop1101" + offset="0.50000000" + style="stop-color:#ffffff;stop-opacity:0.22314049;" /> + <stop + id="stop1102" + offset="0.59930235" + style="stop-color:#ffffff;stop-opacity:0.00000000;" /> + <stop + id="stop1100" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:0.60330576;" /> + </linearGradient> + <linearGradient + id="linearGradient902"> + <stop + id="stop903" + offset="0.00000000" + style="stop-color:#000000;stop-opacity:0.00000000;" /> + <stop + id="stop904" + offset="1.0000000" + style="stop-color:#000000;stop-opacity:0.22000000;" /> + </linearGradient> + <linearGradient + id="linearGradient892"> + <stop + id="stop893" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:0.0000000;" /> + <stop + id="stop894" + offset="1" + style="stop-color:#fff;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient888"> + <stop + id="stop889" + offset="0.0000000" + style="stop-color:#626262;stop-opacity:1.0000000;" /> + <stop + id="stop890" + offset="1" + style="stop-color:#fff;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient891" + x1="1.3485916" + x2="0.024647888" + xlink:href="#linearGradient888" + y1="-0.85185188" + y2="1.0899471" /> + <linearGradient + id="linearGradient901" + spreadMethod="pad" + x1="1.5803921" + x2="0.14117648" + xlink:href="#linearGradient888" + y1="2.4285715" + y2="-0.38571429" /> + <linearGradient + id="linearGradient905" + x1="-1.5389611" + x2="1.0909091" + xlink:href="#linearGradient888" + y1="2.7890625" + y2="-0.19531250" /> + <radialGradient + cx="0.10362694" + cy="0.093750000" + fx="0.10362694" + fy="0.093750000" + id="radialGradient1132" + r="1.2958785" + xlink:href="#linearGradient1133" /> + <linearGradient + id="linearGradient1138" + xlink:href="#linearGradient4894" /> + <linearGradient + id="linearGradient1140" + x1="0.54117650" + x2="0.57647061" + xlink:href="#linearGradient888" + y1="-2.4210527" + y2="4.6315789" /> + <linearGradient + id="linearGradient1141" + x1="1.8281938" + x2="-0.0088105723" + xlink:href="#linearGradient888" + y1="3.0546875" + y2="-0.44531250" /> + <linearGradient + id="linearGradient1144" + x1="0.21960784" + x2="0.59607846" + xlink:href="#linearGradient1853" + y1="-11.111111" + y2="5.2777777" /> + <linearGradient + id="linearGradient1146" + x1="0.51351351" + x2="-0.076576576" + xlink:href="#linearGradient892" + y1="0.55468750" + y2="1.1875000" /> + <linearGradient + id="linearGradient1148" + x1="0.23245615" + x2="1.0789474" + xlink:href="#linearGradient892" + y1="0.15625000" + y2="-0.64843750" /> + <linearGradient + id="linearGradient1150" + x1="0.25221238" + x2="-0.57522124" + xlink:href="#linearGradient892" + y1="0.57812500" + y2="1.4765625" /> + <linearGradient + id="linearGradient1156" + x1="0.48260871" + x2="0.48260871" + xlink:href="#linearGradient888" + y1="-0.40000001" + y2="1.8750000" /> + <linearGradient + id="linearGradient1157" + x1="1.5528169" + x2="-1.2077465" + xlink:href="#linearGradient888" + y1="3.3265307" + y2="-0.48979592" /> + <linearGradient + id="linearGradient1166" + x1="0.52941179" + x2="0.57647061" + xlink:href="#linearGradient1317" + y1="-3.5714285" + y2="4.6315789" /> + <linearGradient + id="linearGradient1167" + x1="1.6111112" + x2="-0.083333336" + xlink:href="#linearGradient888" + y1="3.0703125" + y2="0.046875000" /> + <linearGradient + id="linearGradient1169" + x1="1.4780220" + x2="-0.13028169" + xlink:href="#linearGradient893" + y1="2.9218750" + y2="-0.26732674" /> + <linearGradient + gradientTransform="scale(0.998371,1.001632)" + id="linearGradient1170" + x1="0.47284532" + x2="0.48655096" + xlink:href="#linearGradient902" + y1="-0.016295359" + y2="1.8378206" /> + <linearGradient + id="linearGradient1171" + x1="0.83050847" + x2="0.56355929" + xlink:href="#linearGradient902" + y1="0.57812500" + y2="0.36718750" /> + <radialGradient + cx="0.088082902" + cy="0.093750000" + fx="0.090673581" + fy="0.10937500" + id="radialGradient1315" + r="1.1765809" + xlink:href="#linearGradient1133" /> + <radialGradient + cx="0.50000000" + cy="0.50000006" + fx="0.50352114" + fy="0.18269235" + id="radialGradient1316" + r="0.34964636" + xlink:href="#linearGradient1317" /> + <linearGradient + id="linearGradient1404" + x1="0.53169012" + x2="0.54577464" + xlink:href="#linearGradient892" + y1="0.28888890" + y2="1.1000000" /> + <linearGradient + gradientTransform="scale(0.997825,1.002180)" + id="linearGradient1505" + x1="0.47157744" + x2="0.48548824" + xlink:href="#linearGradient902" + y1="-0.024853170" + y2="1.8570156" /> + <linearGradient + gradientTransform="scale(0.995847,1.004170)" + id="linearGradient1506" + x1="0.47042510" + x2="0.48481107" + xlink:href="#linearGradient902" + y1="-0.043652620" + y2="1.9025002" /> + <linearGradient + gradientTransform="scale(0.997153,1.002855)" + id="linearGradient2740" + x1="0.47041038" + x2="0.48453596" + xlink:href="#linearGradient902" + y1="-0.033741195" + y2="1.8771822" /> + <linearGradient + id="linearGradient4283" + x1="-0.77314812" + x2="0.99074072" + xlink:href="#linearGradient893" + y1="2.0837989" + y2="-0.033519555" /> + <linearGradient + id="linearGradient4284" + x1="-2.3960868e-17" + x2="0.92957747" + xlink:href="#linearGradient893" + y1="3.3012049" + y2="-0.45783132" /> + <radialGradient + cx="0.50000000" + cy="0.50000000" + fx="0.50000000" + fy="0.50000000" + id="radialGradient1977" + r="0.50000000" + xlink:href="#linearGradient1853" /> + </defs> + <sodipodi:namedview + bordercolor="#666666" + borderopacity="1.0" + id="base" + inkscape:cx="62.122256" + inkscape:cy="81.091465" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:window-height="667" + inkscape:window-width="573" + inkscape:window-x="380" + inkscape:window-y="151" + inkscape:zoom="2" + pagecolor="#ffffff" + showborder="true" + showgrid="false" + inkscape:current-layer="Andysvg" /> + <path + d="M 26.564473,83.749649 L 26.564473,121.41271 L 57.756286,121.41271" + id="path3723" + sodipodi:nodetypes="ccc" + style="fill:none;fill-rule:evenodd;stroke:#9c9c9c;stroke-width:5.7184987;stroke-linecap:round;stroke-linejoin:round;" /> + <g + id="g2843" + transform="matrix(0.999379,0.000000,0.000000,0.999379,1.227893e-3,3.986513)"> + <rect + height="8.3153667" + id="rect1906" + style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;" + transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" + width="57.567924" + x="33.326111" + y="78.658051" /> + <rect + height="60.126495" + id="rect1907" + rx="5.4369707" + ry="5.4369707" + style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;" + transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" + width="72.279724" + x="26.015469" + y="22.413721" /> + <rect + height="38.044163" + id="rect1908" + style="fill:url(#radialGradient1315);fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;" + transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" + width="58.178177" + x="33.386066" + y="31.695871" /> + <path + d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z " + id="path1909" + sodipodi:nodetypes="czzccccc" + style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" + transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" /> + <rect + height="26.147448" + id="rect1913" + rx="7.4449978" + ry="7.4449978" + style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;" + transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)" + width="104.09673" + x="140.62315" + y="-34.316952" /> + <rect + height="15.829688" + id="rect1914" + rx="3.7576280" + ry="3.7576280" + style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;" + transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)" + width="56.908955" + x="184.04552" + y="-28.539845" /> + <rect + height="15.829688" + id="rect1915" + rx="2.9970589" + ry="2.9970589" + style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;" + transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)" + width="28.796961" + x="145.28902" + y="-28.227346" /> + <rect + height="3.3627598" + id="rect1916" + rx="1.6813799" + ry="1.6813799" + style="fill-opacity:0.13836475;fill-rule:evenodd;stroke-width:0.46326005;" + transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)" + width="49.231453" + x="187.88426" + y="-21.681381" /> + </g> + <path + style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica" + d="M 7.0612345,-14.660837 L 7.0612345,-23.250681 L 8.2272501,-23.250681 L 12.738969,-16.50654 L 12.738969,-23.250681 L 13.828813,-23.250681 L 13.828813,-14.660837 L 12.662797,-14.660837 L 8.1510782,-21.410837 L 8.1510782,-14.660837 L 7.0612345,-14.660837 z M 19.869828,-16.664743 L 20.959672,-16.529978 C 20.787791,-15.893258 20.469432,-15.399118 20.004594,-15.047556 C 19.539745,-14.695993 18.945996,-14.520212 18.223344,-14.520212 C 17.313185,-14.520212 16.591506,-14.800485 16.058305,-15.361032 C 15.525101,-15.921578 15.2585,-16.70771 15.2585,-17.719431 C 15.2585,-18.766302 15.528031,-19.578801 16.067094,-20.156931 C 16.606155,-20.73505 17.305373,-21.024112 18.16475,-21.024118 C 18.996777,-21.024112 19.676464,-20.740909 20.203813,-20.174509 C 20.73115,-19.608098 20.994822,-18.811224 20.994828,-17.783884 C 20.994822,-17.721381 20.992869,-17.627631 20.988969,-17.502634 L 16.348344,-17.502634 C 16.387405,-16.819038 16.580764,-16.295601 16.928422,-15.932322 C 17.276076,-15.569039 17.709669,-15.387399 18.229203,-15.3874 C 18.615918,-15.387399 18.945996,-15.488961 19.219438,-15.692087 C 19.49287,-15.895211 19.709667,-16.219429 19.869828,-16.664743 L 19.869828,-16.664743 z M 16.406938,-18.369822 L 19.881547,-18.369822 C 19.834667,-18.893255 19.701855,-19.285833 19.483109,-19.547556 C 19.147168,-19.953801 18.711621,-20.156925 18.176469,-20.156931 C 17.692091,-20.156925 17.284865,-19.994816 16.954789,-19.670603 C 16.624709,-19.346379 16.442092,-18.912786 16.406938,-18.369822 L 16.406938,-18.369822 z M 24.592484,-15.604197 L 24.744828,-14.672556 C 24.44795,-14.610056 24.182326,-14.578806 23.947953,-14.578806 C 23.565139,-14.578806 23.268264,-14.639353 23.057328,-14.760447 C 22.846389,-14.88154 22.697952,-15.04072 22.612016,-15.237986 C 22.526077,-15.43525 22.483108,-15.850289 22.483109,-16.483103 L 22.483109,-20.063181 L 21.709672,-20.063181 L 21.709672,-20.883493 L 22.483109,-20.883493 L 22.483109,-22.424509 L 23.531938,-23.057322 L 23.531938,-20.883493 L 24.592484,-20.883493 L 24.592484,-20.063181 L 23.531938,-20.063181 L 23.531938,-16.424509 C 23.531936,-16.123726 23.55049,-15.930367 23.587602,-15.844431 C 23.624709,-15.758492 23.685256,-15.690133 23.769242,-15.639353 C 23.853224,-15.588571 23.973341,-15.56318 24.129594,-15.563181 C 24.246779,-15.56318 24.401075,-15.576852 24.592484,-15.604197 L 24.592484,-15.604197 z M 26.766313,-14.660837 L 24.862016,-20.883493 L 25.951859,-20.883493 L 26.942094,-17.291697 L 27.311234,-15.955759 C 27.326857,-16.022164 27.434279,-16.449898 27.6335,-17.238962 L 28.623734,-20.883493 L 29.707719,-20.883493 L 30.639359,-17.274118 L 30.949906,-16.084665 L 31.307328,-17.285837 L 32.373734,-20.883493 L 33.399125,-20.883493 L 31.453813,-14.660837 L 30.358109,-14.660837 L 29.367875,-18.3874 L 29.127641,-19.447947 L 27.867875,-14.660837 L 26.766313,-14.660837 z M 33.897172,-17.772165 C 33.897172,-18.924505 34.217484,-19.77802 34.858109,-20.332712 C 35.393264,-20.793644 36.045607,-21.024112 36.815141,-21.024118 C 37.670605,-21.024112 38.369823,-20.743839 38.912797,-20.183298 C 39.45576,-19.622746 39.727244,-18.848333 39.72725,-17.860056 C 39.727244,-17.059272 39.607127,-16.42939 39.366899,-15.970407 C 39.126659,-15.511422 38.77705,-15.154977 38.31807,-14.901072 C 37.859082,-14.647165 37.358106,-14.520212 36.815141,-14.520212 C 35.944045,-14.520212 35.239944,-14.799509 34.702836,-15.358103 C 34.165726,-15.916695 33.897172,-16.721382 33.897172,-17.772165 L 33.897172,-17.772165 z M 34.981156,-17.772165 C 34.981155,-16.975288 35.154983,-16.378609 35.502641,-15.982126 C 35.850295,-15.585641 36.287794,-15.387399 36.815141,-15.3874 C 37.338574,-15.387399 37.774121,-15.586617 38.121781,-15.985056 C 38.469433,-16.383492 38.643261,-16.990913 38.643266,-17.807322 C 38.643261,-18.576849 38.468456,-19.159856 38.118852,-19.556345 C 37.769238,-19.952824 37.334668,-20.151066 36.815141,-20.151072 C 36.287794,-20.151066 35.850295,-19.953801 35.502641,-19.559275 C 35.154983,-19.164739 34.981155,-18.569036 34.981156,-17.772165 L 34.981156,-17.772165 z M 40.957719,-14.660837 L 40.957719,-20.883493 L 41.906938,-20.883493 L 41.906938,-19.940134 C 42.149123,-20.381535 42.372756,-20.67255 42.577836,-20.813181 C 42.782912,-20.9538 43.008497,-21.024112 43.254594,-21.024118 C 43.610059,-21.024112 43.971387,-20.910831 44.338578,-20.684275 L 43.975297,-19.705759 C 43.717481,-19.858098 43.459669,-19.934269 43.201859,-19.934275 C 42.971388,-19.934269 42.764357,-19.864934 42.580766,-19.726267 C 42.39717,-19.58759 42.266311,-19.395207 42.188188,-19.149118 C 42.070998,-18.774114 42.012405,-18.363958 42.012406,-17.91865 L 42.012406,-14.660837 L 40.957719,-14.660837 z M 44.983109,-14.660837 L 44.983109,-23.250681 L 46.037797,-23.250681 L 46.037797,-18.352243 L 48.533891,-20.883493 L 49.899125,-20.883493 L 47.520219,-18.5749 L 50.139359,-14.660837 L 48.838578,-14.660837 L 46.781938,-17.842478 L 46.037797,-17.127634 L 46.037797,-14.660837 L 44.983109,-14.660837 z" + id="text1232" /> + <path + transform="scale(0.246729,0.246729)" + style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica" + d="M 91.619637,-37.962852 L 92.756355,-37.675743 C 92.518066,-36.742148 92.089356,-36.030234 91.470222,-35.540001 C 90.851076,-35.049766 90.09424,-34.804649 89.199715,-34.804649 C 88.27393,-34.804649 87.521001,-34.993126 86.940926,-35.370079 C 86.360846,-35.747031 85.91944,-36.292929 85.616707,-37.007774 C 85.313972,-37.722615 85.162605,-38.490193 85.162605,-39.310509 C 85.162605,-40.205035 85.333503,-40.985307 85.675301,-41.651329 C 86.017096,-42.317337 86.503424,-42.823196 87.134285,-43.168907 C 87.765141,-43.514602 88.459476,-43.687453 89.217293,-43.687462 C 90.076662,-43.687453 90.799318,-43.468703 91.385262,-43.031212 C 91.971192,-42.593704 92.379394,-41.97847 92.609871,-41.185509 L 91.49073,-40.921837 C 91.291505,-41.54683 91.002443,-42.001908 90.623543,-42.287071 C 90.244631,-42.57222 89.768069,-42.714798 89.193855,-42.714806 C 88.533695,-42.714798 87.981938,-42.556595 87.538582,-42.240196 C 87.09522,-41.923783 86.783697,-41.498979 86.604012,-40.965782 C 86.424322,-40.432574 86.334479,-39.882769 86.33448,-39.316368 C 86.334479,-38.585896 86.440924,-37.948201 86.653816,-37.403282 C 86.866705,-36.858358 87.197759,-36.451132 87.64698,-36.181602 C 88.096196,-35.91207 88.582523,-35.777305 89.105965,-35.777306 C 89.742678,-35.777305 90.28174,-35.960898 90.723152,-36.328087 C 91.164552,-36.695273 91.46338,-37.240194 91.619637,-37.962852 L 91.619637,-37.962852 z M 94.016121,-34.951134 L 94.016121,-41.17379 L 94.96534,-41.17379 L 94.96534,-40.230431 C 95.207525,-40.671831 95.431158,-40.962846 95.636238,-41.103477 C 95.841314,-41.244096 96.066899,-41.314409 96.312996,-41.314415 C 96.668461,-41.314409 97.029789,-41.201127 97.39698,-40.974571 L 97.033699,-39.996056 C 96.775883,-40.148394 96.518071,-40.224566 96.260262,-40.224571 C 96.02979,-40.224566 95.822759,-40.15523 95.639168,-40.016563 C 95.455572,-39.877887 95.324713,-39.685504 95.24659,-39.439415 C 95.1294,-39.064411 95.070807,-38.654255 95.070808,-38.208946 L 95.070808,-34.951134 L 94.016121,-34.951134 z M 102.29542,-36.95504 L 103.38526,-36.820274 C 103.21338,-36.183554 102.89502,-35.689414 102.43018,-35.337852 C 101.96533,-34.98629 101.37159,-34.810509 100.64893,-34.810509 C 99.738775,-34.810509 99.017096,-35.090782 98.483894,-35.651329 C 97.950691,-36.211875 97.684089,-36.998007 97.68409,-38.009727 C 97.684089,-39.056598 97.95362,-39.869098 98.492683,-40.447227 C 99.031744,-41.025346 99.730962,-41.314409 100.59034,-41.314415 C 101.42237,-41.314409 102.10205,-41.031206 102.6294,-40.464806 C 103.15674,-39.898394 103.42041,-39.10152 103.42042,-38.074181 C 103.42041,-38.011678 103.41846,-37.917928 103.41456,-37.792931 L 98.773933,-37.792931 C 98.812994,-37.109335 99.006354,-36.585898 99.354012,-36.222618 C 99.701665,-35.859336 100.13526,-35.677696 100.65479,-35.677696 C 101.04151,-35.677696 101.37159,-35.779258 101.64503,-35.982384 C 101.91846,-36.185507 102.13526,-36.509726 102.29542,-36.95504 L 102.29542,-36.95504 z M 98.832527,-38.660118 L 102.30714,-38.660118 C 102.26026,-39.183551 102.12744,-39.576129 101.9087,-39.837852 C 101.57276,-40.244097 101.13721,-40.447222 100.60206,-40.447227 C 100.11768,-40.447222 99.710454,-40.285113 99.380379,-39.960899 C 99.050299,-39.636676 98.867682,-39.203083 98.832527,-38.660118 L 98.832527,-38.660118 z M 108.77589,-35.718712 C 108.38526,-35.38668 108.00928,-35.152305 107.64796,-35.015587 C 107.28663,-34.878868 106.89893,-34.810509 106.48487,-34.810509 C 105.80128,-34.810509 105.27589,-34.977501 104.9087,-35.311485 C 104.54151,-35.645469 104.35792,-36.072226 104.35792,-36.591759 C 104.35792,-36.896444 104.42725,-37.174764 104.56593,-37.42672 C 104.7046,-37.67867 104.88624,-37.880818 105.11085,-38.033165 C 105.33546,-38.185505 105.58838,-38.30074 105.86964,-38.378868 C 106.07667,-38.433552 106.38917,-38.486286 106.80714,-38.537071 C 107.6587,-38.63863 108.28565,-38.759724 108.688,-38.900352 C 108.6919,-39.04488 108.69385,-39.136676 108.69386,-39.175743 C 108.69385,-39.605426 108.59424,-39.90816 108.39503,-40.083946 C 108.12549,-40.322222 107.7251,-40.441363 107.19386,-40.441368 C 106.69776,-40.441363 106.33155,-40.354449 106.09522,-40.180626 C 105.85889,-40.006793 105.68409,-39.699176 105.57081,-39.257774 L 104.53956,-39.398399 C 104.63331,-39.839801 104.7876,-40.196246 105.00245,-40.467735 C 105.21729,-40.739214 105.52784,-40.948198 105.93409,-41.094688 C 106.34034,-41.241167 106.81104,-41.314409 107.3462,-41.314415 C 107.87745,-41.314409 108.30909,-41.251909 108.64112,-41.126915 C 108.97315,-41.001909 109.21729,-40.844683 109.37354,-40.655235 C 109.52979,-40.465777 109.63916,-40.226519 109.70167,-39.937462 C 109.73682,-39.75777 109.7544,-39.433551 109.7544,-38.964806 L 109.7544,-37.558556 C 109.7544,-36.578085 109.77686,-35.957969 109.82178,-35.698204 C 109.8667,-35.438438 109.95557,-35.189415 110.08839,-34.951134 L 108.98682,-34.951134 C 108.87744,-35.169884 108.80713,-35.425743 108.77589,-35.718712 L 108.77589,-35.718712 z M 108.688,-38.074181 C 108.30518,-37.917928 107.73096,-37.785115 106.96534,-37.675743 C 106.53174,-37.61324 106.2251,-37.542928 106.04542,-37.464806 C 105.86573,-37.386678 105.72706,-37.27242 105.6294,-37.122032 C 105.53174,-36.97164 105.48292,-36.804647 105.48292,-36.621056 C 105.48292,-36.339804 105.58936,-36.105429 105.80225,-35.917931 C 106.01514,-35.73043 106.32667,-35.63668 106.73682,-35.636681 C 107.14307,-35.63668 107.5044,-35.725547 107.82081,-35.903282 C 108.13721,-36.081015 108.36963,-36.324179 108.51807,-36.632774 C 108.63135,-36.871054 108.68799,-37.222616 108.688,-37.687462 L 108.688,-38.074181 z M 113.69776,-35.894493 L 113.85011,-34.962852 C 113.55323,-34.900353 113.2876,-34.869103 113.05323,-34.869102 C 112.67042,-34.869103 112.37354,-34.929649 112.16261,-35.050743 C 111.95167,-35.171837 111.80323,-35.331016 111.71729,-35.528282 C 111.63135,-35.725547 111.58839,-36.140586 111.58839,-36.773399 L 111.58839,-40.353477 L 110.81495,-40.353477 L 110.81495,-41.17379 L 111.58839,-41.17379 L 111.58839,-42.714806 L 112.63721,-43.347618 L 112.63721,-41.17379 L 113.69776,-41.17379 L 113.69776,-40.353477 L 112.63721,-40.353477 L 112.63721,-36.714806 C 112.63721,-36.414023 112.65577,-36.220664 112.69288,-36.134727 C 112.72999,-36.048789 112.79053,-35.98043 112.87452,-35.929649 C 112.9585,-35.878867 113.07862,-35.853477 113.23487,-35.853477 C 113.35206,-35.853477 113.50635,-35.867148 113.69776,-35.894493 L 113.69776,-35.894493 z M 118.98292,-36.95504 L 120.07276,-36.820274 C 119.90088,-36.183554 119.58252,-35.689414 119.11768,-35.337852 C 118.65283,-34.98629 118.05909,-34.810509 117.33643,-34.810509 C 116.42627,-34.810509 115.7046,-35.090782 115.17139,-35.651329 C 114.63819,-36.211875 114.37159,-36.998007 114.37159,-38.009727 C 114.37159,-39.056598 114.64112,-39.869098 115.18018,-40.447227 C 115.71924,-41.025346 116.41846,-41.314409 117.27784,-41.314415 C 118.10987,-41.314409 118.78955,-41.031206 119.3169,-40.464806 C 119.84424,-39.898394 120.10791,-39.10152 120.10792,-38.074181 C 120.10791,-38.011678 120.10596,-37.917928 120.10206,-37.792931 L 115.46143,-37.792931 C 115.50049,-37.109335 115.69385,-36.585898 116.04151,-36.222618 C 116.38917,-35.859336 116.82276,-35.677696 117.34229,-35.677696 C 117.72901,-35.677696 118.05909,-35.779258 118.33253,-35.982384 C 118.60596,-36.185507 118.82276,-36.509726 118.98292,-36.95504 L 118.98292,-36.95504 z M 115.52003,-38.660118 L 118.99464,-38.660118 C 118.94776,-39.183551 118.81494,-39.576129 118.5962,-39.837852 C 118.26026,-40.244097 117.82471,-40.447222 117.28956,-40.447227 C 116.80518,-40.447222 116.39795,-40.285113 116.06788,-39.960899 C 115.7378,-39.636676 115.55518,-39.203083 115.52003,-38.660118 L 115.52003,-38.660118 z M 125.43995,-34.951134 L 125.43995,-35.73629 C 125.04541,-35.119102 124.46534,-34.810509 123.69971,-34.810509 C 123.20362,-34.810509 122.74756,-34.947227 122.33155,-35.220665 C 121.91553,-35.494102 121.59327,-35.875937 121.36475,-36.366173 C 121.13624,-36.856405 121.02198,-37.419881 121.02198,-38.056602 C 121.02198,-38.677693 121.1255,-39.241169 121.33253,-39.747032 C 121.53956,-40.252886 121.8501,-40.640581 122.26417,-40.910118 C 122.67823,-41.179643 123.14112,-41.314409 123.65284,-41.314415 C 124.02784,-41.314409 124.36182,-41.235307 124.65479,-41.07711 C 124.94776,-40.918901 125.18604,-40.712847 125.36964,-40.458946 L 125.36964,-43.540977 L 126.41846,-43.540977 L 126.41846,-34.951134 L 125.43995,-34.951134 z M 122.10596,-38.056602 C 122.10596,-37.259725 122.27393,-36.664023 122.60987,-36.269493 C 122.94581,-35.874961 123.34229,-35.677696 123.79932,-35.677696 C 124.26026,-35.677696 124.65186,-35.866172 124.97413,-36.243126 C 125.29639,-36.620077 125.45752,-37.195272 125.45753,-37.968712 C 125.45752,-38.82027 125.29346,-39.44527 124.96534,-39.843712 C 124.63721,-40.242144 124.23291,-40.441363 123.75245,-40.441368 C 123.2837,-40.441363 122.8921,-40.249957 122.57764,-39.867149 C 122.26319,-39.484332 122.10596,-38.880817 122.10596,-38.056602 L 122.10596,-38.056602 z M 132.38331,-34.951134 L 131.40479,-34.951134 L 131.40479,-43.540977 L 132.45948,-43.540977 L 132.45948,-40.476524 C 132.90479,-41.035112 133.47315,-41.314409 134.16456,-41.314415 C 134.54737,-41.314409 134.90967,-41.23726 135.25147,-41.08297 C 135.59326,-40.928667 135.87451,-40.71187 136.09522,-40.432579 C 136.31592,-40.153277 136.48877,-39.816363 136.61378,-39.421837 C 136.73877,-39.027302 136.80127,-38.605427 136.80128,-38.156212 C 136.80127,-37.089803 136.5376,-36.265586 136.01026,-35.683556 C 135.48291,-35.101524 134.8501,-34.810509 134.11182,-34.810509 C 133.37745,-34.810509 132.80127,-35.117149 132.38331,-35.730431 L 132.38331,-34.951134 z M 132.37159,-38.109337 C 132.37159,-37.363241 132.47315,-36.824179 132.67628,-36.492149 C 133.00831,-35.94918 133.45752,-35.677696 134.02393,-35.677696 C 134.48487,-35.677696 134.8833,-35.877891 135.21925,-36.278282 C 135.55518,-36.678671 135.72315,-37.27535 135.72315,-38.068321 C 135.72315,-38.880817 135.56201,-39.480426 135.23975,-39.867149 C 134.91748,-40.253863 134.52784,-40.447222 134.07081,-40.447227 C 133.60987,-40.447222 133.21143,-40.247027 132.8755,-39.846642 C 132.53956,-39.446246 132.37159,-38.867145 132.37159,-38.109337 L 132.37159,-38.109337 z M 138.04346,-32.554649 L 137.92628,-33.544884 C 138.15675,-33.482385 138.35792,-33.451135 138.52979,-33.451134 C 138.76417,-33.451135 138.95167,-33.490198 139.09229,-33.568321 C 139.23292,-33.646448 139.34815,-33.755822 139.438,-33.896446 C 139.5044,-34.001916 139.61182,-34.263634 139.76026,-34.681602 C 139.77979,-34.740196 139.81104,-34.826134 139.85401,-34.939415 L 137.49268,-41.17379 L 138.6294,-41.17379 L 139.92432,-37.570274 C 140.09229,-37.113241 140.24268,-36.632773 140.3755,-36.128868 C 140.49659,-36.613241 140.64112,-37.085897 140.80909,-37.546837 L 142.13917,-41.17379 L 143.19386,-41.17379 L 140.82667,-34.845665 C 140.57276,-34.162072 140.37549,-33.691369 140.23487,-33.433556 C 140.04737,-33.085901 139.83252,-32.831019 139.59034,-32.668907 C 139.34815,-32.5068 139.05909,-32.425746 138.72315,-32.425743 C 138.52003,-32.425746 138.29346,-32.468714 138.04346,-32.554649 L 138.04346,-32.554649 z M 146.60987,-34.951134 L 149.9087,-43.540977 L 151.13331,-43.540977 L 154.64893,-34.951134 L 153.35401,-34.951134 L 152.35206,-37.552696 L 148.76026,-37.552696 L 147.8169,-34.951134 L 146.60987,-34.951134 z M 149.08839,-38.478477 L 152.0005,-38.478477 L 151.10401,-40.857384 C 150.83057,-41.580033 150.62745,-42.173783 150.49464,-42.638634 C 150.38526,-42.087845 150.23096,-41.540971 150.03175,-40.998009 L 149.08839,-38.478477 z M 155.43409,-34.951134 L 155.43409,-41.17379 L 156.38331,-41.17379 L 156.38331,-40.289024 C 156.84034,-40.972612 157.50049,-41.314409 158.36378,-41.314415 C 158.73877,-41.314409 159.0835,-41.247026 159.39796,-41.112267 C 159.7124,-40.977495 159.94776,-40.800737 160.10401,-40.581993 C 160.26026,-40.363238 160.36963,-40.103472 160.43214,-39.802696 C 160.47119,-39.607379 160.49072,-39.265583 160.49073,-38.777306 L 160.49073,-34.951134 L 159.43604,-34.951134 L 159.43604,-38.73629 C 159.43604,-39.165973 159.39502,-39.487262 159.313,-39.700157 C 159.23096,-39.913043 159.08545,-40.082965 158.87647,-40.209923 C 158.66748,-40.336871 158.42237,-40.400347 158.14112,-40.400352 C 157.6919,-40.400347 157.3042,-40.257769 156.97803,-39.972618 C 156.65186,-39.687457 156.48878,-39.146442 156.48878,-38.349571 L 156.48878,-34.951134 L 155.43409,-34.951134 z M 166.15089,-34.951134 L 166.15089,-35.73629 C 165.75635,-35.119102 165.17627,-34.810509 164.41065,-34.810509 C 163.91456,-34.810509 163.4585,-34.947227 163.04249,-35.220665 C 162.62647,-35.494102 162.30421,-35.875937 162.07569,-36.366173 C 161.84718,-36.856405 161.73292,-37.419881 161.73292,-38.056602 C 161.73292,-38.677693 161.83643,-39.241169 162.04346,-39.747032 C 162.25049,-40.252886 162.56104,-40.640581 162.97511,-40.910118 C 163.38917,-41.179643 163.85206,-41.314409 164.36378,-41.314415 C 164.73877,-41.314409 165.07276,-41.235307 165.36573,-41.07711 C 165.65869,-40.918901 165.89698,-40.712847 166.08057,-40.458946 L 166.08057,-43.540977 L 167.1294,-43.540977 L 167.1294,-34.951134 L 166.15089,-34.951134 z M 162.8169,-38.056602 C 162.8169,-37.259725 162.98487,-36.664023 163.32081,-36.269493 C 163.65674,-35.874961 164.05323,-35.677696 164.51026,-35.677696 C 164.9712,-35.677696 165.3628,-35.866172 165.68507,-36.243126 C 166.00733,-36.620077 166.16846,-37.195272 166.16846,-37.968712 C 166.16846,-38.82027 166.0044,-39.44527 165.67628,-39.843712 C 165.34815,-40.242144 164.94385,-40.441363 164.46339,-40.441368 C 163.99463,-40.441363 163.60303,-40.249957 163.28858,-39.867149 C 162.97413,-39.484332 162.8169,-38.880817 162.8169,-38.056602 L 162.8169,-38.056602 z M 168.78175,-34.951134 L 168.78175,-41.17379 L 169.73096,-41.17379 L 169.73096,-40.230431 C 169.97315,-40.671831 170.19678,-40.962846 170.40186,-41.103477 C 170.60694,-41.244096 170.83252,-41.314409 171.07862,-41.314415 C 171.43409,-41.314409 171.79541,-41.201127 172.16261,-40.974571 L 171.79932,-39.996056 C 171.54151,-40.148394 171.2837,-40.224566 171.02589,-40.224571 C 170.79541,-40.224566 170.58838,-40.15523 170.40479,-40.016563 C 170.2212,-39.877887 170.09034,-39.685504 170.01221,-39.439415 C 169.89503,-39.064411 169.83643,-38.654255 169.83643,-38.208946 L 169.83643,-34.951134 L 168.78175,-34.951134 z M 177.06104,-36.95504 L 178.15089,-36.820274 C 177.97901,-36.183554 177.66065,-35.689414 177.19581,-35.337852 C 176.73096,-34.98629 176.13721,-34.810509 175.41456,-34.810509 C 174.5044,-34.810509 173.78272,-35.090782 173.24952,-35.651329 C 172.71632,-36.211875 172.44971,-36.998007 172.44971,-38.009727 C 172.44971,-39.056598 172.71925,-39.869098 173.25831,-40.447227 C 173.79737,-41.025346 174.49659,-41.314409 175.35596,-41.314415 C 176.18799,-41.314409 176.86768,-41.031206 177.39503,-40.464806 C 177.92236,-39.898394 178.18604,-39.10152 178.18604,-38.074181 C 178.18604,-38.011678 178.18408,-37.917928 178.18018,-37.792931 L 173.53956,-37.792931 C 173.57862,-37.109335 173.77198,-36.585898 174.11964,-36.222618 C 174.46729,-35.859336 174.90088,-35.677696 175.42042,-35.677696 C 175.80713,-35.677696 176.13721,-35.779258 176.41065,-35.982384 C 176.68408,-36.185507 176.90088,-36.509726 177.06104,-36.95504 L 177.06104,-36.95504 z M 173.59815,-38.660118 L 177.07276,-38.660118 C 177.02588,-39.183551 176.89307,-39.576129 176.67432,-39.837852 C 176.33838,-40.244097 175.90284,-40.447222 175.36768,-40.447227 C 174.88331,-40.447222 174.47608,-40.285113 174.146,-39.960899 C 173.81592,-39.636676 173.63331,-39.203083 173.59815,-38.660118 L 173.59815,-38.660118 z M 180.6294,-34.951134 L 178.72511,-41.17379 L 179.81495,-41.17379 L 180.80518,-37.581993 L 181.17432,-36.246056 C 181.18995,-36.31246 181.29737,-36.740194 181.49659,-37.529259 L 182.48682,-41.17379 L 183.57081,-41.17379 L 184.50245,-37.564415 L 184.813,-36.374962 L 185.17042,-37.576134 L 186.23682,-41.17379 L 187.26221,-41.17379 L 185.3169,-34.951134 L 184.2212,-34.951134 L 183.23096,-38.677696 L 182.99073,-39.738243 L 181.73096,-34.951134 L 180.6294,-34.951134 z M 191.67432,-34.951134 L 191.67432,-43.540977 L 197.46925,-43.540977 L 197.46925,-42.527306 L 192.81104,-42.527306 L 192.81104,-39.867149 L 196.84229,-39.867149 L 196.84229,-38.853477 L 192.81104,-38.853477 L 192.81104,-34.951134 L 191.67432,-34.951134 z M 198.82276,-42.328087 L 198.82276,-43.540977 L 199.87745,-43.540977 L 199.87745,-42.328087 L 198.82276,-42.328087 z M 198.82276,-34.951134 L 198.82276,-41.17379 L 199.87745,-41.17379 L 199.87745,-34.951134 L 198.82276,-34.951134 z M 203.79151,-35.894493 L 203.94386,-34.962852 C 203.64698,-34.900353 203.38135,-34.869103 203.14698,-34.869102 C 202.76417,-34.869103 202.46729,-34.929649 202.25636,-35.050743 C 202.04542,-35.171837 201.89698,-35.331016 201.81104,-35.528282 C 201.7251,-35.725547 201.68214,-36.140586 201.68214,-36.773399 L 201.68214,-40.353477 L 200.9087,-40.353477 L 200.9087,-41.17379 L 201.68214,-41.17379 L 201.68214,-42.714806 L 202.73096,-43.347618 L 202.73096,-41.17379 L 203.79151,-41.17379 L 203.79151,-40.353477 L 202.73096,-40.353477 L 202.73096,-36.714806 C 202.73096,-36.414023 202.74952,-36.220664 202.78663,-36.134727 C 202.82374,-36.048789 202.88428,-35.98043 202.96827,-35.929649 C 203.05225,-35.878867 203.17237,-35.853477 203.32862,-35.853477 C 203.44581,-35.853477 203.6001,-35.867148 203.79151,-35.894493 L 203.79151,-35.894493 z M 204.26026,-34.951134 L 204.26026,-35.806602 L 208.2212,-40.353477 C 207.77198,-40.330035 207.37549,-40.318316 207.03175,-40.318321 L 204.49464,-40.318321 L 204.49464,-41.17379 L 209.58057,-41.17379 L 209.58057,-40.476524 L 206.21143,-36.527306 L 205.56104,-35.806602 C 206.0337,-35.841758 206.47706,-35.859336 206.89112,-35.859337 L 209.76807,-35.859337 L 209.76807,-34.951134 L 204.26026,-34.951134 z M 210.39503,-36.808556 L 211.438,-36.972618 C 211.49659,-36.554648 211.65967,-36.234336 211.92725,-36.011681 C 212.19483,-35.789024 212.56885,-35.677696 213.04932,-35.677696 C 213.5337,-35.677696 213.89307,-35.776328 214.12745,-35.973595 C 214.36182,-36.170859 214.47901,-36.402304 214.47901,-36.667931 C 214.47901,-36.90621 214.37549,-37.09371 214.16846,-37.230431 C 214.02393,-37.324178 213.66455,-37.443319 213.09034,-37.587852 C 212.3169,-37.783162 211.78077,-37.952107 211.48194,-38.094688 C 211.18311,-38.237263 210.95655,-38.434529 210.80225,-38.686485 C 210.64796,-38.938434 210.57081,-39.216754 210.57081,-39.521446 C 210.57081,-39.798785 210.63428,-40.055621 210.76124,-40.291954 C 210.88819,-40.528277 211.06104,-40.724565 211.27979,-40.880821 C 211.44385,-41.001909 211.66749,-41.104448 211.95069,-41.188438 C 212.23389,-41.272416 212.5376,-41.314409 212.86182,-41.314415 C 213.3501,-41.314409 213.77881,-41.244096 214.14796,-41.103477 C 214.51709,-40.962846 214.78955,-40.772417 214.96534,-40.532188 C 215.14112,-40.291949 215.26221,-39.97066 215.32862,-39.568321 L 214.29737,-39.427696 C 214.25049,-39.748004 214.11475,-39.998004 213.89014,-40.177696 C 213.66553,-40.357378 213.34815,-40.447222 212.938,-40.447227 C 212.45362,-40.447222 212.10792,-40.367144 211.90089,-40.206993 C 211.69385,-40.046832 211.59034,-39.859332 211.59034,-39.644493 C 211.59034,-39.50777 211.63331,-39.384723 211.71925,-39.275352 C 211.80518,-39.162067 211.93995,-39.068317 212.12354,-38.994102 C 212.22901,-38.955036 212.53956,-38.865192 213.05518,-38.724571 C 213.80127,-38.525349 214.32178,-38.362263 214.61671,-38.235313 C 214.91162,-38.108357 215.14307,-37.923787 215.31104,-37.681602 C 215.47901,-37.439412 215.56299,-37.138632 215.563,-36.779259 C 215.56299,-36.427695 215.46045,-36.09664 215.25538,-35.786095 C 215.0503,-35.475547 214.7544,-35.235313 214.36768,-35.065392 C 213.98096,-34.89547 213.54346,-34.810509 213.05518,-34.810509 C 212.24659,-34.810509 211.63038,-34.978477 211.20655,-35.314415 C 210.78272,-35.650352 210.51221,-36.148398 210.39503,-36.808556 L 210.39503,-36.808556 z M 216.82276,-42.328087 L 216.82276,-43.540977 L 217.87745,-43.540977 L 217.87745,-42.328087 L 216.82276,-42.328087 z M 216.82276,-34.951134 L 216.82276,-41.17379 L 217.87745,-41.17379 L 217.87745,-34.951134 L 216.82276,-34.951134 z M 219.48878,-34.951134 L 219.48878,-41.17379 L 220.43214,-41.17379 L 220.43214,-40.300743 C 220.62745,-40.605425 220.88721,-40.850542 221.21143,-41.036095 C 221.53565,-41.221635 221.90479,-41.314409 222.31886,-41.314415 C 222.77979,-41.314409 223.15772,-41.218706 223.45264,-41.027306 C 223.74756,-40.835893 223.95557,-40.568316 224.07667,-40.224571 C 224.56885,-40.951128 225.20947,-41.314409 225.99854,-41.314415 C 226.61572,-41.314409 227.09033,-41.14351 227.42237,-40.80172 C 227.75439,-40.459917 227.92041,-39.933551 227.92042,-39.222618 L 227.92042,-34.951134 L 226.87159,-34.951134 L 226.87159,-38.871056 C 226.87158,-39.292926 226.8374,-39.596637 226.76905,-39.782188 C 226.70068,-39.96773 226.57666,-40.117144 226.39698,-40.230431 C 226.21729,-40.343706 226.00635,-40.400347 225.76417,-40.400352 C 225.32666,-40.400347 224.96338,-40.254839 224.67432,-39.963829 C 224.38526,-39.672809 224.24072,-39.206989 224.24073,-38.566368 L 224.24073,-34.951134 L 223.18604,-34.951134 L 223.18604,-38.994102 C 223.18604,-39.462848 223.1001,-39.81441 222.92823,-40.04879 C 222.75635,-40.28316 222.4751,-40.400347 222.08448,-40.400352 C 221.7876,-40.400347 221.51319,-40.322222 221.26124,-40.165977 C 221.00928,-40.009722 220.82667,-39.781207 220.71339,-39.480431 C 220.6001,-39.179645 220.54346,-38.746052 220.54346,-38.179649 L 220.54346,-34.951134 L 219.48878,-34.951134 z M 229.10401,-38.062462 C 229.10401,-39.214801 229.42432,-40.068316 230.06495,-40.623009 C 230.6001,-41.08394 231.25245,-41.314409 232.02198,-41.314415 C 232.87744,-41.314409 233.57666,-41.034135 234.11964,-40.473595 C 234.6626,-39.913043 234.93408,-39.13863 234.93409,-38.150352 C 234.93408,-37.349569 234.81397,-36.719687 234.57374,-36.260704 C 234.3335,-35.801719 233.98389,-35.445274 233.52491,-35.191368 C 233.06592,-34.937462 232.56495,-34.810509 232.02198,-34.810509 C 231.15088,-34.810509 230.44678,-35.089805 229.90968,-35.648399 C 229.37257,-36.206992 229.10401,-37.011679 229.10401,-38.062462 L 229.10401,-38.062462 z M 230.188,-38.062462 C 230.18799,-37.265585 230.36182,-36.668905 230.70948,-36.272423 C 231.05713,-35.875937 231.49463,-35.677696 232.02198,-35.677696 C 232.54541,-35.677696 232.98096,-35.876914 233.32862,-36.275352 C 233.67627,-36.673788 233.8501,-37.28121 233.85011,-38.097618 C 233.8501,-38.867145 233.6753,-39.450153 233.32569,-39.846642 C 232.97608,-40.243121 232.54151,-40.441363 232.02198,-40.441368 C 231.49463,-40.441363 231.05713,-40.244097 230.70948,-39.849571 C 230.36182,-39.455035 230.18799,-38.859333 230.188,-38.062462 L 230.188,-38.062462 z M 236.17628,-34.951134 L 236.17628,-41.17379 L 237.1255,-41.17379 L 237.1255,-40.289024 C 237.58252,-40.972612 238.24268,-41.314409 239.10596,-41.314415 C 239.48096,-41.314409 239.82569,-41.247026 240.14014,-41.112267 C 240.45459,-40.977495 240.68994,-40.800737 240.8462,-40.581993 C 241.00244,-40.363238 241.11182,-40.103472 241.17432,-39.802696 C 241.21338,-39.607379 241.23291,-39.265583 241.23292,-38.777306 L 241.23292,-34.951134 L 240.17823,-34.951134 L 240.17823,-38.73629 C 240.17823,-39.165973 240.13721,-39.487262 240.05518,-39.700157 C 239.97315,-39.913043 239.82764,-40.082965 239.61866,-40.209923 C 239.40967,-40.336871 239.16455,-40.400347 238.88331,-40.400352 C 238.43409,-40.400347 238.04639,-40.257769 237.72022,-39.972618 C 237.39405,-39.687457 237.23096,-39.146442 237.23096,-38.349571 L 237.23096,-34.951134 L 236.17628,-34.951134 z" + id="text1235" /> + <g + id="g2852" + transform="matrix(1.018857,0.000000,0.000000,1.018857,-4.481650,2.131177)"> + <rect + height="8.3153667" + id="rect1866" + style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" + width="57.567924" + x="33.326111" + y="78.658051" /> + <rect + height="60.126495" + id="rect1867" + rx="5.4369707" + ry="5.4369707" + style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" + width="72.279724" + x="26.015469" + y="22.413721" /> + <rect + height="38.044163" + id="rect1868" + style="fill:url(#radialGradient1132);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" + width="58.178177" + x="33.386066" + y="31.695871" /> + <path + d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z " + id="path1869" + sodipodi:nodetypes="czzccccc" + style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" /> + <g + id="g1870" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"> + <path + d="M 42.062098,33.460351 L 77.341205,33.008055 C 82.787126,32.938235 89.553204,38.416797 89.553204,43.863165 L 89.553204,60.145830 L 41.609801,59.693534 L 42.062098,33.460351 z " + id="path1871" + sodipodi:nodetypes="czzccc" + style="fill:url(#linearGradient1148);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" /> + <path + d="M 78.337784,67.629235 L 46.723745,67.724544 C 41.843589,67.739257 35.829319,62.771024 35.877168,57.891081 L 36.020221,43.301821 L 78.973514,44.128288 L 78.337784,67.629235 z " + id="path1872" + sodipodi:nodetypes="czzccc" + style="fill:url(#linearGradient1150);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" /> + </g> + <rect + height="26.147448" + id="rect1888" + rx="7.4449978" + ry="7.4449978" + style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;" + transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)" + width="104.09673" + x="140.62315" + y="-34.316952" /> + <rect + height="15.829688" + id="rect1889" + rx="3.7576280" + ry="3.7576280" + style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;" + transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)" + width="56.908955" + x="184.04552" + y="-28.539845" /> + <rect + height="15.829688" + id="rect1890" + rx="2.9970589" + ry="2.9970589" + style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;" + transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)" + width="28.796961" + x="145.28902" + y="-28.227346" /> + <rect + height="3.3627598" + id="rect1891" + rx="1.6813799" + ry="1.6813799" + style="fill-opacity:0.16981132;fill-rule:evenodd;stroke-width:0.46326005;" + transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)" + width="49.231453" + x="187.88426" + y="-21.681381" /> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg b/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg new file mode 100644 index 0000000..1a02d13 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg @@ -0,0 +1,374 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="64.000000px" + height="64.000000px" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.42" + sodipodi:docbase="G:\Projs\Cliparts Stocker\released" + sodipodi:docname="unknown_green.svg" + inkscape:export-filename="/datas/wiki/unknown_green.png" + inkscape:export-xdpi="90.000000" + inkscape:export-ydpi="90.000000"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2842" + id="linearGradient1363" + x1="25.403513" + y1="19.175573" + x2="35.541985" + y2="49.068703" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-2.402975,4.759656e-3)" /> + <linearGradient + id="linearGradient2900"> + <stop + id="stop2902" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop2904" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient2842"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop2844" /> + <stop + style="stop-color:#c8c8c8;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop2846" /> + </linearGradient> + <linearGradient + id="linearGradient2814"> + <stop + id="stop2816" + offset="0.0000000" + style="stop-color:#e6e6e6;stop-opacity:1.0000000;" /> + <stop + id="stop2818" + offset="1.0000000" + style="stop-color:#11661d;stop-opacity:0.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient2171"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop2173" /> + <stop + style="stop-color:#a3a5ee;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop2175" /> + </linearGradient> + <linearGradient + id="linearGradient2160"> + <stop + id="stop2162" + offset="0.0000000" + style="stop-color:#d3cece;stop-opacity:1.0000000;" /> + <stop + id="stop2164" + offset="1.0000000" + style="stop-color:#474240;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1367"> + <stop + id="stop1369" + offset="0.0000000" + style="stop-color:#f67e36;stop-opacity:1.0000000;" /> + <stop + id="stop1371" + offset="1.0000000" + style="stop-color:#602604;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1347"> + <stop + style="stop-color:#f0da27;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop1349" /> + <stop + style="stop-color:#bf4d09;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop1351" /> + </linearGradient> + <linearGradient + id="linearGradient1315"> + <stop + style="stop-color:#97ff82;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop1317" /> + <stop + style="stop-color:#ceff24;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop1319" /> + </linearGradient> + <linearGradient + id="linearGradient2122"> + <stop + style="stop-color:#2edc32;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop2124" /> + <stop + style="stop-color:#11661d;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop2126" /> + </linearGradient> + <linearGradient + id="linearGradient1364"> + <stop + style="stop-color:#236b0d;stop-opacity:1.0000000;" + offset="0.00000000" + id="stop1366" /> + <stop + style="stop-color:#0a2205;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop1368" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient1367" + id="radialGradient1402" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)" + cx="21.959658" + cy="14.921703" + fx="21.959658" + fy="14.921703" + r="27.500000" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2122" + id="radialGradient1404" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)" + cx="21.959658" + cy="14.921703" + fx="21.959658" + fy="14.921703" + r="27.500000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1364" + id="linearGradient1419" + gradientUnits="userSpaceOnUse" + x1="74.910713" + y1="32.362179" + x2="84.910713" + y2="47.451466" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2122" + id="linearGradient1421" + gradientUnits="userSpaceOnUse" + x1="73.839287" + y1="34.428566" + x2="76.875000" + y2="43.714283" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1315" + id="linearGradient1423" + gradientUnits="userSpaceOnUse" + x1="72.946426" + y1="35.589287" + x2="85.000000" + y2="47.375000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2171" + id="linearGradient2177" + x1="24.916031" + y1="28.824427" + x2="39.816792" + y2="49.099239" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2122" + id="radialGradient2184" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(9.909149e-17,1.088708,-1.102427,1.658760e-5,41.48828,-4.732338)" + cx="21.959658" + cy="14.921703" + fx="21.959658" + fy="14.921703" + r="27.500000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1364" + id="linearGradient2189" + x1="10.018247" + y1="8.6306763" + x2="63.487556" + y2="63.660282" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2171" + id="linearGradient1339" + gradientUnits="userSpaceOnUse" + x1="24.916031" + y1="28.824427" + x2="39.816792" + y2="49.099239" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2122" + id="radialGradient1343" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.521415e-2,1.026125,-0.978137,2.404729e-2,38.83024,-3.575704)" + cx="24.764277" + cy="16.361967" + fx="24.764277" + fy="16.361967" + r="27.500000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1364" + id="linearGradient1346" + gradientUnits="userSpaceOnUse" + x1="10.018247" + y1="8.6306763" + x2="63.487556" + y2="63.660282" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2814" + id="radialGradient2812" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.142398e-2,1.098850,-1.843995,1.878760e-2,52.15051,-5.667446)" + cx="18.387238" + cy="14.046815" + fx="18.387238" + fy="14.046815" + r="27.500000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1364" + id="linearGradient2832" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-2.841000e-3,-2.841000e-3)" + x1="10.018247" + y1="8.6306763" + x2="63.487556" + y2="63.660282" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2842" + id="linearGradient2848" + x1="-0.56685609" + y1="22.651009" + x2="-0.33713850" + y2="23.858734" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2842" + id="linearGradient2864" + gradientUnits="userSpaceOnUse" + x1="-0.82287467" + y1="22.444542" + x2="-0.33713850" + y2="23.858734" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="8.2031250" + inkscape:cx="32.000000" + inkscape:cy="32.000000" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:grid-bbox="true" + inkscape:grid-points="true" + inkscape:window-width="1156" + inkscape:window-height="693" + inkscape:window-x="0" + inkscape:window-y="25" + showguides="false" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>Green Unknown</dc:title> + <dc:date>2005-11-01</dc:date> + <dc:creator> + <cc:Agent> + <dc:title>Jean-Victor Balin</dc:title> + </cc:Agent> + </dc:creator> + <dc:description>jean.victor.balin@gmail.com</dc:description> + <cc:license + rdf:resource="http://web.resource.org/cc/PublicDomain" /> + <dc:subject> + <rdf:Bag> + <rdf:li>icon</rdf:li> + </rdf:Bag> + </dc:subject> + </cc:Work> + <cc:License + rdf:about="http://web.resource.org/cc/PublicDomain"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g1354"> + <path + id="path1373" + d="M 32.000000,8.6306766 C 19.113097,8.6306766 8.6306766,19.113097 8.6306766,32.000000 C 8.6306766,44.886903 19.113097,55.369323 32.000000,55.369323 C 44.886903,55.369323 55.369323,44.886903 55.369323,32.000000 C 55.369323,19.113097 44.886903,8.6306766 32.000000,8.6306766 z " + style="fill:url(#linearGradient1346);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" /> + <path + id="path1339" + d="M 54.500005,32.000000 C 54.500005,44.420003 44.420003,54.500005 32.000000,54.500005 C 19.579997,54.500005 9.4999950,44.420003 9.4999950,32.000000 C 9.4999950,19.579997 19.579997,9.4999950 32.000000,9.4999950 C 44.420003,9.4999950 54.500005,19.579997 54.500005,32.000000 z " + style="fill:url(#radialGradient1343);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" /> + <path + id="path1341" + d="M 32.016991,9.1562500 C 22.574792,9.1562500 14.505423,14.865048 11.062500,22.968750 C 16.006322,25.801817 21.393258,27.855853 27.181339,27.593750 C 32.755311,27.279922 37.553510,23.530916 43.236968,23.812500 C 47.451058,23.716455 52.244330,25.294372 54.488550,29.000000 C 53.142630,17.846718 43.657640,9.1562500 32.016991,9.1562500 z " + style="fill:url(#radialGradient2812);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" /> + <path + id="path2827" + d="M 32.000000,8.6250000 C 19.113098,8.6250000 8.6250000,19.113097 8.6250000,32.000000 C 8.6250000,44.886904 19.113097,55.375000 32.000000,55.375000 C 44.886904,55.375000 55.375000,44.886903 55.375000,32.000000 C 55.375000,19.113098 44.886903,8.6250000 32.000000,8.6250000 z M 32.000000,9.5000000 C 44.420004,9.4999998 54.500000,19.579997 54.500000,32.000000 C 54.499998,44.420004 44.420003,54.500000 32.000000,54.500000 C 19.579998,54.499998 9.5000000,44.420003 9.5000000,32.000000 C 9.5000000,19.579998 19.579997,9.5000000 32.000000,9.5000000 z " + style="fill:url(#linearGradient2832);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" /> + <path + id="text1353" + d="M 32.556888,39.006317 C 32.692760,35.835967 33.100380,35.066018 35.908404,32.892064 C 39.395790,30.219911 39.803410,29.902873 40.120445,29.631129 C 41.705621,28.272407 42.611437,26.189029 42.611437,24.015074 C 42.611437,19.078386 38.625844,15.953318 32.285143,15.953318 C 26.306768,15.953318 22.094721,18.851931 22.094721,23.018677 C 22.094721,25.464376 23.906354,27.230718 26.397344,27.230718 C 28.707171,27.230718 30.292350,25.736121 30.292350,23.607457 C 30.292350,22.384608 29.794150,21.388209 28.843045,20.663558 C 28.027812,20.029488 27.982521,19.984196 27.982521,19.667161 C 27.982521,19.033091 28.978919,18.534892 30.382931,18.534892 C 33.100374,18.534892 34.640263,20.346525 34.640263,23.516876 C 34.640263,25.373795 33.960900,27.683628 32.828632,29.721710 C 30.337643,34.160201 29.975314,35.066023 29.975314,37.104105 C 29.975314,37.557012 30.020605,38.281665 30.111187,39.006317 L 32.556888,39.006317 M 31.424619,41.497309 C 29.069501,41.497309 27.167287,43.399523 27.167287,45.754641 C 27.167287,48.064467 29.069501,50.011973 31.379328,50.011973 C 33.779736,50.011973 35.681951,48.109758 35.681951,45.754641 C 35.681951,43.399523 33.779736,41.497309 31.424619,41.497309" + style="font-size:45.290764px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient1363);fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Century Schoolbook L" /> + </g> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg b/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg new file mode 100644 index 0000000..06235f0 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg @@ -0,0 +1,1568 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="400" + height="400" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45" + version="1.0" + sodipodi:docbase="C:\Documents and Settings\Mete Ä°slam\Desktop" + sodipodi:docname="MyLaptop.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="C:\Documents and Settings\Mete Ä°slam\Desktop\MyLaptop.png" + inkscape:export-xdpi="98" + inkscape:export-ydpi="98" + sodipodi:modified="true"> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1" + inkscape:cx="319.93339" + inkscape:cy="202.90098" + inkscape:document-units="px" + inkscape:current-layer="layer1" + width="400px" + height="400px" + inkscape:window-width="1277" + inkscape:window-height="751" + inkscape:window-x="0" + inkscape:window-y="22" + showguides="true" + inkscape:guide-bbox="true" /> + <defs + id="defs4"> + <linearGradient + id="linearGradient3757"> + <stop + style="stop-color:#70ffea;stop-opacity:1;" + offset="0" + id="stop3759" /> + <stop + style="stop-color:#0055f6;stop-opacity:1;" + offset="1" + id="stop3761" /> + </linearGradient> + <linearGradient + id="linearGradient3460"> + <stop + style="stop-color:#f1ff00;stop-opacity:1;" + offset="0" + id="stop3462" /> + <stop + style="stop-color:#8bff00;stop-opacity:0;" + offset="1" + id="stop3464" /> + </linearGradient> + <linearGradient + id="linearGradient26774"> + <stop + style="stop-color:#ffffff;stop-opacity:0.1122449;" + offset="0" + id="stop26776" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.89795917;" + offset="1" + id="stop26778" /> + </linearGradient> + <linearGradient + id="linearGradient17245"> + <stop + offset="0" + style="stop-color:#ffefef;stop-opacity:0.58163267;" + id="stop17247" /> + <stop + offset="1" + style="stop-color:#ffefef;stop-opacity:0.14285715;" + id="stop17249" /> + </linearGradient> + <pattern + patternTransform="matrix(0.9848362,0,0,0.9848362,-402.92422,36.839002)" + id="pattern13296" + xlink:href="#pattern13289" + inkscape:collect="always" /> + <pattern + patternTransform="matrix(0.6565232,0,0,0.6651903,-8.1640579,-22.602821)" + id="pattern13287" + xlink:href="#pattern12311" + inkscape:collect="always" /> + <pattern + patternTransform="translate(-88.774232,-72.100299)" + id="pattern12309" + xlink:href="#pattern11335" + inkscape:collect="always" /> + <pattern + patternTransform="translate(-5.8654428,10.456268)" + id="pattern9368" + xlink:href="#pattern8394" + inkscape:collect="always" /> + <linearGradient + id="linearGradient4451"> + <stop + offset="0" + style="stop-color:#fffbfb;stop-opacity:0.82653064;" + id="stop4453" /> + <stop + offset="1" + style="stop-color:#000000;stop-opacity:0;" + id="stop4455" /> + </linearGradient> + <pattern + height="253" + id="pattern8394" + patternUnits="userSpaceOnUse" + patternTransform="translate(404.25649,166.01976)" + width="337"> + <image + id="image4466" + width="337" + y="0" + xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QCURXhpZgAASUkqAAgAAAADADEBAgAcAAAAMgAAADIBAgAU AAAATgAAAGmHBAABAAAAYgAAAAAAAABBZG9iZSBQaG90b3Nob3AgQ1MyIFdpbmRvd3MAMjAwNjow NjoxMyAxMzozNDoyNAADAAGgAwABAAAAAQAAAAKgBAABAAAAAAQAAAOgBAABAAAAAAMAAAAAAAD/ 4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3Nw TVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAA AGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAAC QAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1p AAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxi VFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29t cGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJzUkdCIElFQzYx OTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA WFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUA AAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklF QyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMg NjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMg NjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAA AAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2 LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJRUM2MTk2Ni0y LjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk/gAUXy4AEM8UAAPtzAAEEwsA A1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA Ao8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsA QABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDL ANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUB fAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YD ogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUc BSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG 9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQ CSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4AL mAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5k Dn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwR qhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0 FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZ RRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2Z HcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUi giKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneier J9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEt di2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/ M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6 Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50Ep QWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI 10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7 UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZ aVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr /2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXh dj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeA qIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuW i/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqX dZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2 o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACw dbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2P vgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbL tsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx 2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6Lzp RunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio +Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB AQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEB AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAD9 AVEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIE AwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJico KSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZ 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 AJ+x/wDAn/8AKj//2Q== " + x="0" + height="253" /> + </pattern> + <filter + id="filter16257" + inkscape:collect="always"> + <feGaussianBlur + stdDeviation="0.41431294" + id="feGaussianBlur16259" + inkscape:collect="always" /> + </filter> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient17245" + id="linearGradient27074" + gradientUnits="userSpaceOnUse" + x1="136.5" + y1="161.5" + x2="313.74622" + y2="285.25275" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4451" + id="linearGradient27076" + gradientUnits="userSpaceOnUse" + x1="155.34465" + y1="112.46042" + x2="136.51547" + y2="2.1517708" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient26774" + id="linearGradient27078" + gradientUnits="userSpaceOnUse" + x1="280.27875" + y1="261.40704" + x2="322.26389" + y2="275.19568" /> + <filter + inkscape:collect="always" + x="-0.086395349" + width="1.1727907" + y="-0.11145" + height="1.2229" + id="filter3497"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="11.145" + id="feGaussianBlur3499" /> + </filter> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3460" + id="radialGradient3466" + cx="167.48819" + cy="192.38739" + fx="167.48819" + fy="192.38739" + r="105.62836" + gradientTransform="matrix(0.8393229,-0.4383343,0.2966517,0.5680291,-30.16055,165.27307)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient26774" + id="linearGradient2490" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + x1="280.27875" + y1="261.40704" + x2="322.26389" + y2="275.19568" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4451" + id="linearGradient2500" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + x1="159.38791" + y1="126.94874" + x2="138.87404" + y2="12.596838" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient17245" + id="linearGradient2777" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + x1="136.5" + y1="161.5" + x2="313.74622" + y2="285.25275" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3757" + id="radialGradient3763" + cx="170.31175" + cy="209.16652" + fx="170.31175" + fy="209.16652" + r="104.54334" + gradientTransform="matrix(1.0743517,-0.8811517,0.8948667,1.0910737,-193.89727,134.77199)" + gradientUnits="userSpaceOnUse" /> + </defs> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + id="layer1" + inkscape:groupmode="layer"> + <g + id="g2491"> + <path + transform="matrix(0.9747328,0,0,0.9747328,8.1232897,4.8258519)" + inkscape:export-ydpi="98" + inkscape:export-xdpi="98" + sodipodi:nodetypes="cccccc" + id="path2486" + d="M 183,323 L 53,296 L 0.99999999,150 L 196,96 L 255,237 L 183,323 z " + style="fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3497)" /> + <path + id="path5140" + sodipodi:nodetypes="ccccc" + d="M 103.94986,273.26347 L 112.82679,278.13224 L 130.15293,266.67465 L 121.4575,262.08933 L 103.94986,273.26347 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5128" + sodipodi:nodetypes="ccccc" + d="M 156.01098,239.64164 L 164.60219,243.86025 L 180.14702,233.7166 L 171.54021,229.64474 L 156.01098,239.64164 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5132" + sodipodi:nodetypes="ccccc" + d="M 171.60139,229.6086 L 180.08668,233.62387 L 195.07741,223.67266 L 186.68482,219.84353 L 171.60139,229.6086 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5130" + sodipodi:nodetypes="ccccc" + d="M 186.53932,219.8472 L 195.14618,223.77328 L 207.96039,215.29699 L 199.53214,211.5709 L 186.53932,219.8472 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5134" + sodipodi:nodetypes="ccccc" + d="M 199.53942,211.55443 L 208.00833,215.29699 L 219.61158,207.48873 L 211.43624,203.97858 L 199.53942,211.55443 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5136" + sodipodi:nodetypes="ccccc" + d="M 211.48298,203.99346 L 219.48498,207.43045 L 231.12889,200.02493 L 223.45622,196.26063 L 211.48298,203.99346 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2172" + sodipodi:nodetypes="ccccc" + d="M 68.307573,278.59889 L 233.3461,375.01483 L 393.09703,219.55258 L 250.10479,166.11978 L 68.307573,278.59889 z " + style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="cccccc" + id="path16273" + d="M 277.69986,176.50516 C 177.30035,235.42715 239.60255,221.94517 101.71421,297.5792 L 189.47945,349.38523 C 273.26264,323.79193 366.41421,274.97725 355.07543,205.22753 L 295.56691,183.1214 L 277.69986,176.50516 z " + style="fill:url(#linearGradient2777);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path2174" + sodipodi:nodetypes="ccccc" + d="M 68.171626,278.57262 L 68.303363,291.78988 L 232.57786,391.18452 C 235.83055,389.43142 237.23238,379.0975 233.63174,375.00319 L 68.171626,278.57262 z " + style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path2176" + sodipodi:nodetypes="ccccc" + d="M 393.07972,219.62716 C 394.063,222.66073 395.10956,229.26471 392.24335,231.97585 L 232.84133,390.92104 C 237.0608,386.09241 235.76133,380.47536 233.63174,374.73973 L 393.07972,219.62716 z " + style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 34.974214,77.099241 C 34.974214,77.099241 63.429139,279.82741 65.53691,277.66987 C 67.644683,275.51232 249.44002,164.63325 249.44002,164.63325 L 249.96698,8.789858 L 34.974214,77.099241 z " + sodipodi:nodetypes="csccc" + id="path2178" + style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:export-ydpi="98" + inkscape:export-xdpi="98" + d="M 243.62988,7.3609944 C 248.64901,5.9522606 248.53121,7.8674281 249.93941,8.795189 L 34.974214,76.572297 C 33.041161,74.848362 32.232239,75.552395 27.898382,75.685479 L 243.62988,7.3609944 z " + sodipodi:nodetypes="ccccc" + id="path2182" + style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path3155" + sodipodi:nodetypes="ccccc" + d="M 41.127572,89.515883 L 247.04644,21.857141 L 245.56959,155.03645 L 66.892268,262.94003 L 41.127572,89.515883 z " + style="fill:url(#radialGradient3763);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + id="path4130" + sodipodi:nodetypes="ccccc" + d="M 270.30146,173.89938 L 92.059548,291.88393 L 83.064049,286.84829 L 262.53913,170.82327 L 270.30146,173.89938 z " + style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5126" + sodipodi:nodetypes="ccc" + d="M 113.88343,277.34844 L 133.41939,264.57042 L 113.88343,277.34844 z " + style="opacity:0.3;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 260.55944,282.16245 C 260.55944,282.16245 299.34305,249.72696 299.34305,249.72696 C 301.56567,247.86815 306.24993,247.63883 309.88553,249.20565 C 309.88553,249.20565 329.40032,257.90584 329.40032,257.90584 C 333.27561,259.57597 334.75677,262.55505 332.67995,264.59388 C 332.67995,264.59388 296.81833,299.27831 296.81833,299.27831 C 294.33747,301.71377 288.97351,302.06916 284.843,300.06406 C 284.843,300.06406 262.71041,289.52115 262.71041,289.52115 C 258.86144,287.6527 257.92393,284.36659 260.55944,282.16245 C 260.55944,282.16245 260.55944,282.16245 260.55944,282.16245" + sodipodi:nodetypes="cccccccccc" + id="rect2179" + style="opacity:0.88999999;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.97445869;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 156.31199,239.61556 C 158.84932,237.6711 165.25317,241.39833 164.88374,243.74344 L 156.31199,239.61556 z " + sodipodi:nodetypes="ccc" + id="path5322" + style="opacity:0.99720004;fill:#666666;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 112.5495,278.27384 L 130.07045,266.61299 C 128.6036,263.60624 125.86497,261.45071 121.46704,261.8451 L 104.04747,273.28768 C 107.08494,271.21271 112.98604,275.81472 112.5495,278.27384 z " + sodipodi:nodetypes="ccccc" + id="path6299" + style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 104.03121,273.29774 C 107.49791,271.05672 113.43311,276.6129 112.52259,278.36122 L 104.03121,273.29774 z " + sodipodi:nodetypes="ccc" + id="path4349" + style="opacity:0.68999999;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path8239" + sodipodi:nodetypes="ccccc" + d="M 164.84019,243.7543 L 179.63884,233.97378 C 178.89388,231.15559 176.668,229.33065 171.42447,229.78669 L 156.27423,239.63114 C 158.95009,237.51097 165.57347,241.76179 164.84019,243.7543 z " + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 179.54294,233.98976 L 194.50141,224.24118 C 193.46879,221.32711 190.97123,219.43825 185.96742,220.29382 L 170.84915,230.10631 C 173.88125,228.78365 179.05461,230.45474 179.54294,233.98976 z " + sodipodi:nodetypes="ccccc" + id="path8241" + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path8243" + sodipodi:nodetypes="ccccc" + d="M 194.43749,224.22521 L 207.70194,215.40355 C 205.96614,212.71322 203.80418,211.59145 199.45559,211.72786 L 185.90349,220.3098 C 189.09541,219.56246 193.62952,220.65822 194.43749,224.22521 z " + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 207.74987,215.38757 L 219.33629,207.66862 C 217.34479,205.17006 215.05499,204.11222 211.28174,204.12078 L 199.40765,211.74385 C 202.7434,211.44397 205.80725,212.3959 207.74987,215.38757 z " + sodipodi:nodetypes="ccccc" + id="path8245" + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path8247" + sodipodi:nodetypes="ccccc" + d="M 219.30433,207.70059 L 231.03457,199.96565 C 229.55448,196.50822 226.62543,195.89785 223.29965,196.19408 L 211.04202,204.2646 C 214.01019,203.93277 216.67451,204.42126 219.30433,207.70059 z " + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="rect2202" + sodipodi:nodetypes="cccccccccc" + d="M 221.68659,286.68216 C 221.68659,286.68216 271.7827,246.38199 271.7827,246.38199 C 272.38533,245.90267 273.62888,245.89275 274.57389,246.35926 C 274.57389,246.35926 282.22513,250.1218 282.22513,250.1218 C 283.18438,250.59535 283.48058,251.37156 282.88624,251.86264 C 282.88624,251.86264 233.55382,293.3224 233.55382,293.3224 C 232.81433,293.93337 231.39356,293.96431 230.37209,293.3911 C 230.37209,293.3911 222.1464,288.7742 222.1464,288.7742 C 221.14187,288.21048 220.9385,287.27719 221.68659,286.68216 C 221.68659,286.68216 221.68659,286.68216 221.68659,286.68216" + style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path2449" + d="M 219.03426,288.29491 L 230.39646,294.74997 L 222.03124,301.79784 L 210.47874,295.2849 L 219.03426,288.29491 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2451" + d="M 209.4152,296.07932 L 221.07617,302.55531 L 212.38161,309.86664 L 201.01939,302.85172 L 209.4152,296.07932 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2453" + d="M 200.12349,303.5516 L 211.48907,310.55261 L 202.5404,317.99996 L 190.60096,311.18361 L 200.12349,303.5516 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2455" + d="M 189.70952,311.95745 L 201.6931,318.78476 L 190.71106,328.04619 L 178.53214,321.0788 L 189.70952,311.95745 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2457" + sodipodi:nodetypes="ccccc" + d="M 273.68393,244.42789 L 284.19406,249.63047 L 292.16408,243.02723 L 281.60172,238.10854 L 273.68393,244.42789 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2459" + sodipodi:nodetypes="ccccc" + d="M 282.58552,237.35878 L 290.13787,231.26634 L 300.94177,235.89096 L 293.0886,242.26102 L 282.58552,237.35878 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2461" + sodipodi:nodetypes="ccccc" + d="M 291.16226,230.48888 L 301.87328,235.11565 L 309.36795,228.87453 L 299.02555,224.08989 L 291.16226,230.48888 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2463" + sodipodi:nodetypes="ccccc" + d="M 289.93538,230.01549 L 280.93975,225.55672 L 289.03737,219.25932 L 297.95995,223.58772 L 289.93538,230.01549 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2465" + d="M 177.42321,320.41889 L 166.48786,314.26225 L 188.72682,297.2375 L 199.03429,302.90648 L 177.42321,320.41889 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2467" + sodipodi:nodetypes="ccccc" + d="M 290.08046,218.43641 L 298.85735,222.73969 L 319.87746,205.75759 L 310.78391,202.19572 L 290.08046,218.43641 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2469" + sodipodi:nodetypes="ccccccc" + d="M 298.08535,210.7646 L 309.62549,201.69751 L 288.03973,193.31937 L 273.6384,203.95987 L 283.46625,207.8962 L 286.18927,205.81393 L 298.08535,210.7646 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2471" + sodipodi:nodetypes="ccccc" + d="M 286.62375,192.75442 L 266.91147,207.38925 L 257.59069,203.41102 L 277.74109,189.32694 L 286.62375,192.75442 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2473" + d="M 165.36066,313.63832 L 184.46965,298.98505 L 172.33203,292.59392 L 153.19109,306.83736 L 165.36066,313.63832 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2475" + d="M 151.95557,306.23713 L 167.0892,294.93513 L 156.09726,289.01503 L 140.88977,299.91382 L 151.95557,306.23713 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2477" + d="M 139.74507,299.3323 L 150.85746,291.335 L 140.35153,285.53863 L 129.36288,293.43149 L 139.74507,299.3323 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2497" + d="M 141.44916,284.84419 L 150.48582,278.42628 L 160.93153,283.91261 L 151.79607,290.59399 L 141.44916,284.84419 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2499" + d="M 200.06639,302.0818 L 208.31119,295.49502 L 197.64102,290.05561 L 189.67659,296.43646 L 200.06639,302.0818 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2501" + d="M 185.44374,298.19559 L 193.86354,291.59284 L 181.96116,285.25056 L 173.47309,291.62812 L 185.44374,298.19559 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2503" + d="M 168.25222,294.17765 L 176.70261,287.76919 L 165.64522,281.91859 L 157.20372,288.20299 L 168.25222,294.17765 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 151.55607,277.71812 L 160.35985,271.62622 L 170.55671,277.01143 L 161.94955,283.18847 L 151.55607,277.71812 z " + sodipodi:nodetypes="ccccc" + id="path2505" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2507" + d="M 177.75365,287.00502 L 186.23698,280.4648 L 175.54186,274.93789 L 166.63928,281.22229 L 177.75365,287.00502 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2509" + d="M 161.45339,270.89479 L 170.04758,264.82618 L 180.19785,270.07166 L 171.68385,276.24869 L 161.45339,270.89479 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2511" + d="M 194.72496,290.89785 L 203.34236,284.16335 L 191.50585,278.08454 L 183.01779,284.4621 L 194.72496,290.89785 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2513" + d="M 209.44739,294.73981 L 218.00506,287.74135 L 207.50073,282.4405 L 198.6953,289.16034 L 209.44739,294.73981 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2515" + d="M 219.03117,287.00034 L 227.95111,279.8372 L 217.57851,274.66809 L 208.54255,281.61848 L 219.03117,287.00034 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2517" + d="M 229.01016,279.06327 L 236.63632,273.08946 L 226.18986,268.0248 L 218.58741,273.879 L 229.01016,279.06327 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2519" + d="M 237.61157,272.25159 L 246.12267,265.48601 L 235.81592,260.50649 L 227.24338,267.18776 L 237.61157,272.25159 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 246.92622,264.70405 L 255.31922,258.03494 L 245.2101,253.19516 L 236.79987,259.71739 L 246.92622,264.70405 z " + sodipodi:nodetypes="ccccc" + id="path2521" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2523" + sodipodi:nodetypes="ccccc" + d="M 256.24944,257.30562 L 264.00138,251.14415 L 253.91623,246.49536 L 246.24255,252.47136 L 256.24944,257.30562 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 264.94343,250.38346 L 271.79281,244.57197 L 262.14241,240.07284 L 254.8806,245.75341 L 264.94343,250.38346 z " + sodipodi:nodetypes="ccccc" + id="path2525" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2527" + sodipodi:nodetypes="ccccc" + d="M 272.6829,243.79667 L 280.45445,237.62292 L 270.90284,233.32138 L 263.17995,239.29836 L 272.6829,243.79667 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 281.41039,236.84761 L 288.91847,230.83852 L 279.86087,226.40525 L 272.03919,232.4481 L 281.41039,236.84761 z " + sodipodi:nodetypes="ccccc" + id="path2529" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2531" + d="M 171.18769,264.04817 L 180.52708,257.51381 L 190.25819,262.57298 L 181.32501,269.3555 L 171.18769,264.04817 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2533" + sodipodi:nodetypes="ccccc" + d="M 181.56334,256.75316 L 190.15752,250.68454 L 199.60916,255.83687 L 191.23489,261.73446 L 181.56334,256.75316 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 191.13838,249.97224 L 200.12848,243.67074 L 209.58148,248.76519 L 200.57843,255.12855 L 191.13838,249.97224 z " + sodipodi:nodetypes="ccccc" + id="path2535" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2537" + sodipodi:nodetypes="ccccc" + d="M 201.14148,242.94942 L 208.96784,237.50889 L 218.76812,242.01382 L 210.62107,248.07043 L 201.14148,242.94942 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 209.93861,236.70829 L 218.48624,230.77939 L 228.17074,235.18651 L 219.79647,241.31698 L 209.93861,236.70829 z " + sodipodi:nodetypes="ccccc" + id="path2539" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2541" + sodipodi:nodetypes="ccccc" + d="M 219.48896,230.08656 L 227.94108,224.18895 L 237.62559,228.38647 L 229.19014,234.46609 L 219.48896,230.08656 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 229.04981,223.34939 L 237.64399,217.28078 L 247.18879,221.5016 L 238.67479,227.67863 L 229.04981,223.34939 z " + sodipodi:nodetypes="ccccc" + id="path2543" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2545" + sodipodi:nodetypes="ccccc" + d="M 238.75966,216.51376 L 246.46891,211.02733 L 256.03698,215.155 L 248.29149,220.74984 L 238.75966,216.51376 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 247.52515,210.2999 L 256.52687,204.13813 L 265.95523,208.14936 L 257.24328,214.48941 L 247.52515,210.2999 z " + sodipodi:nodetypes="ccccc" + id="path2547" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2242" + d="M 192.42707,277.38276 L 201.04243,270.93376 L 212.81727,276.9437 L 204.25518,283.46405 L 192.42707,277.38276 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2244" + d="M 202.01582,270.12726 L 210.21341,264.10219 L 221.92044,269.93134 L 213.74806,276.20857 L 202.01582,270.12726 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2246" + d="M 211.22102,263.35122 L 219.52192,257.1363 L 231.06172,262.8037 L 222.85738,269.20877 L 211.22102,263.35122 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2248" + sodipodi:nodetypes="ccccc" + d="M 220.48694,256.41147 L 231.99345,262.05286 L 239.51447,256.25393 L 228.07804,250.72214 C 228.07804,250.72214 220.51891,256.44343 220.48694,256.41147 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 228.98488,249.90241 L 240.4914,255.5438 L 248.01241,249.74486 L 236.57599,244.21308 C 236.57599,244.21308 229.01684,249.93436 228.98488,249.90241 z " + sodipodi:nodetypes="ccccc" + id="path2250" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2252" + sodipodi:nodetypes="ccccc" + d="M 237.56868,243.44725 L 248.97931,248.99274 L 257.07404,242.57877 L 246.02277,237.37437 L 237.56868,243.44725 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 247.06561,236.56786 L 258.0749,241.84765 L 265.97947,235.69713 L 254.71266,230.82259 L 247.06561,236.56786 z " + sodipodi:nodetypes="ccccc" + id="path2254" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2256" + sodipodi:nodetypes="ccccc" + d="M 255.89713,229.9869 L 266.95163,234.90506 L 274.38224,229.19653 L 263.30742,224.43317 L 255.89713,229.9869 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 264.32207,223.68092 L 275.3101,228.44997 L 282.89504,222.58711 L 271.91318,217.99159 L 264.32207,223.68092 z " + sodipodi:nodetypes="ccccc" + id="path2258" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2260" + sodipodi:nodetypes="ccccc" + d="M 272.9276,217.22814 L 283.86907,221.82988 L 290.30525,216.88979 L 279.45645,212.28463 L 272.9276,217.22814 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 280.51495,211.48086 L 291.20783,216.17301 L 297.14678,211.54933 L 286.38838,207.05719 L 280.51495,211.48086 z " + sodipodi:nodetypes="ccccc" + id="path2262" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2264" + d="M 187.21456,279.65363 L 195.69788,273.11343 L 185.00277,267.58651 L 176.10019,273.87091 L 187.21456,279.65363 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2266" + d="M 196.93117,272.23833 L 205.68571,265.78852 L 194.71938,260.1712 L 185.8168,266.4556 L 196.93117,272.23833 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2268" + sodipodi:nodetypes="ccccc" + d="M 206.76011,265.04128 L 215.60505,258.63667 L 204.90993,253.42617 L 196.23337,259.52977 L 206.76011,265.04128 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 216.55615,257.79127 L 225.72667,251.12321 L 215.30323,245.97983 L 205.95318,252.6478 L 216.55615,257.79127 z " + sodipodi:nodetypes="ccccc" + id="path2270" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2272" + sodipodi:nodetypes="ccccc" + d="M 226.62217,250.28446 L 234.42747,244.46748 L 224.14692,239.49074 L 216.41185,245.13456 L 226.62217,250.28446 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 235.3202,243.66108 L 242.92208,238.20571 L 232.8824,233.22122 L 225.20027,238.78239 L 235.3202,243.66108 z " + sodipodi:nodetypes="ccccc" + id="path2274" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2276" + sodipodi:nodetypes="ccccc" + d="M 244.04269,237.40717 L 251.47219,231.70252 L 241.73406,226.77098 L 234.01317,232.34768 L 244.04269,237.40717 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 252.6522,231.11226 L 260.3045,225.27523 L 250.58643,220.64138 L 242.89843,226.09476 L 252.6522,231.11226 z " + sodipodi:nodetypes="ccccc" + id="path2278" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2280" + sodipodi:nodetypes="ccccc" + d="M 261.23976,224.42239 L 268.45876,219.21044 L 258.80517,214.83417 L 251.72352,219.79033 L 261.23976,224.42239 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 269.52519,218.43638 L 275.83883,213.61385 L 266.36416,209.30737 L 259.8121,214.10012 L 269.52519,218.43638 z " + sodipodi:nodetypes="ccccc" + id="path2282" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2284" + sodipodi:nodetypes="ccccc" + d="M 276.80269,212.87655 L 282.2575,208.64164 L 272.64721,204.83239 L 267.406,208.63069 L 276.80269,212.87655 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="rect3291" + d="M 302.87759,234.27933 C 302.87759,234.27933 308.38212,229.69548 308.38212,229.69548 C 308.92862,229.24038 308.77424,228.59985 308.03355,228.25718 C 308.03355,228.25718 300.43832,224.74347 300.43832,224.74347 C 299.65719,224.38211 298.56475,224.46487 297.99153,224.93134 C 297.99153,224.93134 292.2163,229.63113 292.2163,229.63113 C 291.63275,230.10601 291.81664,230.77156 292.62585,231.12111 C 292.62585,231.12111 300.49174,234.51889 300.49174,234.51889 C 301.25859,234.85015 302.32155,234.74235 302.87759,234.27933 C 302.87759,234.27933 302.87759,234.27933 302.87759,234.27933" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3293" + d="M 235.3731,272.47703 C 235.3731,272.47703 227.42329,268.6228 227.42329,268.6228 C 226.73948,268.29128 225.78761,268.33455 225.2875,268.71966 C 225.2875,268.71966 219.50192,273.17479 219.50192,273.17479 C 218.99549,273.56477 219.13572,274.15174 219.81793,274.49107 C 219.81793,274.49107 227.7497,278.43631 227.7497,278.43631 C 228.44749,278.78339 229.41954,278.74258 229.9276,278.34461 C 229.9276,278.34461 235.73123,273.79845 235.73123,273.79845 C 236.23285,273.4055 236.07241,272.81607 235.3731,272.47703 C 235.3731,272.47703 235.3731,272.47703 235.3731,272.47703" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3299" + d="M 247.43674,211.44485 C 247.43674,211.44485 255.04475,214.72695 255.04475,214.72695 C 255.59412,214.96395 255.68572,215.40873 255.24904,215.72416 C 255.24904,215.72416 249.09015,220.17295 249.09015,220.17295 C 248.64784,220.49244 247.85024,220.55375 247.30291,220.31051 C 247.30291,220.31051 239.72373,216.94221 239.72373,216.94221 C 239.18929,216.7047 239.11431,216.26136 239.55452,215.94809 C 239.55452,215.94809 245.68459,211.58552 245.68459,211.58552 C 246.11924,211.27618 246.90023,211.21341 247.43674,211.44485 C 247.43674,211.44485 247.43674,211.44485 247.43674,211.44485" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3301" + d="M 211.38791,264.687 C 211.38791,264.687 220.69642,269.32187 220.69642,269.32187 C 221.37384,269.65917 221.55369,270.21304 221.09728,270.56361 C 221.09728,270.56361 214.59913,275.55485 214.59913,275.55485 C 214.12804,275.9167 213.20005,275.92451 212.52093,275.57249 C 212.52093,275.57249 203.19239,270.73714 203.19239,270.73714 C 202.53988,270.39891 202.39682,269.84723 202.86919,269.50005 C 202.86919,269.50005 209.38739,264.7093 209.38739,264.7093 C 209.84537,264.37269 210.73656,264.36268 211.38791,264.687 C 211.38791,264.687 211.38791,264.687 211.38791,264.687" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3305" + d="M 190.80124,251.03544 C 190.80124,251.03544 198.99762,255.5035 198.99762,255.5035 C 199.33669,255.68835 199.3609,256.01171 199.05255,256.22886 C 199.05255,256.22886 191.79012,261.34344 191.79012,261.34344 C 191.48252,261.56007 190.95608,261.59085 190.60912,261.41215 C 190.60912,261.41215 182.222,257.0924 182.222,257.0924 C 181.85741,256.90462 181.81745,256.57372 182.13313,256.35082 C 182.13313,256.35082 189.58629,251.08792 189.58629,251.08792 C 189.90275,250.86444 190.44492,250.8412 190.80124,251.03544 C 190.80124,251.03544 190.80124,251.03544 190.80124,251.03544" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3309" + d="M 200.86922,319.47957 C 200.86922,319.47957 191.5626,327.32806 191.5626,327.32806 C 191.09111,327.72566 190.29604,327.80875 189.78059,327.51386 C 189.78059,327.51386 179.45944,321.6093 179.45944,321.6093 C 178.94568,321.31539 178.91894,320.76315 179.3988,320.37157 C 179.3988,320.37157 188.87097,312.64175 188.87097,312.64175 C 189.33581,312.26242 190.11645,312.18928 190.62199,312.47729 C 190.62199,312.47729 200.77757,318.26317 200.77757,318.26317 C 201.28475,318.55212 201.32593,319.09442 200.86922,319.47957 C 200.86922,319.47957 200.86922,319.47957 200.86922,319.47957" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3311" + d="M 227.03328,279.37981 C 227.03328,279.37981 218.4746,275.11465 218.4746,275.11465 C 217.9779,274.86712 217.23368,274.93335 216.80449,275.26346 C 216.80449,275.26346 209.3489,280.99824 209.3489,280.99824 C 208.90258,281.34154 208.94628,281.82562 209.44841,282.08328 C 209.44841,282.08328 218.10282,286.52398 218.10282,286.52398 C 218.61686,286.78775 219.38667,286.71486 219.82736,286.36095 C 219.82736,286.36095 227.18725,280.45063 227.18725,280.45063 C 227.61081,280.1105 227.5415,279.63307 227.03328,279.37981 C 227.03328,279.37981 227.03328,279.37981 227.03328,279.37981" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3313" + d="M 200.87727,244.07429 C 200.87727,244.07429 208.8242,248.35707 208.8242,248.35707 C 209.24365,248.58313 209.26299,248.99029 208.86725,249.27001 C 208.86725,249.27001 201.29858,254.61953 201.29858,254.61953 C 200.89969,254.90148 200.24107,254.94426 199.82216,254.71545 C 199.82216,254.71545 191.88613,250.38067 191.88613,250.38067 C 191.47181,250.15435 191.45917,249.7474 191.8575,249.46819 C 191.8575,249.46819 199.41526,244.17067 199.41526,244.17067 C 199.81044,243.89367 200.46234,243.85067 200.87727,244.07429 C 200.87727,244.07429 200.87727,244.07429 200.87727,244.07429" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3317" + d="M 168.98052,312.35404 C 168.98052,312.35404 186.52228,298.92516 186.52228,298.92516 C 187.74742,297.98727 189.20942,297.50291 189.80916,297.83277 C 189.80916,297.83277 197.94313,302.30636 197.94313,302.30636 C 198.54751,302.63875 198.08283,303.67748 196.89261,304.64197 C 196.89261,304.64197 179.8462,318.45547 179.8462,318.45547 C 178.50836,319.53957 176.90663,320.12806 176.26526,319.76696 C 176.26526,319.76696 167.6358,314.90855 167.6358,314.90855 C 166.99971,314.55042 167.60418,313.40767 168.98052,312.35404 C 168.98052,312.35404 168.98052,312.35404 168.98052,312.35404" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3319" + d="M 192.5101,290.87165 C 192.5101,290.87165 183.27464,285.95046 183.27464,285.95046 C 182.54633,285.56237 181.54412,285.5639 181.0251,285.95388 C 181.0251,285.95388 174.43893,290.90244 174.43893,290.90244 C 173.90432,291.3041 174.06146,291.95091 174.79375,292.35267 C 174.79375,292.35267 184.08217,297.44861 184.08217,297.44861 C 184.83581,297.86207 185.87162,297.86005 186.40206,297.44408 C 186.40206,297.44408 192.93525,292.32079 192.93525,292.32079 C 193.44997,291.91714 193.25925,291.27084 192.5101,290.87165 C 192.5101,290.87165 192.5101,290.87165 192.5101,290.87165" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3321" + d="M 185.1382,279.89699 C 185.1382,279.89699 176.63387,275.50221 176.63387,275.50221 C 176.02877,275.18952 175.15088,275.21388 174.66322,275.55813 C 174.66322,275.55813 167.58535,280.55447 167.58535,280.55447 C 167.06223,280.92372 167.14522,281.48553 167.77396,281.81266 C 167.77396,281.81266 176.61165,286.41084 176.61165,286.41084 C 177.2442,286.73996 178.15674,286.69425 178.65527,286.3099 C 178.65527,286.3099 185.39983,281.11021 185.39983,281.11021 C 185.86446,280.752 185.74682,280.2115 185.1382,279.89699 C 185.1382,279.89699 185.1382,279.89699 185.1382,279.89699" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3323" + d="M 161.12258,272.02905 C 161.12258,272.02905 169.7964,276.60989 169.7964,276.60989 C 170.21763,276.83236 170.27487,277.2137 169.92432,277.46528 C 169.92432,277.46528 162.60291,282.7196 162.60291,282.7196 C 162.24117,282.9792 161.60395,283.00656 161.17461,282.7806 C 161.17461,282.7806 152.33353,278.12732 152.33353,278.12732 C 151.90283,277.90063 151.85435,277.51172 152.22438,277.25569 C 152.22438,277.25569 159.71303,272.0738 159.71303,272.0738 C 160.07158,271.82571 160.70003,271.80589 161.12258,272.02905 C 161.12258,272.02905 161.12258,272.02905 161.12258,272.02905" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3325" + d="M 220.2904,303.21609 C 220.2904,303.21609 213.19436,309.1832 213.19436,309.1832 C 212.74444,309.56153 211.88835,309.56212 211.27912,309.18598 C 211.27912,309.18598 202.00839,303.46232 202.00839,303.46232 C 201.45919,303.12325 201.36941,302.5694 201.80347,302.21926 C 201.80347,302.21926 208.65568,296.69198 208.65568,296.69198 C 209.07675,296.35232 209.86703,296.33025 210.43121,296.64355 C 210.43121,296.64355 219.94579,301.92755 219.94579,301.92755 C 220.57046,302.27448 220.72603,302.84974 220.2904,303.21609 C 220.2904,303.21609 220.2904,303.21609 220.2904,303.21609" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3327" + d="M 250.18155,231.04893 C 250.18155,231.04893 243.02922,227.42687 243.02922,227.42687 C 242.31181,227.06355 241.28875,227.09263 240.73358,227.49361 C 240.73358,227.49361 235.06335,231.58914 235.06335,231.58914 C 234.48252,232.00866 234.60822,232.64785 235.34714,233.02061 C 235.34714,233.02061 242.71349,236.73664 242.71349,236.73664 C 243.44994,237.10815 244.49431,237.0604 245.05318,236.63127 C 245.05318,236.63127 250.50942,232.44177 250.50942,232.44177 C 251.04367,232.03155 250.89663,231.41106 250.18155,231.04893 C 250.18155,231.04893 250.18155,231.04893 250.18155,231.04893" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3329" + d="M 241.73896,237.6183 C 241.73896,237.6183 234.09875,233.82511 234.09875,233.82511 C 233.42547,233.49084 232.47146,233.51871 231.96125,233.88804 C 231.96125,233.88804 226.11501,238.1202 226.11501,238.1202 C 225.60814,238.48713 225.74764,239.04628 226.42628,239.37343 C 226.42628,239.37343 234.12755,243.08611 234.12755,243.08611 C 234.78882,243.40491 235.72382,243.37142 236.22543,243.01146 C 236.22543,243.01146 242.01062,238.85982 242.01062,238.85982 C 242.51547,238.49751 242.39495,237.94399 241.73896,237.6183 C 241.73896,237.6183 241.73896,237.6183 241.73896,237.6183" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3331" + d="M 291.06923,231.66501 C 291.06923,231.66501 299.93537,235.46017 299.93537,235.46017 C 300.49195,235.69842 300.62867,236.14494 300.23967,236.46046 C 300.23967,236.46046 293.79405,241.68879 293.79405,241.68879 C 293.40327,242.00578 292.65126,242.0569 292.11015,241.80432 C 292.11015,241.80432 283.49087,237.78134 283.49087,237.78134 C 282.98847,237.54685 282.88811,237.11468 283.26389,236.81155 C 283.26389,236.81155 289.4626,231.81108 289.4626,231.81108 C 289.83674,231.50927 290.5524,231.44378 291.06923,231.66501 C 291.06923,231.66501 291.06923,231.66501 291.06923,231.66501" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3333" + d="M 214.6618,258.17714 C 214.6618,258.17714 205.79323,253.8565 205.79323,253.8565 C 205.30323,253.61778 204.58399,253.65545 204.17864,253.94061 C 204.17864,253.94061 196.98327,259.00223 196.98327,259.00223 C 196.56804,259.29434 196.62028,259.73235 197.10235,259.98475 C 197.10235,259.98475 205.83129,264.55497 205.83129,264.55497 C 206.34512,264.82401 207.10144,264.79412 207.5249,264.48748 C 207.5249,264.48748 214.85991,259.17623 214.85991,259.17623 C 215.27294,258.87715 215.18363,258.43136 214.6618,258.17714 C 214.6618,258.17714 214.6618,258.17714 214.6618,258.17714" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3335" + d="M 238.61522,217.71027 C 238.61522,217.71027 246.20482,221.06648 246.20482,221.06648 C 246.74975,221.30745 246.80537,221.77975 246.32841,222.12581 C 246.32841,222.12581 239.55856,227.03746 239.55856,227.03746 C 239.06928,227.39242 238.23201,227.47949 237.68245,227.2323 C 237.68245,227.2323 230.0291,223.78987 230.0291,223.78987 C 229.48641,223.54578 229.44798,223.06823 229.94181,222.71952 C 229.94181,222.71952 236.77542,217.89409 236.77542,217.89409 C 237.25693,217.55409 238.07699,217.47225 238.61522,217.71027 C 238.61522,217.71027 238.61522,217.71027 238.61522,217.71027" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3337" + d="M 228.91723,224.61205 C 228.91723,224.61205 236.61775,227.94965 236.61775,227.94965 C 237.17563,228.19144 237.24665,228.65958 236.77513,228.99941 C 236.77513,228.99941 230.06777,233.83356 230.06777,233.83356 C 229.58195,234.18369 228.73926,234.26255 228.18027,234.01019 C 228.18027,234.01019 220.46652,230.52787 220.46652,230.52787 C 219.92451,230.28318 219.88144,229.8127 220.36809,229.47313 C 220.36809,229.47313 227.0887,224.78372 227.0887,224.78372 C 227.56129,224.45396 228.37602,224.37747 228.91723,224.61205 C 228.91723,224.61205 228.91723,224.61205 228.91723,224.61205" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3339" + d="M 257.14967,204.40311 C 257.14967,204.40311 265.32623,207.88176 265.32623,207.88176 C 265.67464,208.02998 265.706,208.33073 265.3957,208.55655 C 265.3957,208.55655 257.84099,214.05443 257.84099,214.05443 C 257.51021,214.29515 256.95402,214.3647 256.59485,214.20987 C 256.59485,214.20987 248.16698,210.57659 248.16698,210.57659 C 247.81135,210.42328 247.8009,210.11114 248.14264,209.87723 C 248.14264,209.87723 255.94863,204.53395 255.94863,204.53395 C 256.2693,204.31446 256.80459,204.25629 257.14967,204.40311 C 257.14967,204.40311 257.14967,204.40311 257.14967,204.40311" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3341" + d="M 201.8511,271.34651 C 201.8511,271.34651 212.00249,276.52784 212.00249,276.52784 C 212.45381,276.7582 212.55592,277.14272 212.23112,277.39008 C 212.23112,277.39008 204.84954,283.01143 204.84954,283.01143 C 204.52035,283.26212 203.89008,283.27634 203.43671,283.04324 C 203.43671,283.04324 193.23937,277.8004 193.23937,277.8004 C 192.78929,277.56899 192.69387,277.18305 193.0251,276.9351 C 193.0251,276.9351 200.45262,271.37525 200.45262,271.37525 C 200.77944,271.1306 201.40305,271.11781 201.8511,271.34651 C 201.8511,271.34651 201.8511,271.34651 201.8511,271.34651" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3343" + d="M 219.21667,231.1118 C 219.21667,231.1118 227.42383,234.84661 227.42383,234.84661 C 227.83744,235.03484 227.89363,235.38938 227.54865,235.64192 C 227.54865,235.64192 220.45221,240.83693 220.45221,240.83693 C 220.08929,241.10262 219.45708,241.15831 219.03594,240.96141 C 219.03594,240.96141 210.6819,237.05577 210.6819,237.05577 C 210.26995,236.86319 210.23739,236.50104 210.60771,236.24418 C 210.60771,236.24418 217.85107,231.21996 217.85107,231.21996 C 218.20329,230.97566 218.81185,230.92758 219.21667,231.1118 C 219.21667,231.1118 219.21667,231.1118 219.21667,231.1118" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3345" + d="M 166.11203,294.40885 C 166.11203,294.40885 157.04241,289.52408 157.04241,289.52408 C 156.51843,289.24186 155.52217,289.42717 154.80588,289.94053 C 154.80588,289.94053 142.25863,298.93279 142.25863,298.93279 C 141.50124,299.47559 141.31352,300.15596 141.84075,300.45723 C 141.84075,300.45723 150.97129,305.67469 150.97129,305.67469 C 151.51617,305.98604 152.56442,305.78244 153.31853,305.21926 C 153.31853,305.21926 165.80481,295.89435 165.80481,295.89435 C 166.51725,295.36228 166.65299,294.70021 166.11203,294.40885 C 166.11203,294.40885 166.11203,294.40885 166.11203,294.40885" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3349" + d="M 151.01455,278.70397 C 151.01455,278.70397 160.37455,283.62006 160.37455,283.62006 C 160.68292,283.78202 160.7229,284.06518 160.46357,284.25485 C 160.46357,284.25485 152.27741,290.24195 152.27741,290.24195 C 152.01084,290.43692 151.5497,290.45709 151.24414,290.28729 C 151.24414,290.28729 141.97268,285.13512 141.97268,285.13512 C 141.68247,284.97384 141.66152,284.69337 141.92511,284.50616 C 141.92511,284.50616 150.02275,278.75515 150.02275,278.75515 C 150.27937,278.57289 150.72146,278.55003 151.01455,278.70397 C 151.01455,278.70397 151.01455,278.70397 151.01455,278.70397" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3351" + d="M 175.52443,287.1458 C 175.52443,287.1458 166.79876,282.52893 166.79876,282.52893 C 166.15936,282.19062 165.25137,282.21179 164.76171,282.57631 C 164.76171,282.57631 158.10028,287.53553 158.10028,287.53553 C 157.60379,287.90516 157.7174,288.48077 158.35625,288.82623 C 158.35625,288.82623 167.07489,293.541 167.07489,293.541 C 167.72676,293.8935 168.65274,293.87392 169.1498,293.49696 C 169.1498,293.49696 175.81827,288.43985 175.81827,288.43985 C 176.30838,288.06815 176.17677,287.49096 175.52443,287.1458 C 175.52443,287.1458 175.52443,287.1458 175.52443,287.1458" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3353" + d="M 201.97097,283.45905 C 201.97097,283.45905 192.78768,278.74285 192.78768,278.74285 C 192.07622,278.37747 191.08839,278.3982 190.5689,278.78852 C 190.5689,278.78852 183.98269,283.7371 183.98269,283.7371 C 183.44857,284.1384 183.58158,284.77203 184.28486,285.15865 C 184.28486,285.15865 193.36778,290.15177 193.36778,290.15177 C 194.11817,290.56428 195.16258,290.55584 195.70514,290.13183 C 195.70514,290.13183 202.39168,284.90631 202.39168,284.90631 C 202.9188,284.49436 202.72924,283.84847 201.97097,283.45905 C 201.97097,283.45905 201.97097,283.45905 201.97097,283.45905" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3355" + d="M 183.209,298.32124 C 183.209,298.32124 173.55788,293.23939 173.55788,293.23939 C 172.87828,292.88156 171.48018,293.22782 170.41912,294.01738 C 170.41912,294.01738 155.20003,305.34244 155.20003,305.34244 C 154.08849,306.16958 153.73855,307.14332 154.41967,307.52396 C 154.41967,307.52396 164.0962,312.93167 164.0962,312.93167 C 164.79619,313.32286 166.25692,312.95103 167.36704,312.09976 C 167.36704,312.09976 182.56068,300.44889 182.56068,300.44889 C 183.61957,299.63691 183.90689,298.68872 183.209,298.32124 C 183.209,298.32124 183.209,298.32124 183.209,298.32124" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3357" + d="M 181.25173,257.89055 C 181.25173,257.89055 189.52937,262.19408 189.52937,262.19408 C 189.93309,262.40396 189.973,262.78951 189.61763,263.05932 C 189.61763,263.05932 182.01974,268.82803 182.01974,268.82803 C 181.63543,269.11981 180.98628,269.17817 180.56566,268.95795 C 180.56566,268.95795 171.94248,264.44335 171.94248,264.44335 C 171.5243,264.2244 171.5122,263.82114 171.91393,263.54006 C 171.91393,263.54006 179.85732,257.98241 179.85732,257.98241 C 180.2289,257.72244 180.85025,257.68182 181.25173,257.89055 C 181.25173,257.89055 181.25173,257.89055 181.25173,257.89055" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3359" + d="M 207.30074,294.9799 C 207.30074,294.9799 198.59287,290.54083 198.59287,290.54083 C 198.06485,290.27167 197.31412,290.31751 196.90812,290.6428 C 196.90812,290.6428 190.40778,295.85065 190.40778,295.85065 C 190.00268,296.17521 190.08929,296.66071 190.60345,296.94008 C 190.60345,296.94008 199.08253,301.54721 199.08253,301.54721 C 199.62684,301.84297 200.40395,301.81213 200.82331,301.47711 C 200.82331,301.47711 207.55245,296.10117 207.55245,296.10117 C 207.97277,295.76537 207.85976,295.26488 207.30074,294.9799 C 207.30074,294.9799 207.30074,294.9799 207.30074,294.9799" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3361" + d="M 210.31834,311.52693 C 210.31834,311.52693 203.74605,316.99659 203.74605,316.99659 C 203.0788,317.55189 201.86,317.61152 201.01035,317.12644 C 201.01035,317.12644 192.24289,312.121 192.24289,312.121 C 191.3355,311.60296 191.17426,310.72413 191.88471,310.15474 C 191.88471,310.15474 198.87844,304.54948 198.87844,304.54948 C 199.56891,303.99608 200.82226,303.98204 201.68556,304.51382 C 201.68556,304.51382 210.03168,309.65489 210.03168,309.65489 C 210.84096,310.15338 210.96757,310.98662 210.31834,311.52693 C 210.31834,311.52693 210.31834,311.52693 210.31834,311.52693" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3363" + d="M 229.40869,295.58217 C 229.40869,295.58217 223.04271,300.94565 223.04271,300.94565 C 222.48275,301.41742 221.41805,301.45214 220.65599,301.0225 C 220.65599,301.0225 211.86431,296.06605 211.86431,296.06605 C 211.09687,295.63339 210.94055,294.9076 211.51326,294.43967 C 211.51326,294.43967 218.02408,289.12024 218.02408,289.12024 C 218.58411,288.66268 219.64217,288.64028 220.39694,289.06907 C 220.39694,289.06907 229.04379,293.98149 229.04379,293.98149 C 229.79334,294.40733 229.95631,295.12081 229.40869,295.58217 C 229.40869,295.58217 229.40869,295.58217 229.40869,295.58217" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3365" + d="M 217.05231,287.26056 C 217.05231,287.26056 208.43084,282.90987 208.43084,282.90987 C 207.9153,282.6497 207.16011,282.70044 206.73573,283.02431 C 206.73573,283.02431 199.50923,288.5392 199.50923,288.5392 C 199.05894,288.88283 199.11946,289.38044 199.64696,289.65417 C 199.64696,289.65417 208.47177,294.23353 208.47177,294.23353 C 209.01197,294.51386 209.80095,294.45066 210.23875,294.09263 C 210.23875,294.09263 217.26191,288.3491 217.26191,288.3491 C 217.67418,288.01194 217.57986,287.52677 217.05231,287.26056 C 217.05231,287.26056 217.05231,287.26056 217.05231,287.26056" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3367" + d="M 170.90895,265.27133 C 170.90895,265.27133 179.32655,269.62137 179.32655,269.62137 C 179.80913,269.87078 179.87784,270.30382 179.47992,270.59253 C 179.47992,270.59253 172.41934,275.71508 172.41934,275.71508 C 172.01209,276.01056 171.29204,276.04365 170.8056,275.78907 C 170.8056,275.78907 162.3215,271.34911 162.3215,271.34911 C 161.84045,271.09734 161.7847,270.66085 162.19576,270.37059 C 162.19576,270.37059 169.32283,265.33795 169.32283,265.33795 C 169.72453,265.05428 170.43163,265.02465 170.90895,265.27133 C 170.90895,265.27133 170.90895,265.27133 170.90895,265.27133" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3369" + d="M 204.55438,265.20903 C 204.55438,265.20903 195.83444,260.74237 195.83444,260.74237 C 195.21646,260.42583 194.32033,260.45289 193.82368,260.80347 C 193.82368,260.80347 186.74496,265.80041 186.74496,265.80041 C 186.23123,266.16305 186.32053,266.71769 186.94675,267.04351 C 186.94675,267.04351 195.78441,271.64167 195.78441,271.64167 C 196.41947,271.97209 197.33877,271.93803 197.84403,271.56578 C 197.84403,271.56578 204.80505,266.43735 204.80505,266.43735 C 205.29337,266.07757 205.18091,265.52994 204.55438,265.20903 C 204.55438,265.20903 204.55438,265.20903 204.55438,265.20903" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3371" + d="M 194.5991,272.5456 C 194.5991,272.5456 186.09478,268.15083 186.09478,268.15083 C 185.48971,267.83814 184.61179,267.8625 184.12412,268.20675 C 184.12412,268.20675 177.04625,273.20308 177.04625,273.20308 C 176.52314,273.57235 176.60616,274.13416 177.23487,274.46127 C 177.23487,274.46127 186.07255,279.05946 186.07255,279.05946 C 186.70512,279.38857 187.61765,279.34288 188.11618,278.95853 C 188.11618,278.95853 194.86074,273.75884 194.86074,273.75884 C 195.32537,273.40062 195.20773,272.86013 194.5991,272.5456 C 194.5991,272.5456 194.5991,272.5456 194.5991,272.5456" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3373" + d="M 284.73208,249.18471 C 284.73208,249.18471 291.64382,243.45826 291.64382,243.45826 C 291.9322,243.21935 291.84218,242.87732 291.44316,242.6915 C 291.44316,242.6915 282.28362,238.42609 282.28362,238.42609 C 281.90552,238.25002 281.37123,238.2925 281.08461,238.52125 C 281.08461,238.52125 274.21817,244.0015 274.21817,244.0015 C 273.92236,244.23759 273.98607,244.57745 274.36212,244.76359 C 274.36212,244.76359 283.47634,249.2752 283.47634,249.2752 C 283.87359,249.47184 284.43418,249.43153 284.73208,249.18471 C 284.73208,249.18471 284.73208,249.18471 284.73208,249.18471" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3375" + d="M 266.25849,207.11055 C 266.25849,207.11055 258.22296,203.68087 258.22296,203.68087 C 257.8725,203.5313 258.26258,202.94141 259.09217,202.36158 C 259.09217,202.36158 276.45653,190.22478 276.45653,190.22478 C 277.16993,189.72614 278.01001,189.4307 278.34442,189.55973 C 278.34442,189.55973 286.00228,192.51461 286.00228,192.51461 C 286.34641,192.64741 286.06579,193.16868 285.36877,193.68615 C 285.36877,193.68615 268.38219,206.29734 268.38219,206.29734 C 267.56963,206.9006 266.62006,207.26486 266.25849,207.11055 C 266.25849,207.11055 266.25849,207.11055 266.25849,207.11055" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3377" + d="M 150.04703,290.88787 C 150.04703,290.88787 141.14376,285.97573 141.14376,285.97573 C 140.70469,285.73348 139.97814,285.80683 139.51431,286.13998 C 139.51431,286.13998 130.20189,292.82886 130.20189,292.82886 C 129.7371,293.1627 129.71188,293.62984 130.14579,293.87645 C 130.14579,293.87645 138.94417,298.87711 138.94417,298.87711 C 139.38768,299.12918 140.1235,299.05996 140.59351,298.72169 C 140.59351,298.72169 150.01083,291.94429 150.01083,291.94429 C 150.47987,291.60674 150.49581,291.13546 150.04703,290.88787 C 150.04703,290.88787 150.04703,290.88787 150.04703,290.88787" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3379" + sodipodi:nodetypes="cccccssssccccc" + d="M 308.02082,201.07469 C 308.02082,201.07469 290.25388,194.17877 290.25388,194.17877 C 289.03643,193.70622 287.49217,193.72395 286.81047,194.22762 C 286.81047,194.22762 274.92805,203.00701 274.92805,203.00701 C 274.21432,203.53434 274.06866,203.98769 275.45256,204.3729 L 282.20799,207.13372 C 283.30081,207.58033 283.29864,207.60345 284.24763,206.91193 L 285.51228,205.99038 C 285.78882,205.78886 286.61757,205.71073 287.50613,206.09359 L 296.65875,210.03701 C 297.66812,210.31796 298.54496,210.40349 299.11475,209.9558 C 299.11475,209.9558 308.63657,202.4745 308.63657,202.4745 C 309.1849,202.04368 308.91548,201.42193 308.02082,201.07469 C 308.02082,201.07469 308.02082,201.07469 308.02082,201.07469" + style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3381" + d="M 263.00018,250.94679 C 263.00018,250.94679 254.5221,247.0388 254.5221,247.0388 C 254.08593,246.83775 253.46532,246.88652 253.12956,247.148 C 253.12956,247.148 246.6785,252.17186 246.6785,252.17186 C 246.33694,252.43788 246.40946,252.82015 246.84217,253.0292 C 246.84217,253.0292 255.25442,257.09309 255.25442,257.09309 C 255.70512,257.31082 256.34668,257.26542 256.6918,256.99111 C 256.6918,256.99111 263.20867,251.81133 263.20867,251.81133 C 263.54778,251.54179 263.45431,251.15613 263.00018,250.94679 C 263.00018,250.94679 263.00018,250.94679 263.00018,250.94679" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3385" + d="M 224.63535,250.5847 C 224.63535,250.5847 216.40989,246.52591 216.40989,246.52591 C 215.79702,246.2235 214.86783,246.29035 214.3264,246.67645 C 214.3264,246.67645 206.94803,251.93833 206.94803,251.93833 C 206.39716,252.33118 206.45553,252.89148 207.07898,253.19391 C 207.07898,253.19391 215.44611,257.2528 215.44611,257.2528 C 216.0613,257.55121 216.99156,257.47468 217.53182,257.08184 C 217.53182,257.08184 224.76854,251.81989 224.76854,251.81989 C 225.29959,251.43374 225.24017,250.88315 224.63535,250.5847 C 224.63535,250.5847 224.63535,250.5847 224.63535,250.5847" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3387" + d="M 220.79919,257.76358 C 220.79919,257.76358 229.7534,262.16116 229.7534,262.16116 C 230.47771,262.51688 230.65884,263.11821 230.15739,263.5097 C 230.15739,263.5097 223.79138,268.4796 223.79138,268.4796 C 223.27442,268.8832 222.26833,268.91225 221.53782,268.54453 C 221.53782,268.54453 212.50868,263.9994 212.50868,263.9994 C 211.79484,263.64005 211.64287,263.03537 212.16581,262.64384 C 212.16581,262.64384 218.60675,257.8215 218.60675,257.8215 C 219.11421,257.44156 220.09111,257.41585 220.79919,257.76358 C 220.79919,257.76358 220.79919,257.76358 220.79919,257.76358" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3389" + d="M 209.90305,237.93879 C 209.90305,237.93879 217.69144,241.5189 217.69144,241.5189 C 218.2857,241.79206 218.40792,242.28161 217.95894,242.61539 C 217.95894,242.61539 211.48141,247.43084 211.48141,247.43084 C 211.00555,247.78462 210.15328,247.81772 209.57718,247.50651 C 209.57718,247.50651 202.04394,243.43694 202.04394,243.43694 C 201.54218,243.16588 201.50989,242.69331 201.96601,242.37623 C 201.96601,242.37623 208.18868,238.05054 208.18868,238.05054 C 208.62095,237.75004 209.38314,237.69979 209.90305,237.93879 C 209.90305,237.93879 209.90305,237.93879 209.90305,237.93879" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3391" + d="M 300.49388,221.41753 C 300.49388,221.41753 318.37188,206.97394 318.37188,206.97394 C 319.2072,206.29909 319.56463,205.63506 319.17704,205.48324 C 319.17704,205.48324 311.44233,202.45361 311.44233,202.45361 C 311.07717,202.31058 310.12278,202.71434 309.29892,203.3606 C 309.29892,203.3606 291.6901,217.17374 291.6901,217.17374 C 290.79969,217.87223 290.3631,218.57498 290.71507,218.74754 C 290.71507,218.74754 298.1804,222.40777 298.1804,222.40777 C 298.55498,222.59143 299.58864,222.1489 300.49388,221.41753 C 300.49388,221.41753 300.49388,221.41753 300.49388,221.41753" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 258.97104,224.63939 C 258.97104,224.63939 251.83463,221.23656 251.83463,221.23656 C 251.14159,220.90611 250.14534,220.95426 249.59505,221.34461 C 249.59505,221.34461 243.94919,225.34942 243.94919,225.34942 C 243.36824,225.76151 243.45494,226.38105 244.14978,226.73848 C 244.14978,226.73848 251.31231,230.42301 251.31231,230.42301 C 252.05284,230.80395 253.12041,230.75512 253.69927,230.31357 C 253.69927,230.31357 259.31886,226.02706 259.31886,226.02706 C 259.86601,225.60971 259.70806,224.99085 258.97104,224.63939 C 258.97104,224.63939 258.97104,224.63939 258.97104,224.63939" + id="path13125" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 245.03115,264.95865 C 245.03115,264.95865 236.89775,261.02915 236.89775,261.02915 C 236.29828,260.73954 235.41666,260.81767 234.92021,261.2046 C 234.92021,261.2046 228.15537,266.47697 228.15537,266.47697 C 227.65036,266.87057 227.72859,267.42473 228.33161,267.71924 C 228.33161,267.71924 236.5135,271.71529 236.5135,271.71529 C 237.12168,272.01232 238.01565,271.93037 238.51705,271.53179 C 238.51705,271.53179 245.23339,266.19289 245.23339,266.19289 C 245.7263,265.80109 245.63571,265.25074 245.03115,264.95865 C 245.03115,264.95865 245.03115,264.95865 245.03115,264.95865" + id="path13127" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 241.3425,254.88757 C 241.3425,254.88757 247.17841,250.38791 247.17841,250.38791 C 247.6407,250.03146 247.43669,249.46639 246.72219,249.12079 C 246.72219,249.12079 237.84809,244.82838 237.84809,244.82838 C 237.14306,244.48737 236.20078,244.49428 235.73414,244.84402 C 235.73414,244.84402 229.84385,249.25863 229.84385,249.25863 C 229.36826,249.61507 229.55538,250.18212 230.26467,250.52985 C 230.26467,250.52985 239.19316,254.90731 239.19316,254.90731 C 239.9121,255.25978 240.87126,255.2509 241.3425,254.88757 C 241.3425,254.88757 241.3425,254.88757 241.3425,254.88757" + id="path13129" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path13131" + d="M 250.09625,248.10772 C 250.09625,248.10772 256.04021,243.39795 256.04021,243.39795 C 256.61443,242.94295 256.40961,242.26587 255.58606,241.87803 C 255.58606,241.87803 247.4695,238.05567 247.4695,238.05567 C 246.66725,237.67787 245.54243,237.71942 244.94236,238.15046 C 244.94236,238.15046 238.73449,242.60981 238.73449,242.60981 C 238.09034,243.07251 238.23366,243.77042 239.06151,244.17275 C 239.06151,244.17275 247.44196,248.2456 247.44196,248.2456 C 248.29278,248.65909 249.47914,248.5967 250.09625,248.10772 C 250.09625,248.10772 250.09625,248.10772 250.09625,248.10772" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path13133" + d="M 270.78129,244.1004 C 270.78129,244.1004 263.16585,240.54998 263.16585,240.54998 C 262.59905,240.28573 261.81284,240.33065 261.40189,240.65211 C 261.40189,240.65211 255.67209,245.13428 255.67209,245.13428 C 255.23437,245.47666 255.35686,245.97255 255.94801,246.24454 C 255.94801,246.24454 263.88891,249.89826 263.88891,249.89826 C 264.4733,250.16714 265.27704,250.1004 265.6898,249.75019 C 265.6898,249.75019 271.09419,245.16472 271.09419,245.16472 C 271.48189,244.83578 271.34185,244.36173 270.78129,244.1004 C 270.78129,244.1004 270.78129,244.1004 270.78129,244.1004" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path13135" + d="M 279.36283,237.13131 C 279.36283,237.13131 271.95158,233.79368 271.95158,233.79368 C 271.36993,233.53174 270.52181,233.61628 270.04782,233.98311 C 270.04782,233.98311 264.05529,238.62092 264.05529,238.62092 C 263.57067,238.99597 263.64455,239.51827 264.22306,239.79213 C 264.22306,239.79213 271.59655,243.28243 271.59655,243.28243 C 272.19767,243.56699 273.0762,243.48424 273.56398,243.09674 C 273.56398,243.09674 279.59427,238.30625 279.59427,238.30625 C 280.07112,237.92743 279.96688,237.40335 279.36283,237.13131 C 279.36283,237.13131 279.36283,237.13131 279.36283,237.13131" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 259.13878,241.01984 C 259.13878,241.01984 264.94427,236.50262 264.94427,236.50262 C 265.5183,236.05597 265.27718,235.39329 264.41129,235.01866 C 264.41129,235.01866 256.13903,231.43971 256.13903,231.43971 C 255.34632,231.09675 254.26614,231.15807 253.71044,231.57558 C 253.71044,231.57558 248.09408,235.79517 248.09408,235.79517 C 247.52482,236.22286 247.6843,236.86458 248.45835,237.23578 C 248.45835,237.23578 256.54144,241.11225 256.54144,241.11225 C 257.38813,241.51828 258.54995,241.47801 259.13878,241.01984 C 259.13878,241.01984 259.13878,241.01984 259.13878,241.01984" + id="path14377" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 232.79455,261.43519 C 232.79455,261.43519 238.72958,256.85909 238.72958,256.85909 C 239.16465,256.52365 238.9726,255.99183 238.3001,255.66653 C 238.3001,255.66653 229.27525,251.30123 229.27525,251.30123 C 228.61176,250.98029 227.72493,250.98679 227.28579,251.31591 C 227.28579,251.31591 221.29544,255.80552 221.29544,255.80552 C 220.84778,256.14104 221.02387,256.67471 221.69138,257.00198 C 221.69138,257.00198 230.77154,261.45377 230.77154,261.45377 C 231.44822,261.78553 232.35098,261.77719 232.79455,261.43519 C 232.79455,261.43519 232.79455,261.43519 232.79455,261.43519" + id="path14379" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 254.17375,257.48652 C 254.17375,257.48652 246.32963,253.73115 246.32963,253.73115 C 245.70901,253.43402 244.79483,253.51721 244.27828,253.91779 C 244.27828,253.91779 237.75243,258.97869 237.75243,258.97869 C 237.22504,259.38768 237.29958,259.96347 237.92117,260.26957 C 237.92117,260.26957 245.77862,264.13893 245.77862,264.13893 C 246.41397,264.45181 247.35058,264.36687 247.87695,263.94859 C 247.87695,263.94859 254.38946,258.77374 254.38946,258.77374 C 254.90487,258.36419 254.80792,257.79015 254.17375,257.48652 C 254.17375,257.48652 254.17375,257.48652 254.17375,257.48652" + id="path14381" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path14383" + d="M 233.03678,243.79425 C 233.03678,243.79425 225.48646,240.13918 225.48646,240.13918 C 224.74342,239.77949 223.69299,239.82194 223.12841,240.23388 C 223.12841,240.23388 217.44731,244.37905 217.44731,244.37905 C 216.874,244.79736 217.00415,245.4333 217.742,245.80545 C 217.742,245.80545 225.24071,249.58767 225.24071,249.58767 C 226.00503,249.97318 227.0886,249.93684 227.66722,249.50562 C 227.66722,249.50562 233.39989,245.23329 233.39989,245.23329 C 233.96951,244.80878 233.80621,244.16674 233.03678,243.79425 C 233.03678,243.79425 233.03678,243.79425 233.03678,243.79425" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 282.03015,224.70872 C 282.03015,224.70872 287.9774,220.08364 287.9774,220.08364 C 288.56519,219.62653 289.56121,219.51344 290.21268,219.82947 C 290.21268,219.82947 296.76597,223.00851 296.76597,223.00851 C 297.42707,223.32921 297.49209,223.96249 296.90969,224.429 C 296.90969,224.429 291.01609,229.14982 291.01609,229.14982 C 290.41797,229.62893 289.39806,229.74916 288.73146,229.41875 C 288.73146,229.41875 282.12452,226.14398 282.12452,226.14398 C 281.4678,225.81845 281.42665,225.17805 282.03015,224.70872 C 282.03015,224.70872 282.03015,224.70872 282.03015,224.70872" + id="path14385" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path20314" + d="M 274.59176,213.04701 C 274.59176,213.04701 267.63301,209.8841 267.63301,209.8841 C 266.9305,209.56479 265.98277,209.58635 265.5079,209.93372 C 265.5079,209.93372 260.69581,213.4537 260.69581,213.4537 C 260.20677,213.81142 260.39277,214.35936 261.11308,214.68093 C 261.11308,214.68093 268.24694,217.86572 268.24694,217.86572 C 268.95551,218.18206 269.90543,218.14595 270.37662,217.78604 C 270.37662,217.78604 275.0136,214.24417 275.0136,214.24417 C 275.47126,213.89461 275.28303,213.36122 274.59176,213.04701 C 274.59176,213.04701 274.59176,213.04701 274.59176,213.04701" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 292.00448,215.55279 C 292.00448,215.55279 296.3664,212.1569 296.3664,212.1569 C 296.79902,211.82008 296.4902,211.27517 295.67813,210.9361 C 295.67813,210.9361 287.77738,207.63715 287.77738,207.63715 C 287.00644,207.31524 286.04433,207.31631 285.61635,207.63866 C 285.61635,207.63866 281.30257,210.88766 281.30257,210.88766 C 280.86653,211.21606 281.12901,211.75032 281.89503,212.08646 C 281.89503,212.08646 289.74764,215.53227 289.74764,215.53227 C 290.55499,215.88655 291.56345,215.89614 292.00448,215.55279 C 292.00448,215.55279 292.00448,215.55279 292.00448,215.55279" + id="path20316" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path20318" + d="M 284.72744,221.17105 C 284.72744,221.17105 289.45459,217.54272 289.45459,217.54272 C 289.926,217.18089 289.66619,216.61851 288.87179,216.28131 C 288.87179,216.28131 280.90373,212.899 280.90373,212.899 C 280.10221,212.55875 279.07175,212.57593 278.59356,212.93801 C 278.59356,212.93801 273.79834,216.56885 273.79834,216.56885 C 273.31611,216.93397 273.57888,217.50206 274.38726,217.84205 C 274.38726,217.84205 282.42339,221.22188 282.42339,221.22188 C 283.22457,221.55883 284.25207,221.53592 284.72744,221.17105 C 284.72744,221.17105 284.72744,221.17105 284.72744,221.17105" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 280.70619,208.18882 C 280.70619,208.18882 273.65334,205.39325 273.65334,205.39325 C 272.99267,205.13139 272.16292,205.14273 271.78475,205.41678 C 271.78475,205.41678 267.9355,208.20633 267.9355,208.20633 C 267.54254,208.4911 267.73876,208.95315 268.38364,209.24454 C 268.38364,209.24454 275.27945,212.36039 275.27945,212.36039 C 276.01926,212.69466 276.95281,212.71002 277.36247,212.39198 C 277.36247,212.39198 281.36853,209.28183 281.36853,209.28183 C 281.76145,208.97678 281.46158,208.48822 280.70619,208.18882 C 280.70619,208.18882 280.70619,208.18882 280.70619,208.18882" + id="path20320" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path20322" + d="M 267.13462,218.61017 C 267.13462,218.61017 260.04553,215.39646 260.04553,215.39646 C 259.35688,215.08427 258.39121,215.12387 257.87608,215.4844 C 257.87608,215.4844 252.67496,219.12445 252.67496,219.12445 C 252.14829,219.49305 252.26711,220.05491 252.94569,220.38521 C 252.94569,220.38521 259.93387,223.78674 259.93387,223.78674 C 260.65566,224.13807 261.67299,224.1096 262.21011,223.72182 C 262.21011,223.72182 267.5121,219.8939 267.5121,219.8939 C 268.03699,219.51494 267.86652,218.94196 267.13462,218.61017 C 267.13462,218.61017 267.13462,218.61017 267.13462,218.61017" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 287.7354,230.25946 C 287.7354,230.25946 281.08316,227.00351 281.08316,227.00351 C 280.40678,226.67245 279.40568,226.75692 278.83891,227.19481 C 278.83891,227.19481 273.09436,231.63291 273.09436,231.63291 C 272.51043,232.08403 272.60418,232.71336 273.30418,233.04198 C 273.30418,233.04198 280.18673,236.27314 280.18673,236.27314 C 280.86539,236.59174 281.86259,236.48569 282.42295,236.03722 C 282.42295,236.03722 287.93718,231.62391 287.93718,231.62391 C 288.48138,231.18834 288.39153,230.58062 287.7354,230.25946 C 287.7354,230.25946 287.7354,230.25946 287.7354,230.25946" + id="path20324" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path20326" + d="M 267.94729,234.14015 C 267.94729,234.14015 273.40476,229.94747 273.40476,229.94747 C 273.94662,229.5312 273.71505,228.90956 272.88832,228.55398 C 272.88832,228.55398 264.75456,225.0556 264.75456,225.0556 C 263.95198,224.71041 262.87291,224.75882 262.33246,225.16387 C 262.33246,225.16387 256.88991,229.24286 256.88991,229.24286 C 256.34026,229.6548 256.54039,230.27309 257.34135,230.62945 C 257.34135,230.62945 265.46021,234.24152 265.46021,234.24152 C 266.28553,234.60871 267.39604,234.56364 267.94729,234.14015 C 267.94729,234.14015 267.94729,234.14015 267.94729,234.14015" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path21282" + d="M 276.32923,227.66222 C 276.32923,227.66222 281.90001,223.35623 281.90001,223.35623 C 282.4517,222.9298 282.23314,222.31013 281.41303,221.96694 C 281.41303,221.96694 273.34754,218.59182 273.34754,218.59182 C 272.55203,218.25892 271.46937,218.32422 270.91711,218.73813 C 270.91711,218.73813 265.34181,222.91665 265.34181,222.91665 C 264.77733,223.33971 264.96112,223.95828 265.75691,224.30368 C 265.75691,224.30368 273.82693,227.80624 273.82693,227.80624 C 274.64767,228.16246 275.7651,228.09826 276.32923,227.66222 C 276.32923,227.66222 276.32923,227.66222 276.32923,227.66222" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <g + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + id="g5349"> + <path + id="path3281" + d="M 200.53013,313.0503 C 200.53013,313.0503 193.03458,319.37147 193.03458,319.37147 C 192.65485,319.6917 192.0145,319.75862 191.59935,319.52112 C 191.59935,319.52112 183.2867,314.76558 183.2867,314.76558 C 182.87292,314.52886 182.85139,314.08409 183.23786,313.76871 C 183.23786,313.76871 190.86675,307.54312 190.86675,307.54312 C 191.24112,307.23761 191.86986,307.1787 192.27701,307.41066 C 192.27701,307.41066 200.45632,312.07061 200.45632,312.07061 C 200.86479,312.30333 200.89796,312.7401 200.53013,313.0503 C 200.53013,313.0503 200.53013,313.0503 200.53013,313.0503" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3283" + d="M 172.41739,306.03622 C 172.41739,306.03622 188.0277,294.08595 188.0277,294.08595 C 189.11795,293.25133 190.41897,292.82031 190.95268,293.11384 C 190.95268,293.11384 198.19106,297.09485 198.19106,297.09485 C 198.72888,297.39065 198.31537,298.31501 197.2562,299.17331 C 197.2562,299.17331 182.08669,311.46584 182.08669,311.46584 C 180.89616,312.43058 179.47079,312.95427 178.90005,312.63295 C 178.90005,312.63295 171.22074,308.30947 171.22074,308.30947 C 170.65468,307.99077 171.1926,306.97384 172.41739,306.03622 C 172.41739,306.03622 172.41739,306.03622 172.41739,306.03622" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3285" + d="M 183.71685,293.09242 C 183.71685,293.09242 175.21974,288.61823 175.21974,288.61823 C 174.62141,288.30317 173.39049,288.60803 172.45631,289.30319 C 172.45631,289.30319 159.05702,299.27405 159.05702,299.27405 C 158.07838,300.0023 157.77029,300.8596 158.36997,301.19473 C 158.36997,301.19473 166.88944,305.95582 166.88944,305.95582 C 167.50572,306.30023 168.79179,305.97286 169.76917,305.22339 C 169.76917,305.22339 183.14605,294.96565 183.14605,294.96565 C 184.07833,294.25076 184.33129,293.41596 183.71685,293.09242 C 183.71685,293.09242 183.71685,293.09242 183.71685,293.09242" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3287" + d="M 166.83475,289.12754 C 166.83475,289.12754 159.13844,284.98242 159.13844,284.98242 C 158.69381,284.74294 157.84841,284.90019 157.24056,285.33581 C 157.24056,285.33581 146.5932,292.96648 146.5932,292.96648 C 145.95049,293.4271 145.7912,294.00445 146.23859,294.2601 C 146.23859,294.2601 153.9866,298.68753 153.9866,298.68753 C 154.44898,298.95174 155.33851,298.77896 155.97843,298.30106 C 155.97843,298.30106 166.57405,290.38811 166.57405,290.38811 C 167.17862,289.93661 167.2938,289.37478 166.83475,289.12754 C 166.83475,289.12754 166.83475,289.12754 166.83475,289.12754" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3289" + d="M 151.36095,285.65566 C 151.36095,285.65566 143.84889,281.51108 143.84889,281.51108 C 143.47842,281.30668 142.86541,281.36856 142.47405,281.64966 C 142.47405,281.64966 134.61675,287.29335 134.61675,287.29335 C 134.22459,287.57503 134.20331,287.96918 134.56941,288.17726 C 134.56941,288.17726 141.993,292.39653 141.993,292.39653 C 142.36721,292.60921 142.98804,292.55081 143.38462,292.2654 C 143.38462,292.2654 151.33041,286.54701 151.33041,286.54701 C 151.72616,286.2622 151.73961,285.86457 151.36095,285.65566 C 151.36095,285.65566 151.36095,285.65566 151.36095,285.65566" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3291" + d="M 210.70247,305.77557 C 210.70247,305.77557 204.93036,310.57929 204.93036,310.57929 C 204.34435,311.06699 203.27394,311.11934 202.52773,310.69333 C 202.52773,310.69333 194.82772,306.29731 194.82772,306.29731 C 194.03081,305.84234 193.88921,305.07052 194.51315,304.57044 C 194.51315,304.57044 200.65538,299.64763 200.65538,299.64763 C 201.26179,299.16161 202.36255,299.14928 203.12074,299.61631 C 203.12074,299.61631 210.45071,304.13144 210.45071,304.13144 C 211.16146,304.56926 211.27265,305.30104 210.70247,305.77557 C 210.70247,305.77557 210.70247,305.77557 210.70247,305.77557" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3293" + d="M 220.36679,297.64972 C 220.36679,297.64972 214.19821,302.83693 214.19821,302.83693 C 213.8071,303.16581 213.0629,303.16632 212.53329,302.83934 C 212.53329,302.83934 204.47426,297.86377 204.47426,297.86377 C 203.99682,297.56901 203.91878,297.08754 204.2961,296.78318 C 204.2961,296.78318 210.25274,291.97831 210.25274,291.97831 C 210.61877,291.68306 211.30575,291.66386 211.7962,291.93623 C 211.7962,291.93623 220.06723,296.5296 220.06723,296.5296 C 220.61026,296.83118 220.74549,297.33126 220.36679,297.64972 C 220.36679,297.64972 220.36679,297.64972 220.36679,297.64972" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3295" + d="M 229.27683,290.15464 C 229.27683,290.15464 223.76946,294.79471 223.76946,294.79471 C 223.28503,295.20286 222.36392,295.23289 221.70465,294.86121 C 221.70465,294.86121 214.09874,290.57325 214.09874,290.57325 C 213.43481,290.19895 213.29956,289.57104 213.79504,289.16623 C 213.79504,289.16623 219.4277,284.56425 219.4277,284.56425 C 219.91221,284.1684 220.82757,284.14902 221.48054,284.51999 C 221.48054,284.51999 228.96116,288.76985 228.96116,288.76985 C 229.60961,289.13825 229.75059,289.75549 229.27683,290.15464 C 229.27683,290.15464 229.27683,290.15464 229.27683,290.15464" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3297" + d="M 207.6366,289.76045 C 207.6366,289.76045 200.20453,285.97175 200.20453,285.97175 C 199.75388,285.74202 199.11315,285.78115 198.76662,286.05878 C 198.76662,286.05878 193.21867,290.50362 193.21867,290.50362 C 192.87292,290.78062 192.94685,291.19499 193.38567,291.43343 C 193.38567,291.43343 200.62246,295.36556 200.62246,295.36556 C 201.08703,295.61798 201.75028,295.59166 202.10819,295.30573 C 202.10819,295.30573 207.85144,290.71744 207.85144,290.71744 C 208.21018,290.43084 208.11372,290.00367 207.6366,289.76045 C 207.6366,289.76045 207.6366,289.76045 207.6366,289.76045" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3299" + d="M 193.30538,285.7839 C 193.30538,285.7839 185.22052,281.47582 185.22052,281.47582 C 184.58295,281.13609 183.70561,281.13742 183.25124,281.47882 C 183.25124,281.47882 177.48561,285.81085 177.48561,285.81085 C 177.01761,286.16248 177.15516,286.7287 177.79623,287.08041 C 177.79623,287.08041 185.92745,291.54147 185.92745,291.54147 C 186.5872,291.90343 187.49396,291.90165 187.95831,291.53752 C 187.95831,291.53752 193.67756,287.05251 193.67756,287.05251 C 194.12816,286.69915 193.9612,286.13336 193.30538,285.7839 C 193.30538,285.7839 193.30538,285.7839 193.30538,285.7839" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3301" + d="M 153.65825,274.40909 C 153.65825,274.40909 161.75797,278.66326 161.75797,278.66326 C 162.02482,278.80341 162.05943,279.04844 161.83501,279.21257 C 161.83501,279.21257 154.75107,284.39353 154.75107,284.39353 C 154.52039,284.56224 154.12135,284.5797 153.85694,284.43276 C 153.85694,284.43276 145.83383,279.97431 145.83383,279.97431 C 145.58269,279.83475 145.56457,279.59204 145.79266,279.43005 C 145.79266,279.43005 152.79999,274.45338 152.79999,274.45338 C 153.02206,274.29566 153.40462,274.27588 153.65825,274.40909 C 153.65825,274.40909 153.65825,274.40909 153.65825,274.40909" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3303" + d="M 176.68036,282.15173 C 176.68036,282.15173 169.08774,278.13438 169.08774,278.13438 C 168.53136,277.83999 167.74128,277.85841 167.31522,278.1756 C 167.31522,278.1756 161.51878,282.49085 161.51878,282.49085 C 161.08675,282.81248 161.18562,283.31335 161.7415,283.61396 C 161.7415,283.61396 169.32801,287.71649 169.32801,287.71649 C 169.89523,288.02323 170.70097,288.00619 171.1335,287.67818 C 171.1335,287.67818 176.93604,283.27774 176.93604,283.27774 C 177.36252,282.95432 177.24799,282.45207 176.68036,282.15173 C 176.68036,282.15173 176.68036,282.15173 176.68036,282.15173" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3305" + d="M 186.05575,275.08382 C 186.05575,275.08382 178.66837,271.26624 178.66837,271.26624 C 178.14275,270.99462 177.38016,271.01579 176.95654,271.31482 C 176.95654,271.31482 170.80827,275.65494 170.80827,275.65494 C 170.35386,275.97571 170.42595,276.46372 170.9721,276.74789 C 170.9721,276.74789 178.64907,280.74215 178.64907,280.74215 C 179.19855,281.02804 179.99123,280.98835 180.42429,280.65448 C 180.42429,280.65448 186.28302,276.1377 186.28302,276.1377 C 186.68663,275.82653 186.58444,275.35703 186.05575,275.08382 C 186.05575,275.08382 186.05575,275.08382 186.05575,275.08382" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3307" + d="M 163.51597,267.87954 C 163.51597,267.87954 171.1646,271.91896 171.1646,271.91896 C 171.53604,272.11513 171.58651,272.4514 171.2774,272.67324 C 171.2774,272.67324 164.82134,277.30652 164.82134,277.30652 C 164.50235,277.53544 163.94045,277.55958 163.56185,277.36032 C 163.56185,277.36032 155.76574,273.25703 155.76574,273.25703 C 155.38595,273.05714 155.34321,272.7142 155.66949,272.48842 C 155.66949,272.48842 162.27303,267.91901 162.27303,267.91901 C 162.5892,267.70024 163.14337,267.68276 163.51597,267.87954 C 163.51597,267.87954 163.51597,267.87954 163.51597,267.87954" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3309" + d="M 173.08132,261.26965 C 173.08132,261.26965 180.42033,265.06231 180.42033,265.06231 C 180.84108,265.27975 180.901,265.65731 180.55405,265.90902 C 180.55405,265.90902 174.39819,270.3752 174.39819,270.3752 C 174.04312,270.63281 173.41533,270.66166 172.99122,270.43971 C 172.99122,270.43971 165.59423,266.56865 165.59423,266.56865 C 165.17482,266.34916 165.12621,265.96859 165.4846,265.71552 C 165.4846,265.71552 171.69844,261.32774 171.69844,261.32774 C 172.04868,261.08043 172.66517,261.05459 173.08132,261.26965 C 173.08132,261.26965 173.08132,261.26965 173.08132,261.26965" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3311" + d="M 183.15808,254.05968 C 183.15808,254.05968 190.5411,257.89809 190.5411,257.89809 C 190.90119,258.0853 190.9368,258.42917 190.61983,258.66983 C 190.61983,258.66983 183.84308,263.81508 183.84308,263.81508 C 183.50031,264.07534 182.92132,264.12739 182.54615,263.93097 C 182.54615,263.93097 174.85492,259.90428 174.85492,259.90428 C 174.48192,259.709 174.47113,259.34932 174.82945,259.09862 C 174.82945,259.09862 181.91436,254.14161 181.91436,254.14161 C 182.24579,253.90973 182.79998,253.8735 183.15808,254.05968 C 183.15808,254.05968 183.15808,254.05968 183.15808,254.05968" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3313" + d="M 195.50846,268.00414 C 195.50846,268.00414 187.92237,264.08387 187.92237,264.08387 C 187.38263,263.80495 186.5995,263.82667 186.16449,264.13375 C 186.16449,264.13375 179.85083,268.59062 179.85083,268.59062 C 179.38419,268.92002 179.45825,269.42118 180.01907,269.71297 C 180.01907,269.71297 187.90254,273.81468 187.90254,273.81468 C 188.4668,274.10826 189.28081,274.0675 189.72552,273.72465 C 189.72552,273.72465 195.74185,269.08637 195.74185,269.08637 C 196.15632,268.76683 196.05137,268.2847 195.50846,268.00414 C 195.50846,268.00414 195.50846,268.00414 195.50846,268.00414" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3315" + d="M 202.55006,278.55525 C 202.55006,278.55525 194.52053,274.43157 194.52053,274.43157 C 193.89847,274.1121 193.03474,274.13023 192.58051,274.47152 C 192.58051,274.47152 186.82178,278.79836 186.82178,278.79836 C 186.35477,279.14926 186.47106,279.70327 187.086,280.04131 C 187.086,280.04131 195.02775,284.40711 195.02775,284.40711 C 195.68386,284.76779 196.59706,284.76041 197.07144,284.38968 C 197.07144,284.38968 202.9179,279.82067 202.9179,279.82067 C 203.3788,279.46048 203.21306,278.89574 202.55006,278.55525 C 202.55006,278.55525 202.55006,278.55525 202.55006,278.55525" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3317" + d="M 217.28268,282.30723 C 217.28268,282.30723 209.76798,278.51506 209.76798,278.51506 C 209.31861,278.28829 208.66038,278.33252 208.29049,278.6148 C 208.29049,278.6148 201.99168,283.42172 201.99168,283.42172 C 201.5992,283.72125 201.65194,284.15497 202.11173,284.39356 C 202.11173,284.39356 209.80365,288.38505 209.80365,288.38505 C 210.27451,288.62939 210.96221,288.57431 211.3438,288.26224 C 211.3438,288.26224 217.46536,283.25603 217.46536,283.25603 C 217.82471,282.96216 217.7425,282.53927 217.28268,282.30723 C 217.28268,282.30723 217.28268,282.30723 217.28268,282.30723" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5259" + sodipodi:nodetypes="cccccccccc" + d="M 223.6315,280.9685 C 223.6315,280.9685 271.20382,242.80623 271.20382,242.80623 C 271.77183,242.35446 272.94396,242.34509 273.8347,242.78481 C 273.8347,242.78481 280.16259,245.8893 280.16259,245.8893 C 281.06675,246.33566 281.34594,247.06729 280.78574,247.53016 C 280.78574,247.53016 233.93323,286.78539 233.93323,286.78539 C 233.23622,287.36129 231.89705,287.39044 230.93425,286.85016 C 230.93425,286.85016 224.06489,282.94036 224.06489,282.94036 C 223.11807,282.40904 222.92637,281.52934 223.6315,280.9685 C 223.6315,280.9685 223.6315,280.9685 223.6315,280.9685" + style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5261" + d="M 227.03565,274.61509 C 227.03565,274.61509 219.57159,270.89543 219.57159,270.89543 C 219.13842,270.67957 218.48938,270.73732 218.1151,271.02521 C 218.1151,271.02521 211.61304,276.02653 211.61304,276.02653 C 211.22381,276.32593 211.26193,276.7481 211.69984,276.97279 C 211.69984,276.97279 219.24737,280.84554 219.24737,280.84554 C 219.69565,281.07556 220.36702,281.012 220.75135,280.70336 C 220.75135,280.70336 227.16991,275.54895 227.16991,275.54895 C 227.53929,275.25232 227.47886,274.83596 227.03565,274.61509 C 227.03565,274.61509 227.03565,274.61509 227.03565,274.61509" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5263" + d="M 192.51964,247.35677 C 192.51964,247.35677 199.78325,251.31635 199.78325,251.31635 C 200.08373,251.48015 200.10518,251.76671 199.83193,251.95915 C 199.83193,251.95915 193.39598,256.49168 193.39598,256.49168 C 193.12338,256.68366 192.65687,256.71094 192.34938,256.55257 C 192.34938,256.55257 184.91676,252.72442 184.91676,252.72442 C 184.59365,252.55801 184.55824,252.26477 184.83799,252.06723 C 184.83799,252.06723 191.44296,247.40326 191.44296,247.40326 C 191.7234,247.20524 192.20387,247.18464 192.51964,247.35677 C 192.51964,247.35677 192.51964,247.35677 192.51964,247.35677" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5265" + d="M 204.95758,260.67876 C 204.95758,260.67876 197.45088,256.83357 197.45088,256.83357 C 196.91888,256.56107 196.14744,256.58436 195.71989,256.88617 C 195.71989,256.88617 189.62606,261.18786 189.62606,261.18786 C 189.18381,261.50005 189.26067,261.97752 189.79977,262.25801 C 189.79977,262.25801 197.4078,266.21641 197.4078,266.21641 C 197.95451,266.50085 198.7459,266.47153 199.18087,266.15108 C 199.18087,266.15108 205.17336,261.73618 205.17336,261.73618 C 205.59375,261.42646 205.49693,260.95503 204.95758,260.67876 C 204.95758,260.67876 204.95758,260.67876 204.95758,260.67876" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5267" + d="M 203.42735,267.20908 C 203.42735,267.20908 212.2333,271.70369 212.2333,271.70369 C 212.6248,271.90351 212.71338,272.23708 212.43163,272.45164 C 212.43163,272.45164 206.02839,277.32796 206.02839,277.32796 C 205.74282,277.54543 205.19609,277.55776 204.8028,277.35556 C 204.8028,277.35556 195.957,272.80759 195.957,272.80759 C 195.56658,272.60686 195.48379,272.27205 195.77113,272.05697 C 195.77113,272.05697 202.21422,267.23402 202.21422,267.23402 C 202.49773,267.02179 203.03868,267.0107 203.42735,267.20908 C 203.42735,267.20908 203.42735,267.20908 203.42735,267.20908" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5269" + d="M 212.72962,260.6903 C 212.72962,260.6903 220.81856,264.71792 220.81856,264.71792 C 221.40722,265.01103 221.56351,265.49233 221.1669,265.79697 C 221.1669,265.79697 215.52012,270.13427 215.52012,270.13427 C 215.11075,270.44871 214.30434,270.45549 213.7142,270.1496 C 213.7142,270.1496 205.60787,265.94776 205.60787,265.94776 C 205.04085,265.65385 204.91653,265.17445 205.32701,264.87276 C 205.32701,264.87276 210.99121,260.70967 210.99121,260.70967 C 211.38919,260.41716 212.16362,260.40847 212.72962,260.6903 C 212.72962,260.6903 212.72962,260.6903 212.72962,260.6903" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5271" + d="M 235.38208,267.94652 C 235.38208,267.94652 228.36195,264.54302 228.36195,264.54302 C 227.7581,264.25027 226.91755,264.28848 226.47592,264.62855 C 226.47592,264.62855 221.36693,268.56268 221.36693,268.56268 C 220.91972,268.90705 221.04356,269.42537 221.64599,269.72502 C 221.64599,269.72502 228.65018,273.2089 228.65018,273.2089 C 229.26637,273.51538 230.12476,273.47935 230.5734,273.12791 C 230.5734,273.12791 235.69833,269.1134 235.69833,269.1134 C 236.14129,268.76642 235.99961,268.24591 235.38208,267.94652 C 235.38208,267.94652 235.38208,267.94652 235.38208,267.94652" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5273" + d="M 202.3439,240.55011 C 202.3439,240.55011 209.40685,244.3565 209.40685,244.3565 C 209.77966,244.55741 209.79685,244.91929 209.44513,245.16789 C 209.44513,245.16789 202.71836,249.92236 202.71836,249.92236 C 202.36384,250.17294 201.77847,250.21096 201.40617,250.00761 C 201.40617,250.00761 194.3529,246.15501 194.3529,246.15501 C 193.98466,245.95386 193.97344,245.59217 194.32745,245.34403 C 194.32745,245.34403 201.04453,240.63577 201.04453,240.63577 C 201.39575,240.38958 201.97514,240.35138 202.3439,240.55011 C 202.3439,240.55011 202.3439,240.55011 202.3439,240.55011" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5275" + d="M 214.95641,253.88523 C 214.95641,253.88523 207.20865,250.11064 207.20865,250.11064 C 206.78058,249.90209 206.15223,249.93501 205.79811,250.18412 C 205.79811,250.18412 199.5121,254.60606 199.5121,254.60606 C 199.14935,254.86124 199.19499,255.24389 199.61614,255.46439 C 199.61614,255.46439 207.24189,259.45703 207.24189,259.45703 C 207.69079,259.69206 208.35153,259.66596 208.72148,259.39808 C 208.72148,259.39808 215.12948,254.75806 215.12948,254.75806 C 215.49031,254.49678 215.41229,254.10733 214.95641,253.88523 C 214.95641,253.88523 214.95641,253.88523 214.95641,253.88523" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5277" + d="M 221.9033,253.92205 C 221.9033,253.92205 229.74607,257.77376 229.74607,257.77376 C 230.38047,258.08533 230.53912,258.61203 230.09991,258.95492 C 230.09991,258.95492 224.52409,263.30793 224.52409,263.30793 C 224.07129,263.66142 223.1901,263.68688 222.55025,263.36479 C 222.55025,263.36479 214.64187,259.38384 214.64187,259.38384 C 214.01663,259.0691 213.88352,258.53947 214.34155,258.19654 C 214.34155,258.19654 219.983,253.97277 219.983,253.97277 C 220.42747,253.64 221.28312,253.61747 221.9033,253.92205 C 221.9033,253.92205 221.9033,253.92205 221.9033,253.92205" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 244.75785,260.58731 C 244.75785,260.58731 237.5872,257.12295 237.5872,257.12295 C 237.05869,256.86761 236.28144,256.9365 235.84374,257.27763 C 235.84374,257.27763 229.87966,261.92591 229.87966,261.92591 C 229.43444,262.2729 229.5034,262.76148 230.03504,263.02113 C 230.03504,263.02113 237.24843,266.54415 237.24843,266.54415 C 237.78463,266.80603 238.57278,266.73378 239.01483,266.38238 C 239.01483,266.38238 244.93616,261.67544 244.93616,261.67544 C 245.3707,261.33002 245.29084,260.84482 244.75785,260.58731 C 244.75785,260.58731 244.75785,260.58731 244.75785,260.58731" + id="path5279" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 233.2943,256.416 C 233.2943,256.416 238.45564,252.43646 238.45564,252.43646 C 238.83398,252.14474 238.66697,251.68224 238.08213,251.39936 C 238.08213,251.39936 230.23378,247.60312 230.23378,247.60312 C 229.65678,247.32402 228.88557,247.32967 228.50367,247.6159 C 228.50367,247.6159 223.29424,251.52023 223.29424,251.52023 C 222.90493,251.812 223.05807,252.27611 223.63856,252.56071 C 223.63856,252.56071 231.535,256.43217 231.535,256.43217 C 232.12348,256.72068 232.90856,256.71342 233.2943,256.416 C 233.2943,256.416 233.2943,256.416 233.2943,256.416" + id="path5281" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5283" + d="M 224.84883,246.55597 C 224.84883,246.55597 217.52734,242.94323 217.52734,242.94323 C 216.98183,242.67405 216.15475,242.73355 215.67283,243.07723 C 215.67283,243.07723 209.10533,247.76083 209.10533,247.76083 C 208.61499,248.11051 208.66694,248.60923 209.22188,248.87843 C 209.22188,248.87843 216.66948,252.49124 216.66948,252.49124 C 217.21707,252.75687 218.04508,252.68874 218.52598,252.33907 C 218.52598,252.33907 224.96738,247.65541 224.96738,247.65541 C 225.44008,247.3117 225.38717,246.82161 224.84883,246.55597 C 224.84883,246.55597 224.84883,246.55597 224.84883,246.55597" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5285" + d="M 211.2176,234.55068 C 211.2176,234.55068 218.13924,237.73238 218.13924,237.73238 C 218.66737,237.97515 218.77598,238.41021 218.37697,238.70684 C 218.37697,238.70684 212.62031,242.9864 212.62031,242.9864 C 212.1974,243.3008 211.43997,243.33022 210.928,243.05365 C 210.928,243.05365 204.23311,239.43697 204.23311,239.43697 C 203.78717,239.19607 203.75848,238.77609 204.16385,238.4943 C 204.16385,238.4943 209.694,234.65 209.694,234.65 C 210.07817,234.38294 210.75554,234.33829 211.2176,234.55068 C 211.2176,234.55068 211.2176,234.55068 211.2176,234.55068" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5287" + d="M 233.15615,239.94299 C 233.15615,239.94299 226.44421,236.69379 226.44421,236.69379 C 225.78369,236.37403 224.84989,236.41177 224.348,236.77797 C 224.348,236.77797 219.29773,240.46286 219.29773,240.46286 C 218.78807,240.83472 218.90378,241.40005 219.55968,241.73088 C 219.55968,241.73088 226.22576,245.09312 226.22576,245.09312 C 226.90521,245.43582 227.86846,245.40352 228.38283,245.02018 C 228.38283,245.02018 233.47896,241.22224 233.47896,241.22224 C 233.98531,240.84488 233.84015,240.27412 233.15615,239.94299 C 233.15615,239.94299 233.15615,239.94299 233.15615,239.94299" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5289" + d="M 220.30432,227.87754 C 220.30432,227.87754 227.46486,231.13608 227.46486,231.13608 C 227.82573,231.3003 227.87475,231.60963 227.57378,231.82996 C 227.57378,231.82996 221.38229,236.3625 221.38229,236.3625 C 221.06565,236.59429 220.51407,236.64288 220.14663,236.4711 C 220.14663,236.4711 212.85793,233.06353 212.85793,233.06353 C 212.49851,232.8955 212.47011,232.57953 212.7932,232.35543 C 212.7932,232.35543 219.11286,227.97192 219.11286,227.97192 C 219.42016,227.75877 219.95112,227.71681 220.30432,227.87754 C 220.30432,227.87754 220.30432,227.87754 220.30432,227.87754" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 253.8328,253.35343 C 253.8328,253.35343 246.80705,249.98982 246.80705,249.98982 C 246.25118,249.7237 245.43236,249.7982 244.96971,250.15699 C 244.96971,250.15699 239.12467,254.6899 239.12467,254.6899 C 238.65231,255.05622 238.71907,255.57195 239.27581,255.84611 C 239.27581,255.84611 246.31353,259.3118 246.31353,259.3118 C 246.88258,259.59203 247.72148,259.51595 248.19294,259.14132 C 248.19294,259.14132 254.02601,254.50633 254.02601,254.50633 C 254.48765,254.13951 254.40082,253.62536 253.8328,253.35343 C 253.8328,253.35343 253.8328,253.35343 253.8328,253.35343" + id="path5291" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 241.69245,250.18836 C 241.69245,250.18836 246.86241,246.20217 246.86241,246.20217 C 247.27195,245.8864 247.09122,245.38581 246.45826,245.07965 C 246.45826,245.07965 238.59679,241.27706 238.59679,241.27706 C 237.97222,240.97496 237.13747,240.98108 236.72408,241.29091 C 236.72408,241.29091 231.50595,245.20176 231.50595,245.20176 C 231.08462,245.51753 231.25039,246.01986 231.87874,246.32792 C 231.87874,246.32792 239.78839,250.20585 239.78839,250.20585 C 240.42527,250.5181 241.27499,250.51024 241.69245,250.18836 C 241.69245,250.18836 241.69245,250.18836 241.69245,250.18836" + id="path5293" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5295" + d="M 262.29906,246.8676 C 262.29906,246.8676 254.88168,243.44853 254.88168,243.44853 C 254.50008,243.27263 253.95712,243.3153 253.66336,243.54406 C 253.66336,243.54406 248.0194,247.9394 248.0194,247.9394 C 247.72057,248.17212 247.78403,248.50657 248.1626,248.68946 C 248.1626,248.68946 255.52239,252.24492 255.52239,252.24492 C 255.9167,252.43541 256.47799,252.39569 256.77993,252.1557 C 256.77993,252.1557 262.48146,247.62397 262.48146,247.62397 C 262.77816,247.38815 262.69638,247.05074 262.29906,246.8676 C 262.29906,246.8676 262.29906,246.8676 262.29906,246.8676" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5297" + d="M 269.92313,240.18072 C 269.92313,240.18072 263.31143,237.09826 263.31143,237.09826 C 262.81933,236.86884 262.13676,236.90784 261.77997,237.18694 C 261.77997,237.18694 256.80537,241.07833 256.80537,241.07833 C 256.42536,241.3756 256.53169,241.80611 257.04493,242.04226 C 257.04493,242.04226 263.93919,245.21441 263.93919,245.21441 C 264.44655,245.44785 265.14436,245.3899 265.50271,245.08585 C 265.50271,245.08585 270.19479,241.10477 270.19479,241.10477 C 270.53139,240.81918 270.4098,240.40761 269.92313,240.18072 C 269.92313,240.18072 269.92313,240.18072 269.92313,240.18072" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5299" + d="M 284.15897,244.25926 C 284.15897,244.25926 290.08975,239.34554 290.08975,239.34554 C 290.3372,239.14053 290.25996,238.84705 289.91756,238.6876 C 289.91756,238.6876 282.05801,235.02758 282.05801,235.02758 C 281.73357,234.87649 281.27511,234.91294 281.02917,235.10924 C 281.02917,235.10924 275.13725,239.81168 275.13725,239.81168 C 274.88342,240.01427 274.9381,240.30589 275.26078,240.46561 C 275.26078,240.46561 283.08145,244.3369 283.08145,244.3369 C 283.42232,244.50564 283.90334,244.47105 284.15897,244.25926 C 284.15897,244.25926 284.15897,244.25926 284.15897,244.25926" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5301" + d="M 290.6347,228.41799 C 290.6347,228.41799 298.47677,231.7748 298.47677,231.7748 C 298.96904,231.98552 299.08998,232.38046 298.7459,232.65955 C 298.7459,232.65955 293.04479,237.28397 293.04479,237.28397 C 292.69915,237.56434 292.03399,237.60956 291.55538,237.38617 C 291.55538,237.38617 283.93166,233.82786 283.93166,233.82786 C 283.4873,233.62045 283.39853,233.2382 283.73091,232.97007 C 283.73091,232.97007 289.21363,228.54719 289.21363,228.54719 C 289.54456,228.28023 290.17756,228.22231 290.6347,228.41799 C 290.6347,228.41799 290.6347,228.41799 290.6347,228.41799" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5303" + d="M 301.86982,230.05516 C 301.86982,230.05516 306.72655,226.01075 306.72655,226.01075 C 307.20873,225.60921 307.07252,225.04406 306.41901,224.74173 C 306.41901,224.74173 299.71761,221.64153 299.71761,221.64153 C 299.02841,221.32268 298.06453,221.39571 297.55878,221.80728 C 297.55878,221.80728 292.46319,225.95398 292.46319,225.95398 C 291.94831,226.37298 292.11057,226.9602 292.82454,227.2686 C 292.82454,227.2686 299.76474,230.26652 299.76474,230.26652 C 300.44135,230.5588 301.37922,230.4637 301.86982,230.05516 C 301.86982,230.05516 301.86982,230.05516 301.86982,230.05516" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5305" + d="M 229.75901,221.51708 C 229.75901,221.51708 236.30365,224.3537 236.30365,224.3537 C 236.77779,224.55921 236.83816,224.95708 236.4374,225.2459 C 236.4374,225.2459 230.73685,229.35443 230.73685,229.35443 C 230.32395,229.65201 229.60775,229.71902 229.13267,229.50455 C 229.13267,229.50455 222.57677,226.54493 222.57677,226.54493 C 222.11612,226.33697 222.07952,225.93711 222.49312,225.64852 C 222.49312,225.64852 228.20494,221.66299 228.20494,221.66299 C 228.60659,221.38273 229.29903,221.31772 229.75901,221.51708 C 229.75901,221.51708 229.75901,221.51708 229.75901,221.51708" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5307" + d="M 241.4331,233.77444 C 241.4331,233.77444 234.86702,230.51453 234.86702,230.51453 C 234.2884,230.22725 233.46852,230.2512 233.03004,230.56861 C 233.03004,230.56861 228.00571,234.20578 228.00571,234.20578 C 227.5701,234.52112 227.69,235.00165 228.27321,235.28282 C 228.27321,235.28282 234.89177,238.47354 234.89177,238.47354 C 235.46008,238.74751 236.26364,238.71874 236.69472,238.40938 C 236.69472,238.40938 241.66657,234.84141 241.66657,234.84141 C 242.10045,234.53004 241.99687,234.05434 241.4331,233.77444 C 241.4331,233.77444 241.4331,233.77444 241.4331,233.77444" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5309" + d="M 250.21733,243.40142 C 250.21733,243.40142 255.39176,239.30139 255.39176,239.30139 C 255.89164,238.9053 255.71333,238.31587 254.9964,237.97824 C 254.9964,237.97824 247.93065,234.65074 247.93065,234.65074 C 247.23226,234.32185 246.25306,234.35802 245.73067,234.73327 C 245.73067,234.73327 240.32649,238.61528 240.32649,238.61528 C 239.76575,239.01809 239.8905,239.62564 240.61117,239.97588 C 240.61117,239.97588 247.90666,243.52145 247.90666,243.52145 C 248.64734,243.88142 249.68011,243.82709 250.21733,243.40142 C 250.21733,243.40142 250.21733,243.40142 250.21733,243.40142" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 259.03235,236.38432 C 259.03235,236.38432 264.00233,232.5172 264.00233,232.5172 C 264.49375,232.13483 264.28734,231.56751 263.54605,231.2468 C 263.54605,231.2468 256.4643,228.18292 256.4643,228.18292 C 255.78568,227.88932 254.86097,227.94181 254.38524,228.29923 C 254.38524,228.29923 249.57716,231.91156 249.57716,231.91156 C 249.08982,232.2777 249.22635,232.82706 249.889,233.14485 C 249.889,233.14485 256.80881,236.46342 256.80881,236.46342 C 257.53364,236.81102 258.52826,236.77654 259.03235,236.38432 C 259.03235,236.38432 259.03235,236.38432 259.03235,236.38432" + id="path5311" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5313" + d="M 249.92718,227.47874 C 249.92718,227.47874 243.57843,224.26363 243.57843,224.26363 C 242.94161,223.94113 242.03351,223.96693 241.54071,224.32288 C 241.54071,224.32288 236.50753,227.95826 236.50753,227.95826 C 235.99196,228.33065 236.10354,228.89802 236.75945,229.2289 C 236.75945,229.2289 243.29817,232.52743 243.29817,232.52743 C 243.95188,232.8572 244.8789,232.81481 245.37499,232.4339 C 245.37499,232.4339 250.21821,228.7151 250.21821,228.7151 C 250.69243,228.35098 250.56193,227.80019 249.92718,227.47874 C 249.92718,227.47874 249.92718,227.47874 249.92718,227.47874" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5315" + d="M 278.37779,233.42877 C 278.37779,233.42877 271.86628,230.49633 271.86628,230.49633 C 271.35525,230.26619 270.61009,230.34047 270.19363,230.66278 C 270.19363,230.66278 264.92861,234.73754 264.92861,234.73754 C 264.50283,235.06706 264.56772,235.52596 265.07602,235.76657 C 265.07602,235.76657 271.55435,238.83315 271.55435,238.83315 C 272.08251,239.08315 272.85436,239.01044 273.28293,238.66998 C 273.28293,238.66998 278.58112,234.46107 278.58112,234.46107 C 279.00009,234.12824 278.90852,233.66778 278.37779,233.42877 C 278.37779,233.42877 278.37779,233.42877 278.37779,233.42877" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 286.64967,226.73945 C 286.64967,226.73945 280.75469,223.85413 280.75469,223.85413 C 280.1553,223.56076 279.26816,223.63562 278.7659,224.02366 C 278.7659,224.02366 273.67528,227.95655 273.67528,227.95655 C 273.15783,228.35631 273.24091,228.914 273.86123,229.20522 C 273.86123,229.20522 279.9603,232.06855 279.9603,232.06855 C 280.56171,232.3509 281.4454,232.25691 281.94196,231.85949 C 281.94196,231.85949 286.82847,227.94856 286.82847,227.94856 C 287.31074,227.56258 287.23111,227.02404 286.64967,226.73945 C 286.64967,226.73945 286.64967,226.73945 286.64967,226.73945" + id="path5317" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5319" + d="M 267.6823,229.91307 C 267.6823,229.91307 272.46908,226.23566 272.46908,226.23566 C 272.94434,225.87054 272.74124,225.3253 272.01611,225.01342 C 272.01611,225.01342 264.88193,221.94496 264.88193,221.94496 C 264.17799,221.64219 263.23154,221.68466 262.7575,222.03994 C 262.7575,222.03994 257.98381,225.61763 257.98381,225.61763 C 257.50171,225.97894 257.67724,226.52125 258.37978,226.83381 C 258.37978,226.83381 265.50086,230.00199 265.50086,230.00199 C 266.22477,230.32406 267.1988,230.28451 267.6823,229.91307 C 267.6823,229.91307 267.6823,229.91307 267.6823,229.91307" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 258.51488,221.22986 C 258.51488,221.22986 252.19037,218.21416 252.19037,218.21416 C 251.57618,217.9213 250.69327,217.96397 250.20557,218.30991 C 250.20557,218.30991 245.20204,221.8591 245.20204,221.8591 C 244.68719,222.22431 244.76403,222.77336 245.37981,223.09013 C 245.37981,223.09013 251.72747,226.35547 251.72747,226.35547 C 252.38375,226.69307 253.32987,226.6498 253.84288,226.25849 C 253.84288,226.25849 258.82312,222.45965 258.82312,222.45965 C 259.30803,222.08977 259.16806,221.54132 258.51488,221.22986 C 258.51488,221.22986 258.51488,221.22986 258.51488,221.22986" + id="path5321" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5323" + d="M 239.22906,214.77058 C 239.22906,214.77058 245.75948,217.65839 245.75948,217.65839 C 246.22836,217.86574 246.27624,218.27214 245.86583,218.5699 C 245.86583,218.5699 240.04074,222.79608 240.04074,222.79608 C 239.61975,223.10153 238.89933,223.17644 238.42646,222.96374 C 238.42646,222.96374 231.84118,220.00174 231.84118,220.00174 C 231.37421,219.79169 231.34116,219.38081 231.76608,219.08076 C 231.76608,219.08076 237.64602,214.92874 237.64602,214.92874 C 238.06034,214.63619 238.76594,214.56577 239.22906,214.77058 C 239.22906,214.77058 239.22906,214.77058 239.22906,214.77058" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5325" + d="M 247.90724,208.64597 C 247.90724,208.64597 254.53759,211.50631 254.53759,211.50631 C 255.01636,211.71285 255.09618,212.10048 254.71562,212.37537 C 254.71562,212.37537 249.34818,216.25246 249.34818,216.25246 C 248.96271,216.5309 248.26761,216.58434 247.79061,216.37235 C 247.79061,216.37235 241.18538,213.43689 241.18538,213.43689 C 240.71963,213.22991 240.65428,212.84354 241.03791,212.57052 C 241.03791,212.57052 246.38024,208.76857 246.38024,208.76857 C 246.75905,208.49898 247.43968,208.44427 247.90724,208.64597 C 247.90724,208.64597 247.90724,208.64597 247.90724,208.64597" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5327" + d="M 266.53609,215.35847 C 266.53609,215.35847 260.2425,212.50539 260.2425,212.50539 C 259.63111,212.22823 258.77381,212.26339 258.31648,212.58345 C 258.31648,212.58345 253.69899,215.81504 253.69899,215.81504 C 253.23142,216.14228 253.3369,216.6411 253.93933,216.93434 C 253.93933,216.93434 260.14335,219.95417 260.14335,219.95417 C 260.78415,220.26608 261.68732,220.24081 262.16416,219.89654 C 262.16416,219.89654 266.87121,216.49816 266.87121,216.49816 C 267.3372,216.16172 267.18586,215.65303 266.53609,215.35847 C 266.53609,215.35847 266.53609,215.35847 266.53609,215.35847" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5329" + d="M 275.86123,223.49708 C 275.86123,223.49708 280.69597,219.76003 280.69597,219.76003 C 281.17477,219.38993 280.98509,218.85214 280.27334,218.55429 C 280.27334,218.55429 273.2735,215.62511 273.2735,215.62511 C 272.58309,215.3362 271.64347,215.39287 271.16418,215.75208 C 271.16418,215.75208 266.32552,219.37853 266.32552,219.37853 C 265.83563,219.74569 265.99513,220.28253 266.68578,220.58229 C 266.68578,220.58229 273.68954,223.62208 273.68954,223.62208 C 274.40184,223.93123 275.37164,223.87552 275.86123,223.49708 C 275.86123,223.49708 275.86123,223.49708 275.86123,223.49708" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 282.59083,221.04513 C 282.59083,221.04513 287.71903,217.05699 287.71903,217.05699 C 288.22587,216.66285 289.08472,216.56532 289.64647,216.83783 C 289.64647,216.83783 295.29725,219.57907 295.29725,219.57907 C 295.86731,219.85561 295.92338,220.40166 295.42118,220.80393 C 295.42118,220.80393 290.33924,224.87461 290.33924,224.87461 C 289.82349,225.28773 288.94404,225.3914 288.36924,225.1065 C 288.36924,225.1065 282.67221,222.28271 282.67221,222.28271 C 282.10592,222.00203 282.07045,221.44982 282.59083,221.04513 C 282.59083,221.04513 282.59083,221.04513 282.59083,221.04513" + id="path5331" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5333" + d="M 273.82385,209.9072 C 273.82385,209.9072 267.67742,207.1135 267.67742,207.1135 C 267.05691,206.83146 266.21983,206.85051 265.80038,207.15733 C 265.80038,207.15733 261.55003,210.26641 261.55003,210.26641 C 261.11807,210.58238 261.28238,211.06634 261.91859,211.35037 C 261.91859,211.35037 268.21969,214.1634 268.21969,214.1634 C 268.84554,214.4428 269.68458,214.41091 270.10075,214.09302 C 270.10075,214.09302 274.19645,210.96462 274.19645,210.96462 C 274.60069,210.65585 274.43443,210.18473 273.82385,209.9072 C 273.82385,209.9072 273.82385,209.9072 273.82385,209.9072" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5335" + d="M 257.3726,201.76885 C 257.3726,201.76885 264.64202,204.86157 264.64202,204.86157 C 264.95177,204.99335 264.97965,205.26073 264.70378,205.46149 C 264.70378,205.46149 257.98723,210.34941 257.98723,210.34941 C 257.69315,210.56343 257.19866,210.62527 256.87934,210.48761 C 256.87934,210.48761 249.38649,207.25742 249.38649,207.25742 C 249.07032,207.12112 249.06104,206.84361 249.36486,206.63564 C 249.36486,206.63564 256.30482,201.88518 256.30482,201.88518 C 256.58991,201.69003 257.06581,201.63832 257.3726,201.76885 C 257.3726,201.76885 257.3726,201.76885 257.3726,201.76885" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5337" + d="M 284.0905,217.40082 C 284.0905,217.40082 288.28521,214.18116 288.28521,214.18116 C 288.70353,213.86008 288.47298,213.36105 287.76806,213.06182 C 287.76806,213.06182 280.69745,210.06045 280.69745,210.06045 C 279.9862,209.75854 279.07181,209.77379 278.64748,210.09508 C 278.64748,210.09508 274.39236,213.31696 274.39236,213.31696 C 273.96443,213.64097 274.19761,214.14507 274.91495,214.44677 C 274.91495,214.44677 282.04596,217.44592 282.04596,217.44592 C 282.75689,217.74492 283.66866,217.7246 284.0905,217.40082 C 284.0905,217.40082 284.0905,217.40082 284.0905,217.40082" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5339" + d="M 300.10154,217.06983 C 300.10154,217.06983 316.31837,203.96829 316.31837,203.96829 C 317.07607,203.35614 317.4003,202.7538 317.04873,202.61609 C 317.04873,202.61609 310.03269,199.86796 310.03269,199.86796 C 309.70146,199.73822 308.83575,200.10447 308.08844,200.69068 C 308.08844,200.69068 292.11577,213.22035 292.11577,213.22035 C 291.30809,213.85393 290.91207,214.49139 291.23134,214.64793 C 291.23134,214.64793 298.003,217.96805 298.003,217.96805 C 298.34279,218.13465 299.2804,217.73322 300.10154,217.06983 C 300.10154,217.06983 300.10154,217.06983 300.10154,217.06983" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 291.15172,211.81971 C 291.15172,211.81971 294.95784,208.85652 294.95784,208.85652 C 295.33535,208.56261 295.06588,208.08714 294.35728,207.79127 C 294.35728,207.79127 287.46322,204.91266 287.46322,204.91266 C 286.7905,204.63177 285.951,204.6327 285.57753,204.91398 C 285.57753,204.91398 281.81341,207.749 281.81341,207.749 C 281.43293,208.03556 281.66197,208.50175 282.33038,208.79505 C 282.33038,208.79505 289.18243,211.80181 289.18243,211.80181 C 289.88692,212.11095 290.76688,212.11932 291.15172,211.81971 C 291.15172,211.81971 291.15172,211.81971 291.15172,211.81971" + id="path5341" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 279.78845,205.15721 C 279.78845,205.15721 273.64923,202.7238 273.64923,202.7238 C 273.07415,202.49585 272.35188,202.50572 272.02271,202.74427 C 272.02271,202.74427 268.6721,205.17246 268.6721,205.17246 C 268.33004,205.42034 268.50084,205.82253 269.06217,206.07617 C 269.06217,206.07617 275.0647,208.78839 275.0647,208.78839 C 275.70867,209.07937 276.52129,209.09274 276.87788,208.8159 C 276.87788,208.8159 280.365,206.10863 280.365,206.10863 C 280.70701,205.84311 280.44598,205.41784 279.78845,205.15721 C 279.78845,205.15721 279.78845,205.15721 279.78845,205.15721" + id="path5343" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5345" + d="M 266.63729,203.4528 C 266.63729,203.4528 259.22437,200.28887 259.22437,200.28887 C 258.90107,200.15088 259.26093,199.6067 260.02623,199.07179 C 260.02623,199.07179 276.04515,187.8754 276.04515,187.8754 C 276.70328,187.4154 277.47826,187.14286 277.78675,187.26189 C 277.78675,187.26189 284.85126,189.98782 284.85126,189.98782 C 285.16872,190.11032 284.90984,190.59119 284.26684,191.06857 C 284.26684,191.06857 268.59644,202.70261 268.59644,202.70261 C 267.84684,203.25912 266.97083,203.59516 266.63729,203.4528 C 266.63729,203.4528 266.63729,203.4528 266.63729,203.4528" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5347" + sodipodi:nodetypes="cccccssssccccc" + d="M 306.08894,198.05464 C 306.08894,198.05464 289.86483,191.75754 289.86483,191.75754 C 288.75309,191.32603 287.34294,191.34222 286.72043,191.80216 C 286.72043,191.80216 275.86985,199.81916 275.86985,199.81916 C 275.21811,200.30071 275.08509,200.71469 276.34881,201.06644 L 282.51762,203.58752 C 283.51554,203.99535 283.51356,204.01646 284.38014,203.38499 L 285.53498,202.54346 C 285.7875,202.35945 286.54428,202.2881 287.35569,202.6377 L 295.71352,206.2387 C 296.63524,206.49525 297.43593,206.57335 297.95625,206.16455 C 297.95625,206.16455 306.65122,199.3329 306.65122,199.3329 C 307.15193,198.93949 306.90591,198.37173 306.08894,198.05464 C 306.08894,198.05464 306.08894,198.05464 306.08894,198.05464" + style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + </g> + <path + d="M 195.84384,316.18138 C 198.01729,317.52381 197.72963,318.48268 195.26851,320.68809 C 192.78733,322.9115 196.00365,319.69726 196.67486,318.67446 C 197.37566,317.60657 193.70779,314.86206 195.84384,316.18138 z " + sodipodi:nodetypes="czzz" + id="path2379" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3350" + sodipodi:nodetypes="czzz" + d="M 216.172,300.16816 C 218.34546,301.51059 218.79294,302.1818 216.33181,304.38722 C 213.85064,306.61061 216.81125,303.42834 217.61032,302.34161 C 218.38754,301.28458 214.03595,298.84884 216.172,300.16816 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 206.4554,308.31861 C 208.62885,309.66103 209.20418,310.14046 206.74306,312.34587 C 204.26188,314.56927 207.35035,311.1313 207.82978,310.49205 C 208.39381,309.74001 204.31935,306.99928 206.4554,308.31861 z " + sodipodi:nodetypes="czzz" + id="path3352" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3354" + sodipodi:nodetypes="czzz" + d="M 225.50506,292.46519 C 227.67851,293.80762 228.25383,294.28706 225.79272,296.49247 C 223.31154,298.71586 227.0073,295.62948 226.78356,294.4149 C 226.56079,293.20564 223.36901,291.14588 225.50506,292.46519 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3356" + sodipodi:nodetypes="czzz" + d="M 203.49886,293.36015 C 205.63491,294.67947 206.0239,295.13407 203.48288,297.25957 C 200.9308,299.39433 204.5367,295.76662 204.79335,295.2779 C 205.04344,294.80165 201.36281,292.04083 203.49886,293.36015 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 189.00386,289.3009 C 191.59281,290.73922 191.94897,291.213 189.29153,293.32818 C 186.61856,295.45568 190.37824,292.20949 190.37824,291.21865 C 190.37824,290.19585 186.42834,287.87006 189.00386,289.3009 z " + sodipodi:nodetypes="czzz" + id="path3358" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3360" + sodipodi:nodetypes="czzz" + d="M 171.16877,285.43344 C 173.47007,286.64801 174.17325,287.38316 171.45643,289.46072 C 168.74413,291.53484 172.83082,288.34203 172.60709,287.38316 C 172.36917,286.36356 168.8898,284.23064 171.16877,285.43344 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 155.9546,281.50205 C 158.12806,282.78055 158.83124,283.54764 156.24227,285.52933 C 153.64201,287.51964 157.42488,284.66634 157.20114,283.38784 C 156.97829,282.11439 153.75272,280.20681 155.9546,281.50205 z " + sodipodi:nodetypes="czzz" + id="path3362" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 161.10057,292.56108 C 163.27403,293.9035 163.96914,294.4897 161.38823,296.58835 C 158.81117,298.6839 157.15517,299.80076 160.6531,296.71621 C 164.16898,293.61583 158.96453,291.24176 161.10057,292.56108 z " + sodipodi:nodetypes="czzz" + id="path3364" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 178.74389,296.62031 C 182.35566,298.53807 181.81229,298.31433 176.12296,302.85301 C 170.44917,307.37928 178.76168,300.08938 179.60688,299.14536 C 180.42197,298.23497 175.15702,294.71579 178.74389,296.62031 z " + sodipodi:nodetypes="czzz" + id="path3366" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 193.22292,300.42386 C 195.39636,301.76628 195.49224,303.36442 189.3874,308.28664 C 183.26317,313.22451 195.05807,303.89438 193.51058,301.89414 C 192.00596,299.94933 191.08687,299.10454 193.22292,300.42386 z " + sodipodi:nodetypes="czzz" + id="path3368" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3370" + sodipodi:nodetypes="czzz" + d="M 145.18324,288.69362 C 147.80416,290.22781 147.8681,290.89903 145.18324,292.91266 C 142.52875,294.90353 146.62155,291.63417 146.39782,290.67529 C 146.18115,289.74671 142.58609,287.17332 145.18324,288.69362 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3372" + sodipodi:nodetypes="czzz" + d="M 165.73514,274.56618 C 167.9086,275.9086 168.61177,276.57982 166.0228,278.59347 C 163.40256,280.63143 166.7899,277.25103 167.20542,276.73963 C 167.59048,276.2657 163.5991,273.24685 165.73514,274.56618 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 181.26892,278.14598 C 183.44237,279.48841 184.0177,279.96784 181.55659,282.17326 C 179.07541,284.39665 182.5794,281.34223 182.35566,280.06373 C 182.13279,278.79029 179.13287,276.82666 181.26892,278.14598 z " + sodipodi:nodetypes="czzz" + id="path3374" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3376" + sodipodi:nodetypes="czzz" + d="M 198.2659,281.884 C 200.43936,283.22641 201.01468,283.70585 198.55356,285.91126 C 196.07238,288.13466 199.57637,285.08024 199.35263,283.80174 C 199.12976,282.52829 196.12986,280.56467 198.2659,281.884 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 213.08942,285.57681 C 215.8957,286.96443 216.29022,287.35347 213.37708,289.60408 C 210.50713,291.82133 214.72017,288.27028 214.62817,287.40415 C 214.54091,286.58272 210.2463,284.17095 213.08942,285.57681 z " + sodipodi:nodetypes="czzz" + id="path3378" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3380" + sodipodi:nodetypes="czzz" + d="M 175.13212,267.66227 C 177.30557,269.00471 177.99635,269.63282 175.41979,271.68956 C 172.87468,273.72117 176.44259,270.7946 176.41062,269.64395 C 176.37866,268.4933 172.99607,266.34296 175.13212,267.66227 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3382" + sodipodi:nodetypes="czzz" + d="M 190.72984,270.85853 C 192.96721,272.0731 193.60646,272.74431 191.01751,274.88581 C 188.47105,276.99212 192.23208,274.11871 192.00834,272.84021 C 191.78548,271.56676 188.48472,269.63975 190.72984,270.85853 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 207.55763,274.67852 C 209.73109,276.02094 210.30641,276.50038 207.8453,278.70579 C 205.36412,280.92919 208.8681,277.87477 208.64437,276.59627 C 208.4215,275.32282 205.42158,273.35919 207.55763,274.67852 z " + sodipodi:nodetypes="czzz" + id="path3384" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3386" + sodipodi:nodetypes="czzz" + d="M 223.20376,277.63457 C 226.23604,279.29342 225.95254,279.45644 223.49142,281.66186 C 221.01023,283.88525 224.74999,280.23696 224.6521,279.41673 C 224.56485,278.6857 220.20621,275.99474 223.20376,277.63457 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 277.38117,248.70302 C 281.95031,251.26589 281.48626,250.13465 270.88855,258.92295 C 260.38024,267.63712 274.33964,254.68517 277.12282,252.1444 C 279.88337,249.62427 272.7431,246.10148 277.38117,248.70302 z " + sodipodi:nodetypes="czzz" + id="path3388" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3390" + sodipodi:nodetypes="czzz" + d="M 231.57794,270.34712 C 233.75139,271.68956 234.32672,272.16899 231.86561,274.37441 C 229.38442,276.5978 232.88839,273.54338 232.66467,272.26488 C 232.4418,270.99143 229.44189,269.02781 231.57794,270.34712 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 241.29454,263.69892 C 243.46799,265.04135 244.04332,265.52079 241.58221,267.72621 C 239.10103,269.9496 242.60501,266.89517 242.38128,265.61667 C 242.15841,264.34322 239.15849,262.3796 241.29454,263.69892 z " + sodipodi:nodetypes="czzz" + id="path3392" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3394" + sodipodi:nodetypes="czzz" + d="M 250.24405,256.53932 C 252.4175,257.88175 252.99283,258.36119 250.53172,260.5666 C 248.05053,262.78999 251.55451,259.73557 251.33077,258.45707 C 251.10792,257.18362 248.10801,255.21999 250.24405,256.53932 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 258.68216,249.37971 C 260.85561,250.72214 261.43093,251.20158 258.96982,253.40699 C 256.48864,255.63038 259.99262,252.57596 259.76888,251.29747 C 259.54603,250.02402 256.54611,248.06039 258.68216,249.37971 z " + sodipodi:nodetypes="czzz" + id="path3396" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3398" + sodipodi:nodetypes="czzz" + d="M 215.08528,267.40658 C 217.25872,268.749 217.83405,269.22845 215.37294,271.43386 C 212.89175,273.65725 216.39574,270.60283 216.172,269.32432 C 215.94914,268.05088 212.94923,266.08725 215.08528,267.40658 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 200.54781,263.45098 C 204.31939,265.43265 203.68014,265.40068 200.83547,267.47825 C 198.05046,269.51225 201.27066,266.64722 202.04136,265.54954 C 202.78342,264.49262 196.80222,261.48295 200.54781,263.45098 z " + sodipodi:nodetypes="czzz" + id="path3400" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3402" + sodipodi:nodetypes="czzz" + d="M 185.48798,260.50267 C 188.30069,262.16472 188.68423,262.2606 185.77564,264.52995 C 182.90663,266.76843 187.05414,263.76284 186.83041,262.48434 C 186.60755,261.21089 182.72253,258.86853 185.48798,260.50267 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 194.50141,253.47091 C 196.67486,254.81334 197.25018,255.29277 194.78907,257.49819 C 192.30789,259.72159 195.81187,256.66717 195.58814,255.38867 C 195.36528,254.11522 192.36537,252.15159 194.50141,253.47091 z " + sodipodi:nodetypes="czzz" + id="path3404" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3406" + sodipodi:nodetypes="czzz" + d="M 204.79335,246.88664 C 206.96679,248.22906 207.54211,248.7085 205.081,250.91391 C 202.59982,253.13731 206.10381,250.08289 205.88007,248.80439 C 205.65721,247.53094 202.6573,245.56731 204.79335,246.88664 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 209.97127,256.34754 C 212.14473,257.68997 212.72004,258.16941 210.25893,260.37482 C 207.77775,262.59821 211.28174,259.54379 211.058,258.26529 C 210.83513,256.99184 207.83522,255.02823 209.97127,256.34754 z " + sodipodi:nodetypes="czzz" + id="path3408" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3410" + sodipodi:nodetypes="czzz" + d="M 219.94358,248.99617 C 222.11703,250.33858 222.69236,250.81803 220.23124,253.02345 C 217.75006,255.24684 221.25405,252.19241 221.03031,250.91391 C 220.80745,249.64046 217.80754,247.67683 219.94358,248.99617 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 226.08039,260.5666 C 228.25383,261.90901 228.82916,262.38845 226.36805,264.59388 C 223.88687,266.81727 227.39085,263.76284 227.16711,262.48434 C 226.94426,261.21089 223.94434,259.24727 226.08039,260.5666 z " + sodipodi:nodetypes="czzz" + id="path3412" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3414" + sodipodi:nodetypes="czzz" + d="M 234.39063,254.11016 C 236.56408,255.4526 237.13941,255.93204 234.6783,258.13745 C 232.19712,260.36084 235.70111,257.30642 235.47737,256.02792 C 235.25451,254.75447 232.25458,252.79084 234.39063,254.11016 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 229.27663,242.2201 C 231.45009,243.56254 232.02541,244.04197 229.5643,246.24738 C 227.08312,248.47078 230.58711,245.41636 230.36337,244.13786 C 230.1405,242.86442 227.14058,240.90079 229.27663,242.2201 z " + sodipodi:nodetypes="czzz" + id="path3416" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3418" + sodipodi:nodetypes="czzz" + d="M 243.0141,247.59302 C 245.39457,248.78588 245.85328,249.41488 243.39217,251.6203 C 240.91099,253.84369 244.68618,250.11125 244.59804,249.42037 C 244.51044,248.73372 240.56163,246.3641 243.0141,247.59302 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 213.8707,239.98274 C 216.04415,241.32515 216.61948,241.8046 214.15837,244.01001 C 211.67717,246.23341 215.18116,243.17898 214.95742,241.90048 C 214.73456,240.62703 211.73465,238.66341 213.8707,239.98274 z " + sodipodi:nodetypes="czzz" + id="path3420" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3422" + sodipodi:nodetypes="czzz" + d="M 222.91837,233.04137 C 225.31782,234.20299 225.93836,234.99885 223.20602,237.06866 C 220.48021,239.13354 224.63565,235.424 224.54751,234.77832 C 224.44726,234.04379 220.53635,231.8882 222.91837,233.04137 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 246.64686,229.49127 C 249.00111,230.74329 249.89286,231.08713 246.93452,233.51855 C 243.99824,235.93184 248.36413,232.59712 248.14039,231.31861 C 247.91754,230.04517 244.31107,228.24907 246.64686,229.49127 z " + sodipodi:nodetypes="czzz" + id="path3424" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3426" + sodipodi:nodetypes="czzz" + d="M 237.71153,235.84633 C 240.02059,237.03055 240.64112,237.84899 237.9992,239.87362 C 235.36048,241.89576 239.38361,238.50015 239.25027,237.67368 C 239.10149,236.75149 235.41158,234.66678 237.71153,235.84633 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 284.38244,228.84004 C 286.66889,230.04686 287.0481,230.84805 284.67009,232.86732 C 282.27897,234.89772 285.51201,231.73741 285.85337,231.07421 C 286.18578,230.42841 282.11363,227.64254 284.38244,228.84004 z " + sodipodi:nodetypes="czzz" + id="path3428" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3430" + sodipodi:nodetypes="czzz" + d="M 232.52396,226.41189 C 234.78783,227.52831 235.40834,228.14335 232.81163,230.16796 C 230.19284,232.20977 234.24125,228.7945 234.06271,228.10364 C 233.88666,227.4224 230.2964,225.31338 232.52396,226.41189 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 242.49949,219.69334 C 245.873,221.52582 244.75103,221.244 242.19951,223.3138 C 239.67516,225.36158 243.76147,221.8425 243.54101,221.11387 C 243.32053,220.38523 239.12596,217.86086 242.49949,219.69334 z " + sodipodi:nodetypes="czzz" + id="path3432" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3434" + sodipodi:nodetypes="czzz" + d="M 255.29092,223.29442 C 257.75818,224.59164 258.28831,225.25187 255.57858,227.32169 C 252.87211,229.38902 256.89519,225.88045 256.80705,225.21217 C 256.71558,224.51847 252.84135,222.00649 255.29092,223.29442 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 269.10194,227.3895 C 271.27539,228.73192 271.85071,229.21136 269.3896,231.41677 C 266.90842,233.64017 270.41241,230.58576 270.18867,229.30725 C 269.96581,228.0338 266.96589,226.07017 269.10194,227.3895 z " + sodipodi:nodetypes="czzz" + id="path3436" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3438" + sodipodi:nodetypes="czzz" + d="M 260.47206,233.782 C 262.64551,235.12443 263.22084,235.60386 260.75972,237.80927 C 258.27854,240.03267 261.78252,236.97826 261.55878,235.69975 C 261.33593,234.42631 258.33601,232.46268 260.47206,233.782 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 252.00426,240.41924 C 254.76534,241.85208 254.88865,242.2863 252.29193,244.44653 C 249.69924,246.60338 253.58595,242.98267 253.49781,242.15619 C 253.40345,241.27142 249.33359,239.0316 252.00426,240.41924 z " + sodipodi:nodetypes="czzz" + id="path3440" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3442" + sodipodi:nodetypes="czzz" + d="M 266.73671,242.2201 C 268.91017,243.56254 269.4855,244.04197 267.02438,246.24738 C 264.54319,248.47078 268.04717,245.41636 267.82343,244.13786 C 267.60058,242.86442 264.60067,240.90079 266.73671,242.2201 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 275.77863,235.80888 C 278.29108,237.0157 278.6856,237.56294 276.08889,239.70055 C 273.50776,241.82532 277.52012,238.29917 277.43037,237.50061 C 277.34312,236.7244 273.32091,234.62837 275.77863,235.80888 z " + sodipodi:nodetypes="czzz" + id="path3444" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3446" + sodipodi:nodetypes="czzz" + d="M 286.95302,240.94161 C 289.63786,242.37993 289.92552,242.74749 287.24067,244.96888 C 284.57146,247.17734 287.44366,244.39791 288.24749,243.37076 C 289.03146,242.369 284.28786,239.51385 286.95302,240.94161 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 295.77487,234.08611 C 298.28732,235.22513 298.70444,235.86277 296.06253,238.11339 C 293.44279,240.34511 296.0466,237.62948 297.1102,236.38809 C 298.1513,235.17297 293.28962,232.95944 295.77487,234.08611 z " + sodipodi:nodetypes="czzz" + id="path3448" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3450" + sodipodi:nodetypes="czzz" + d="M 304.36663,227.14477 C 306.83388,228.44201 307.1154,228.96663 304.65428,231.17204 C 302.17311,233.39544 304.72941,230.49547 305.36295,229.31112 C 306.04412,228.03769 301.92637,225.86174 304.36663,227.14477 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 293.12178,221.54036 C 295.47102,222.691 295.98242,223.42615 293.40945,225.56763 C 290.83798,227.70786 294.60852,224.14154 294.54411,223.33025 C 294.48107,222.53624 290.77257,220.38972 293.12178,221.54036 z " + sodipodi:nodetypes="czzz" + id="path3452" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3454" + sodipodi:nodetypes="czzz" + d="M 277.5149,220.70385 C 280.36638,222.31747 280.39928,222.70652 277.80256,224.73113 C 275.18377,226.77294 276.77322,224.86768 278.55642,223.48044 C 280.36765,222.07138 274.77049,219.15079 277.5149,220.70385 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 263.22084,217.41719 C 265.39428,218.75961 265.96961,219.23905 263.5085,221.44447 C 261.02732,223.66786 264.5313,220.61343 264.30756,219.33494 C 264.08471,218.06149 261.08479,216.09787 263.22084,217.41719 z " + sodipodi:nodetypes="czzz" + id="path3456" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3458" + sodipodi:nodetypes="czzz" + d="M 251.15585,213.31501 C 254.39256,214.77625 254.35665,214.86567 251.3531,216.98068 C 248.32814,219.11078 252.86671,215.85366 252.60418,214.91636 C 252.33613,213.95933 247.95664,211.87072 251.15585,213.31501 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 260.85561,206.10245 C 263.02906,207.44489 263.60438,207.92432 261.14327,210.12974 C 258.66209,212.35313 262.16607,209.29871 261.94234,208.02022 C 261.71947,206.74677 258.71956,204.78313 260.85561,206.10245 z " + sodipodi:nodetypes="czzz" + id="path3460" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3462" + sodipodi:nodetypes="czzz" + d="M 271.01969,211.72786 C 273.19314,213.07028 273.76847,213.54972 271.30736,215.75513 C 268.82618,217.97853 272.33015,214.92411 272.10641,213.64561 C 271.88356,212.37216 268.88364,210.40854 271.01969,211.72786 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 284.99091,214.99901 C 287.70679,216.25102 288.10131,216.82086 285.27858,219.02628 C 282.51164,221.18811 285.03572,219.0993 286.34886,217.27838 C 287.55847,215.60098 282.34938,213.78125 284.99091,214.99901 z " + sodipodi:nodetypes="czzz" + id="path3464" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3466" + sodipodi:nodetypes="czzz" + d="M 276.90079,206.80563 C 279.07425,208.14806 279.64957,208.6275 277.18845,210.83291 C 274.70727,213.05631 278.21126,210.00188 277.98752,208.72339 C 277.76466,207.44994 274.76474,205.48631 276.90079,206.80563 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 292.17888,209.68226 C 294.35233,211.02468 294.92766,211.50412 292.46654,213.70954 C 289.98536,215.93294 293.48934,212.87852 293.2656,211.60001 C 293.04275,210.32657 290.04283,208.36293 292.17888,209.68226 z " + sodipodi:nodetypes="czzz" + id="path3468" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3470" + sodipodi:nodetypes="czzz" + d="M 314.68049,204.24863 C 317.7489,205.39928 317.87645,205.56214 311.38835,210.80095 C 304.93164,216.01443 312.98405,209.0753 315.15994,207.02937 C 317.35285,204.96744 311.62248,203.10188 314.68049,204.24863 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 303.62147,199.96565 C 305.79492,201.30808 306.37025,201.78752 303.90913,203.99293 C 301.42794,206.21632 304.93192,203.16191 304.70818,201.8834 C 304.48533,200.60995 301.48542,198.64633 303.62147,199.96565 z " + sodipodi:nodetypes="czzz" + id="path3472" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3474" + sodipodi:nodetypes="czzz" + d="M 281.07787,191.30607 C 284.96899,192.8745 284.82107,192.72112 278.69861,197.41265 C 272.5826,202.09922 281.39387,194.86394 282.66181,193.63065 C 283.93119,192.39593 277.15462,189.7247 281.07787,191.30607 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3477" + d="M 247.0478,21.870476 L 41.113266,89.503174 L 48.496608,139.14097 C 116.54867,104.13919 192.87556,126.61352 246.15284,103.7265 L 247.0478,21.870476 z " + style="fill:url(#linearGradient2500);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + inkscape:transform-center-x="21.926591" + inkscape:transform-center-y="-98.60823" + d="M 27.882376,75.746856 C 32.065415,74.206702 33.389047,74.781014 34.949531,76.633878 L 65.088465,277.78657 L 56.641394,273.5449 L 27.882376,75.746856 z " + sodipodi:nodetypes="ccccc" + id="path2180" + style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path13298" + sodipodi:nodetypes="ccccc" + d="M 92.828627,259.35202 L 230.90672,174.71526 C 232.64824,175.78414 234.17161,176.98389 234.23083,179.06216 L 96.66413,263.82678 C 96.420949,261.86067 95.117349,260.38059 92.828627,259.35202 z " + style="fill:#808080;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + id="path14269" + sodipodi:nodetypes="ccccc" + d="M 166.43526,236.83511 L 228.92582,195.64614 L 229.45615,196.53002 L 168.02625,238.24933 L 166.43526,236.83511 z " + style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" /> + <path + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + d="M 114.5513,270.06913 L 130.10765,259.99286 L 130.63798,260.87674 L 116.14229,271.48335 L 114.5513,270.06913 z " + sodipodi:nodetypes="ccccc" + id="path16261" + style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" /> + <path + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + d="M 114.5513,270.06913 L 130.10765,259.99286 L 130.63798,260.87674 L 116.14229,271.48335 L 114.5513,270.06913 z " + sodipodi:nodetypes="ccccc" + id="path16263" + style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" /> + <path + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + d="M 166.43526,236.83511 L 228.92582,195.64614 L 229.45615,196.53002 L 168.02625,238.24933 L 166.43526,236.83511 z " + sodipodi:nodetypes="ccccc" + id="path16267" + style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" /> + <path + d="M 104.05869,273.38134 C 106.59601,271.03005 113.13546,275.43531 112.49482,278.36804 L 104.05869,273.38134 z " + sodipodi:nodetypes="ccc" + id="path16269" + style="opacity:0.99720004;fill:#666666;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccsccccc" + id="path25800" + d="M 304.06894,248.1971 C 302.1656,248.29093 300.4498,248.80189 299.33849,249.7313 C 299.33849,249.7313 260.56796,282.17326 260.56795,282.17326 C 257.93245,284.37739 258.86046,287.65621 262.70943,289.52464 C 262.70943,289.52464 267.73895,291.93253 272.68174,294.28706 C 306.66914,288.15442 283.9982,253.02759 313.94536,251.0098 C 312.61326,250.41591 309.88612,249.2199 309.88612,249.2199 C 308.06831,248.43649 305.97228,248.10326 304.06894,248.1971 z " + style="fill:url(#linearGradient2490);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.9527356;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + </g> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg b/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg new file mode 100644 index 0000000..b3abf0a --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="128" + height="128" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="wpa_gui.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <metadata + id="metadata47"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + inkscape:window-height="771" + inkscape:window-width="640" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="false" + inkscape:zoom="4.2421875" + inkscape:cx="64" + inkscape:cy="64" + inkscape:window-x="634" + inkscape:window-y="0" + inkscape:current-layer="svg2" /> + <defs + id="defs4"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 64 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="128 : 64 : 1" + inkscape:persp3d-origin="64 : 42.666667 : 1" + id="perspective49" /> + <linearGradient + id="linearGradient39133"> + <stop + id="stop39135" + style="stop-color:#252525;stop-opacity:1" + offset="0" /> + <stop + id="stop39137" + style="stop-color:#515151;stop-opacity:1" + offset="0" /> + <stop + id="stop39139" + style="stop-color:#878787;stop-opacity:1" + offset="0.28677997" /> + <stop + id="stop39141" + style="stop-color:#000000;stop-opacity:1" + offset="0.92151743" /> + <stop + id="stop39143" + style="stop-color:#ffffff;stop-opacity:0.73786408" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39119"> + <stop + id="stop39121" + style="stop-color:#ffffff;stop-opacity:0.82905984" + offset="0" /> + <stop + id="stop39123" + style="stop-color:#ffffff;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39106"> + <stop + id="stop39108" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop39110" + style="stop-color:#a8a8a8;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39094"> + <stop + id="stop39096" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop39098" + style="stop-color:#333333;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39062"> + <stop + id="stop39064" + style="stop-color:#252525;stop-opacity:1" + offset="0" /> + <stop + id="stop39086" + style="stop-color:#515151;stop-opacity:1" + offset="0.21101321" /> + <stop + id="stop39088" + style="stop-color:#878787;stop-opacity:1" + offset="0.75" /> + <stop + id="stop39090" + style="stop-color:#6c6c6c;stop-opacity:1" + offset="0.875" /> + <stop + id="stop39066" + style="stop-color:#1e1e1e;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="4" + y1="40" + x2="124" + y2="60" + id="linearGradient39068" + xlink:href="#linearGradient39062" + gradientUnits="userSpaceOnUse" /> + <radialGradient + cx="100.70589" + cy="96" + r="60" + fx="158.07428" + fy="95.718063" + id="radialGradient39100" + xlink:href="#linearGradient39094" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.7837903e-8,-1,0.99999999,-2.1864248e-6,-32.000004,164.7061)" /> + <radialGradient + cx="100.44444" + cy="34.363636" + r="32" + fx="83.18" + fy="34.228985" + id="radialGradient39104" + xlink:href="#linearGradient39106" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.1472435e-6,1.0227273,-0.87499999,-9.5061964e-8,94.067865,-4.7272712)" /> + <radialGradient + cx="75.999977" + cy="-2.7730541" + r="48" + fx="55.266491" + fy="-2.5338216" + id="radialGradient39125" + xlink:href="#linearGradient39119" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0,0.83333324,-1.6666667,2.518705e-6,59.378243,-35.333302)" /> + <radialGradient + cx="64.066589" + cy="63.713329" + r="60" + fx="64.066589" + fy="63.713329" + id="radialGradient39131" + xlink:href="#linearGradient39133" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1333333,5.1768857e-8,5.2556881e-6,1.1666667,-8.6091298,-10.332226)" /> + <filter + id="filter39153"> + <feGaussianBlur + id="feGaussianBlur39155" + stdDeviation="2.28" + inkscape:collect="always" /> + </filter> + <filter + id="filter39159"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.68" + id="feGaussianBlur39161" /> + </filter> + </defs> + <g + id="layer1" + style="display:inline"> + <path + d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z" + id="path39151" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39153)" /> + <path + d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z" + id="path39157" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39159)" /> + <rect + width="120" + height="120" + ry="25.00531" + x="4" + y="0" + id="rect2573" + style="opacity:1;fill:url(#radialGradient39100);fill-opacity:1;stroke:none" /> + <path + d="M 29,0 C 15.147058,0 4,11.14706 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.14706 112.85294,0 99,0 L 29,0 z" + id="path39127" + style="opacity:0.20512821;fill:url(#radialGradient39131);fill-opacity:1;stroke:none" /> + <path + d="m 44,68 40,0 12,40 c -20,7.27273 -44,7.27273 -64,0 L 44,68 z" + id="path39102" + style="opacity:0.53418801;fill:url(#radialGradient39104);fill-opacity:1;stroke:none" /> + <path + d="M 25.339207,12 C 52,8 76,8 102.66079,12 107.83471,12 112,16.165286 112,21.339207 L 116,52 C 100,73.339207 28,73.339207 12,52 L 16,21.339207 C 16,16.165286 20.165286,12 25.339207,12 z" + id="rect39116" + style="opacity:0.92307691;fill:url(#radialGradient39125);fill-opacity:1;stroke:none" /> + <path + d="M 29,8 C 15.147058,8 4,19.14706 4,33 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,19.14706 112.85294,8 99,8 L 29,8 z" + id="path39147" + style="opacity:0.20512821;fill:#000000;fill-opacity:1;stroke:none" /> + <path + d="M 29,0 C 15.147058,0 4,11.147058 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.147058 112.85294,0 99,0 L 29,0 z m 0,4 70,0 c 11.70613,0 21,9.293869 21,21 l 0,70 c 0,11.70613 -9.29387,21 -21,21 l -70,0 C 17.293869,116 8,106.70613 8,95 L 8,25 C 8,13.293869 17.293869,4 29,4 z" + id="rect39029" + style="opacity:1;fill:url(#linearGradient39068);fill-opacity:1;stroke:none" /> + <path + d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782" + transform="matrix(-0.16680323,0.53082142,-0.53082142,-0.16680323,103.31027,53.117897)" + id="path3351" + style="opacity:1;fill:none;stroke:#ffffff;stroke-width:21.56673813;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + <path + d="m 36,56 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z" + transform="matrix(1.4851301,0,0,1.4851301,16.475837,-23.948973)" + id="path3353" + style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none" /> + <path + d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782" + transform="matrix(-0.35033273,1.1148712,-1.1148712,-0.35033273,146.5624,46.88078)" + id="path2622" + style="opacity:1;fill:none;stroke:#ffffff;stroke-width:10.26852894;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons_png.qrc b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc new file mode 100644 index 0000000..9a30b7f --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/icons" > + <file alias="wpa_gui.png">icons/hicolor/16x16/apps/wpa_gui.png</file> + <file alias="ap.png">icons/hicolor/32x32/apps/ap.png</file> + <file alias="laptop.png">icons/hicolor/32x32/apps/laptop.png</file> + <file alias="group.png">icons/hicolor/32x32/apps/group.png</file> + <file alias="invitation.png">icons/hicolor/32x32/apps/invitation.png</file> + </qresource> +</RCC> diff --git a/wpa_supplicant/wpa_gui-qt4/lang/.gitignore b/wpa_supplicant/wpa_gui-qt4/lang/.gitignore new file mode 100644 index 0000000..8df47d5 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/lang/.gitignore @@ -0,0 +1 @@ +*.qm diff --git a/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts b/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts new file mode 100644 index 0000000..d7a9c89 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts @@ -0,0 +1,1262 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.0" language="de_DE" sourcelanguage="en_US"> +<context> + <name>AddInterface</name> + <message> + <location filename="../addinterface.cpp" line="38"/> + <source>Select network interface to add</source> + <translation>Wähle die Netzwerkschnittstelle zum hinzufügen aus</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="47"/> + <source>driver</source> + <translation>Treiber</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="48"/> + <source>interface</source> + <translation>Schnittstelle</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="49"/> + <source>description</source> + <translation>Beschreibung</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="221"/> + <source>Add interface command could not be completed.</source> + <translation>Das Schnittstellen hinzufügen Kommando konnte nicht abgeschlossen werden.</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="229"/> + <source>Failed to add the interface.</source> + <translation>Fehler beim hinzufügen der Schnittstelle.</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="238"/> + <source>Failed to add the interface into registry.</source> + <translation>Fehler beim hinzufügen der Schnittstelle in die Registry.</translation> + </message> +</context> +<context> + <name>ErrorMsg</name> + <message> + <location filename="../wpagui.cpp" line="1621"/> + <source>wpa_gui error</source> + <translation>wpa_gui Fehler</translation> + </message> +</context> +<context> + <name>EventHistory</name> + <message> + <location filename="../eventhistory.ui" line="13"/> + <source>Event history</source> + <translation>Ereignis Historie</translation> + </message> + <message> + <location filename="../eventhistory.ui" line="48"/> + <source>Close</source> + <translation>Schließen</translation> + </message> +</context> +<context> + <name>EventListModel</name> + <message> + <location filename="../eventhistory.cpp" line="62"/> + <source>Timestamp</source> + <translation>Zeit</translation> + </message> + <message> + <location filename="../eventhistory.cpp" line="64"/> + <source>Message</source> + <translation>Meldung</translation> + </message> +</context> +<context> + <name>NetworkConfig</name> + <message> + <location filename="../networkconfig.ui" line="13"/> + <source>NetworkConfig</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="19"/> + <source>Cancel</source> + <translation>Abbrechen</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="35"/> + <source>SSID</source> + <translation>SSID</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="42"/> + <source>Network name (Service Set IDentifier)</source> + <translation>Netzwerkname (Service Set IDentifier)</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="52"/> + <source>Authentication</source> + <translation>Authentifizierung</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="60"/> + <source>Plaintext (open / no authentication)</source> + <translation>Plaintext (offen / keine Authentifizierung)</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="65"/> + <source>Static WEP (no authentication)</source> + <translation>Static WEP (keine Authentifizierung)</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="70"/> + <source>Static WEP (Shared Key authentication)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="75"/> + <source>IEEE 802.1X</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="80"/> + <source>WPA-Personal (PSK)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="85"/> + <source>WPA-Enterprise (EAP)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="90"/> + <source>WPA2-Personal (PSK)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="95"/> + <source>WPA2-Enterprise (EAP)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="103"/> + <source>Encryption</source> + <translation>Verschlüsselung</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="111"/> + <source>None</source> + <translation>Keine</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="116"/> + <source>WEP</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="121"/> + <source>TKIP</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="126"/> + <source>CCMP</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="134"/> + <source>PSK</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="144"/> + <source>WPA/WPA2 pre-shared key or passphrase</source> + <translation>WPA/WPA2 Pre-Shared Key oder Passphrase</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="157"/> + <source>EAP method</source> + <translation>EAP Verfahren</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="171"/> + <source>Identity</source> + <translation>Identität</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="181"/> + <source>Username/Identity for EAP methods</source> + <translation>Nutzername/Identitär für die EAP Verfahren</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="188"/> + <source>Password</source> + <translation>Passwort</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="198"/> + <source>Password for EAP methods</source> + <translation>Passwort für die EAP Verfahren</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="208"/> + <source>CA certificate</source> + <translation>CA Zertifikat</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="225"/> + <source>WEP keys</source> + <translation>WEP Schlüssel</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="234"/> + <source>key 0</source> + <translation>Schlüssel 0</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="244"/> + <source>key 1</source> + <translation>Schlüssel 1</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="254"/> + <source>key 3</source> + <translation>Schlüssel 3</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="264"/> + <source>key 2</source> + <translation>Schlüssel 2</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="305"/> + <source>Optional Settings</source> + <translation>Optionale Einstellungen</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="311"/> + <source>Network Identification String</source> + <translation>Netzwerk Indentifikations Zeichenfolge</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="318"/> + <source>Network Priority</source> + <translation>Netzwerk Priorität</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="331"/> + <source>IDString</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="338"/> + <source>Priority</source> + <translation>Priorität</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="345"/> + <source>Inner auth</source> + <translation>Geheime Auth</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="365"/> + <source>Add</source> + <translation>Hinzufügen</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="375"/> + <source>Remove</source> + <translation>Entfernen</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="398"/> + <source>WPS</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="202"/> + <source>WPA Pre-Shared Key Error</source> + <translation>WPA Pre Shared Key Fehler</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="203"/> + <source>WPA-PSK requires a passphrase of 8 to 63 characters +or 64 hex digit PSK</source> + <translation>WPA PSK benötigt ein Passphrase mit 8 bis 63 Zeichen +oder 64 hexadezimal stelligen PSK</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="215"/> + <source>Network ID Error</source> + <translation>Netzwerk ID Fehler</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="216"/> + <source>Network ID String contains non-word characters. +It must be a simple string, without spaces, containing +only characters in this range: [A-Za-z0-9_-] +</source> + <translation>Netzwerk ID Zeichnfolge beinhaltet ungültige Zeichen. +Es muss eine einfache Zeichnfolge aus [A-Za-z0-9_] ohne +Leerzeichen sein +</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="237"/> + <source>Failed to add network to wpa_supplicant +configuration.</source> + <translation>Hinzufügen des Netzwerks in die wpa_supplicant +Konfiguration fehlgeschlagen.</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="414"/> + <source>Failed to enable network in wpa_supplicant +configuration.</source> + <translation>Aktivieren des Netzwerks in der wpa_supplicant +Konfiguration fehlgeschlagen.</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="802"/> + <source>This will permanently remove the network +from the configuration. Do you really want +to remove this network?</source> + <translation>Dies wird das Netzwerk permanent aus +der Konfiguration entfernen. Möchtest du +das Netzwerk wirklich entfernen?</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="805"/> + <source>Yes</source> + <translation>Ja</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="805"/> + <source>No</source> + <translation>Nein</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="813"/> + <source>Failed to remove network from wpa_supplicant +configuration.</source> + <translation>Entfernen des Netzwerks aus der wpa_supplicant +Konfiguration fehlgeschlagen.</translation> + </message> +</context> +<context> + <name>Peers</name> + <message> + <location filename="../peers.ui" line="14"/> + <source>Peers</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="107"/> + <source>Associated station</source> + <translation>Verbundene Stationen</translation> + </message> + <message> + <location filename="../peers.cpp" line="110"/> + <source>AP</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="113"/> + <source>WPS AP</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="116"/> + <source>WPS PIN needed</source> + <translation>WPS PIN wird benötigt</translation> + </message> + <message> + <location filename="../peers.cpp" line="119"/> + <source>ER: WPS AP</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="122"/> + <source>ER: WPS AP (Unconfigured)</source> + <translation>ER: WPS AP (nicht konfiguriert)</translation> + </message> + <message> + <location filename="../peers.cpp" line="125"/> + <source>ER: WPS Enrollee</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="128"/> + <source>WPS Enrollee</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="159"/> + <source>Enter WPS PIN</source> + <translation>WPS PIN Eingabe</translation> + </message> + <message> + <location filename="../peers.cpp" line="164"/> + <source>Connect (PBC)</source> + <translation>Verbinden (PBC)</translation> + </message> + <message> + <location filename="../peers.cpp" line="172"/> + <source>Enroll (PBC)</source> + <translation>Anmelden (PBC)</translation> + </message> + <message> + <location filename="../peers.cpp" line="177"/> + <source>Learn Configuration</source> + <translation>Konfiguration lernen</translation> + </message> + <message> + <location filename="../peers.cpp" line="181"/> + <source>Properties</source> + <translation>Eigenschaften</translation> + </message> + <message> + <location filename="../peers.cpp" line="184"/> + <source>Refresh</source> + <translation>Aktualisieren</translation> + </message> + <message> + <location filename="../peers.cpp" line="205"/> + <source>PIN:</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="206"/> + <source>PIN for </source> + <translation>Pin für </translation> + </message> + <message> + <location filename="../peers.cpp" line="227"/> + <source>Failed to set the WPS PIN.</source> + <translation>Setzten des WPS PIN fehlgeschlagen.</translation> + </message> + <message> + <location filename="../peers.cpp" line="815"/> + <source>Peer Properties</source> + <translation>Peer Eigenschaften</translation> + </message> + <message> + <location filename="../peers.cpp" line="820"/> + <source>Name: </source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="827"/> + <source>Address: </source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="831"/> + <source>UUID: </source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="835"/> + <source>Primary Device Type: </source> + <translation>Primärer Geräte Typ: </translation> + </message> + <message> + <location filename="../peers.cpp" line="840"/> + <source>SSID: </source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="845"/> + <source>Configuration Methods: </source> + <translation>Konfigurationsverfahren: </translation> + </message> + <message> + <location filename="../peers.cpp" line="847"/> + <source>[USBA]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="849"/> + <source>[Ethernet]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="851"/> + <source>[Label]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="853"/> + <source>[Display]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="855"/> + <source>[Ext. NFC Token]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="857"/> + <source>[Int. NFC Token]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="859"/> + <source>[NFC Interface]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="861"/> + <source>[Push Button]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="863"/> + <source>[Keypad]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="869"/> + <source>Device Password ID: </source> + <translation>Geräte Passwort ID: </translation> + </message> + <message> + <location filename="../peers.cpp" line="872"/> + <source> (Default PIN)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="875"/> + <source> (User-specified PIN)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="878"/> + <source> (Machine-specified PIN)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="881"/> + <source> (Rekey)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="884"/> + <source> (Push Button)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="887"/> + <source> (Registrar-specified)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="924"/> + <source>Failed to start WPS PBC.</source> + <translation>Starten von WPS PBC fehlgeschlagen.</translation> + </message> + <message> + <location filename="../peers.cpp" line="937"/> + <source>AP PIN:</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="938"/> + <source>AP PIN for </source> + <translation>AP PIN für </translation> + </message> + <message> + <location filename="../peers.cpp" line="953"/> + <source>Failed to start learning AP configuration.</source> + <translation>Fehler beim erkennen der AP Konfiguration.</translation> + </message> +</context> +<context> + <name>ScanResults</name> + <message> + <location filename="../scanresults.ui" line="13"/> + <source>Scan results</source> + <translation>Scan Ergebnisse</translation> + </message> + <message> + <location filename="../scanresults.ui" line="32"/> + <source>SSID</source> + <translation></translation> + </message> + <message> + <location filename="../scanresults.ui" line="37"/> + <source>BSSID</source> + <translation></translation> + </message> + <message> + <location filename="../scanresults.ui" line="42"/> + <source>frequency</source> + <translation>Frequenz</translation> + </message> + <message> + <location filename="../scanresults.ui" line="47"/> + <source>signal</source> + <translation>Signal</translation> + </message> + <message> + <location filename="../scanresults.ui" line="52"/> + <source>flags</source> + <translation>Flags</translation> + </message> + <message> + <location filename="../scanresults.ui" line="75"/> + <source>Scan</source> + <translation>Scannen</translation> + </message> + <message> + <location filename="../scanresults.ui" line="82"/> + <source>Close</source> + <translation>Schließen</translation> + </message> +</context> +<context> + <name>UserDataRequest</name> + <message> + <location filename="../userdatarequest.ui" line="16"/> + <source>Authentication credentials required</source> + <translation>Authentifzierungs Beglaubigung nötig</translation> + </message> + <message> + <location filename="../userdatarequest.ui" line="77"/> + <source>&OK</source> + <translation></translation> + </message> + <message> + <location filename="../userdatarequest.ui" line="93"/> + <source>&Cancel</source> + <translation></translation> + </message> + <message> + <location filename="../userdatarequest.cpp" line="67"/> + <source>Password: </source> + <translation>Passwort: </translation> + </message> + <message> + <location filename="../userdatarequest.cpp" line="70"/> + <source>New password: </source> + <translation>Neues Passwort: </translation> + </message> + <message> + <location filename="../userdatarequest.cpp" line="73"/> + <source>Identity: </source> + <translation>Identität: </translation> + </message> + <message> + <location filename="../userdatarequest.cpp" line="75"/> + <source>Private key passphrase: </source> + <translation>Privater Key Passphrase: </translation> + </message> +</context> +<context> + <name>WpaGui</name> + <message> + <location filename="../wpagui.ui" line="13"/> + <source>wpa_gui</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="24"/> + <source>Adapter:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="34"/> + <source>Network:</source> + <translation>Netzwerk:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="48"/> + <source>Current Status</source> + <translation>Aktueller Status</translation> + </message> + <message> + <location filename="../wpagui.ui" line="63"/> + <location filename="../wpagui.ui" line="300"/> + <source>Status:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="70"/> + <source>Last message:</source> + <translation>Letzte Meldung:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="77"/> + <source>Authentication:</source> + <translation>Authentifizierung:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="84"/> + <source>Encryption:</source> + <translation>Verschlüsselung:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="91"/> + <source>SSID:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="98"/> + <source>BSSID:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="105"/> + <source>IP address:</source> + <translation>IP Adresse:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="177"/> + <source>Connect</source> + <translation>Verbinden</translation> + </message> + <message> + <location filename="../wpagui.ui" line="184"/> + <source>Disconnect</source> + <translation>Trennen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="191"/> + <location filename="../wpagui.ui" line="286"/> + <source>Scan</source> + <translation>Scannen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="212"/> + <source>Manage Networks</source> + <translation>Netzwerke verwalten</translation> + </message> + <message> + <location filename="../wpagui.ui" line="238"/> + <source>Enabled</source> + <translation>Aktiviert</translation> + </message> + <message> + <location filename="../wpagui.ui" line="245"/> + <source>Edit</source> + <translation>Bearbeiten</translation> + </message> + <message> + <location filename="../wpagui.ui" line="252"/> + <source>Remove</source> + <translation>Entfernen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="272"/> + <source>Disabled</source> + <translation>Deaktiviert</translation> + </message> + <message> + <location filename="../wpagui.ui" line="279"/> + <source>Add</source> + <translation>Hinzufügen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="294"/> + <source>WPS</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="314"/> + <source>PBC - push button</source> + <translation>PBC - Taste</translation> + </message> + <message> + <location filename="../wpagui.ui" line="321"/> + <source>Generate PIN</source> + <translation>PIN erzeugen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="328"/> + <source>PIN:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="348"/> + <source>Use AP PIN</source> + <translation>AP PIN verwenden</translation> + </message> + <message> + <location filename="../wpagui.ui" line="355"/> + <source>AP PIN:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="390"/> + <source>&File</source> + <translation>&Datei</translation> + </message> + <message> + <location filename="../wpagui.ui" line="401"/> + <source>&Network</source> + <translation>&Netzwerk</translation> + </message> + <message> + <location filename="../wpagui.ui" line="413"/> + <source>&Help</source> + <translation>&Hilfe</translation> + </message> + <message> + <location filename="../wpagui.ui" line="426"/> + <source>Event &History</source> + <translation>Ereignis &Historie</translation> + </message> + <message> + <location filename="../wpagui.ui" line="431"/> + <source>&Save Configuration</source> + <translation>Konfiguration &Speichern</translation> + </message> + <message> + <location filename="../wpagui.ui" line="434"/> + <source>Ctrl+S</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="439"/> + <source>E&xit</source> + <translation>&Beenden</translation> + </message> + <message> + <location filename="../wpagui.ui" line="442"/> + <source>Ctrl+Q</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="447"/> + <source>&Add</source> + <translation>&Hinzufügen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="452"/> + <source>&Edit</source> + <translation>&Bearbeiten</translation> + </message> + <message> + <location filename="../wpagui.ui" line="457"/> + <source>&Remove</source> + <translation>&Entfernen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="462"/> + <source>E&nable All</source> + <translation>Alle &aktivieren</translation> + </message> + <message> + <location filename="../wpagui.ui" line="467"/> + <source>&Disable All</source> + <translation>Alle &deaktivieren</translation> + </message> + <message> + <location filename="../wpagui.ui" line="472"/> + <source>Re&move All</source> + <translation>Alle &entfernen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="480"/> + <source>&Contents...</source> + <translation>&Inhalt...</translation> + </message> + <message> + <location filename="../wpagui.ui" line="488"/> + <source>&Index...</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="493"/> + <source>&About</source> + <translation>&Ãœber</translation> + </message> + <message> + <location filename="../wpagui.ui" line="501"/> + <source>&Wi-Fi Protected Setup</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="506"/> + <source>&Peers</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.cpp" line="53"/> + <source>Stop Service</source> + <translation>Dienst stoppen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="58"/> + <source>Start Service</source> + <translation>Dienst starten</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="67"/> + <source>Add Interface</source> + <translation>Schnittstelle hinzufügen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="167"/> + <source>connecting to wpa_supplicant</source> + <translation>Verbindungsaufbau zu wpa_supplicant</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="343"/> + <source>wpa_supplicant service is not running. +Do you want to start it?</source> + <translation>wpa_supplicant ist nicht gestartet. +Möchtest du ihn starten?</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="466"/> + <source>Disconnected</source> + <translation>Getrennt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="468"/> + <source>Inactive</source> + <translation>Inaktiv</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="470"/> + <source>Scanning</source> + <translation>Scannen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="472"/> + <source>Authenticating</source> + <translation>Authentifizieren</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="474"/> + <source>Associating</source> + <translation>Assoziieren</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="476"/> + <source>Associated</source> + <translation>Assoziiert</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="478"/> + <source>4-Way Handshake</source> + <translation>4-Wege Handshake</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="480"/> + <source>Group Handshake</source> + <translation>Gruppen Handshake</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="482"/> + <source>Completed</source> + <translation>Abgeschlossen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="484"/> + <source>Unknown</source> + <translation>Unbekannt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="497"/> + <source>Could not get status from wpa_supplicant</source> + <translation>Status konnte nicht von wpa_supplicant abgerufen werden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="512"/> + <source>No network interfaces in use. +Would you like to add one?</source> + <translation>Es ist keine Netzwerkschnittstelle in verwendung. +Möchtest du eine hinzufügen?</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="682"/> + <location filename="../wpagui.cpp" line="974"/> + <location filename="../wpagui.cpp" line="1039"/> + <location filename="../wpagui.cpp" line="1117"/> + <source>Select any network</source> + <translation>Wähle beliebiges Netzwerk</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="883"/> + <source>Disconnected from network.</source> + <translation>Getrennt vom Netzwerk.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="886"/> + <source>Connection to network established.</source> + <translation>Verbindung zum Netzwerk wurde aufgebaut.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="890"/> + <location filename="../wpagui.cpp" line="1523"/> + <source>WPS AP in active PBC mode found</source> + <translation>WPS AP im aktiven PBC Modus gefunden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="894"/> + <source>Press the PBC button on the screen to start registration</source> + <translation>Drücke den PBC Knopf auf dem Bildschirm um die Registrierung zu starten</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="897"/> + <source>WPS AP with recently selected registrar</source> + <translation>WPS AP mit kürzlich ausgewähltem Registrator</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="903"/> + <source>WPS AP detected</source> + <translation>WPS AP erkannt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="905"/> + <source>PBC mode overlap detected</source> + <translation>PBC Modus Overlap erkannt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="906"/> + <source>More than one AP is currently in active WPS PBC mode. Wait couple of minutes and try again</source> + <translation>Mehr als ein AP ist momentan im aktiven WPS PBC Modus. Versuch es in ein paar Minuten nochmal</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="911"/> + <source>Network configuration received</source> + <translation>Netzwerk Konfiguration empfangen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="915"/> + <source>Registration started</source> + <translation>Registrierung gestartet</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="917"/> + <source>Registrar does not yet know PIN</source> + <translation>Registrator kennt den PIN noch nicht</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="919"/> + <source>Registration failed</source> + <translation>Registrierung fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="921"/> + <source>Registration succeeded</source> + <translation>Registrierung erfolgreich</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1069"/> + <location filename="../wpagui.cpp" line="1138"/> + <source>No Networks</source> + <translation>Keine Netzwerke</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1070"/> + <source>There are no networks to edit. +</source> + <translation>Keine Netzwerke zum bearbeiten. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1081"/> + <location filename="../wpagui.cpp" line="1151"/> + <source>Select A Network</source> + <translation>Wähle ein Netzwerk</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1082"/> + <source>Select a network from the list to edit it. +</source> + <translation>Wähle ein Netzwerk aus der Liste zum bearbeiten. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1139"/> + <source>There are no networks to remove. +</source> + <translation>Es sind keine Netzwerke zum entfernen vorhanden. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1152"/> + <source>Select a network from the list to remove it. +</source> + <translation>Wähle ein Netzwerk aus der Liste zum entfernen. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1264"/> + <source>Failed to save configuration</source> + <translation>Speichern der Konfiguration fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1265"/> + <source>The configuration could not be saved. + +The update_config=1 configuration option +must be used for configuration saving to +be permitted. +</source> + <translation>Die Konfiguration konnte nicht gespeichert werden. + +Die Einstellung update_config=1 muss gesetzt sein, +damit Konfigurationen gespeichert werden können. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1272"/> + <source>Saved configuration</source> + <translation>Konfiguration gespeichert</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1273"/> + <source>The current configuration was saved. +</source> + <translation>Die aktuelle Konfiguration wurde gespeichert. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1293"/> + <source> - wpa_supplicant user interface</source> + <translation> - wpa_supplicant Benutzerschnittstelle</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1307"/> + <source>&Disconnect</source> + <translation>&Trennen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1308"/> + <source>Re&connect</source> + <translation>&Wiederverbinden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1317"/> + <source>&Event History</source> + <translation>&Ereignis Historie</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1318"/> + <source>Scan &Results</source> + <translation>Scan E&rgebnisse</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1319"/> + <source>S&tatus</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1328"/> + <source>&Show Window</source> + <translation>&Fenster anzeigen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1329"/> + <source>&Hide Window</source> + <translation>&Fenster ausblenden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1330"/> + <source>&Quit</source> + <translation>&Beenden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1462"/> + <source> will keep running in the system tray.</source> + <translation> wird weiterhin in der System Ablage laufen.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1466"/> + <source> systray</source> + <translation>System Ablage</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1467"/> + <source>The program will keep running in the system tray.</source> + <translation>Das Programm wird weiterhin in der System Ablage laufen.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1524"/> + <source>Press the push button on the AP to start the PBC mode.</source> + <translation>Drücke die Taste am AP um den PBC Modus zu starten.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1527"/> + <source>If you have not yet done so, press the push button on the AP to start the PBC mode.</source> + <translation>Wenn Sie es noch nicht getan haben, so drücken Sie die Taste am AP um den PBC Modus zu starten.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1531"/> + <location filename="../wpagui.cpp" line="1551"/> + <source>Waiting for Registrar</source> + <translation>Warte auf Registrator</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1548"/> + <source>Enter the generated PIN into the Registrar (either the internal one in the AP or an external one).</source> + <translation>Geben Sie den generierten PIN in der Registrierungsstelle ein (entweder der interne oder der externe im AP).</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1561"/> + <source>WPS AP selected from scan results</source> + <translation>WPS AP ausgewählt aus Scan Ergebnissen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1562"/> + <source>If you want to use an AP device PIN, e.g., from a label in the device, enter the eight digit AP PIN and click Use AP PIN button.</source> + <translation>Wenn Sie einen AP Geräte PIN verwenden möchten, z.B.: von einem Aufkleber am Gerät, geben Sie denn acht stelligen AP PIN ein und klicken Sie auf den AP PIN Knopf.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1583"/> + <source>Waiting for AP/Enrollee</source> + <translation>Warte auf AP/Bewerber</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1591"/> + <source>Connected to the network</source> + <translation>Verbunden zum Netzwerk</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1592"/> + <source>Stopped</source> + <translation>Gestoppt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1651"/> + <location filename="../wpagui.cpp" line="1679"/> + <source>OpenSCManager failed</source> + <translation>OpenSCManager fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1657"/> + <location filename="../wpagui.cpp" line="1685"/> + <source>OpenService failed</source> + <translation>OpenService fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1663"/> + <source>Failed to start wpa_supplicant service</source> + <translation>Starten des wpa_supplicant Dienstes fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1691"/> + <source>Failed to stop wpa_supplicant service</source> + <translation>Stoppen des wpa_supplicant Dienstes fehlgeschlagen</translation> + </message> + <message> + <source>OpenSCManager failed: %d +</source> + <translation type="obsolete">OpenSCManager fehlgeschlagen: %d +</translation> + </message> + <message> + <source>OpenService failed: %d + +</source> + <translation type="obsolete">OpenService fehlgeschlagen: %d + +</translation> + </message> +</context> +</TS> diff --git a/wpa_supplicant/wpa_gui-qt4/main.cpp b/wpa_supplicant/wpa_gui-qt4/main.cpp new file mode 100644 index 0000000..6170b15 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/main.cpp @@ -0,0 +1,82 @@ +/* + * wpa_gui - Application startup + * Copyright (c) 2005-2006, 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. + */ + +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <QApplication> +#include <QtCore/QLibraryInfo> +#include <QtCore/QTranslator> +#include "wpagui.h" + + +class WpaGuiApp : public QApplication +{ +public: + WpaGuiApp(int &argc, char **argv); + +#ifndef QT_NO_SESSIONMANAGER + virtual void saveState(QSessionManager &manager); +#endif + + WpaGui *w; +}; + +WpaGuiApp::WpaGuiApp(int &argc, char **argv) : QApplication(argc, argv) +{ +} + +#ifndef QT_NO_SESSIONMANAGER +void WpaGuiApp::saveState(QSessionManager &manager) +{ + QApplication::saveState(manager); + w->saveState(); +} +#endif + + +int main(int argc, char *argv[]) +{ + WpaGuiApp app(argc, argv); + QTranslator translator; + QString locale; + QString resourceDir; + int ret; + + locale = QLocale::system().name(); + resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + if (!translator.load("wpa_gui_" + locale, resourceDir)) + translator.load("wpa_gui_" + locale, "lang"); + app.installTranslator(&translator); + + WpaGui w(&app); + +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + /* printf("Could not find a usable WinSock.dll\n"); */ + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + app.w = &w; + + ret = app.exec(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return ret; +} diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp new file mode 100644 index 0000000..fe50a8d --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp @@ -0,0 +1,858 @@ +/* + * wpa_gui - NetworkConfig class + * Copyright (c) 2005-2006, 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. + */ + +#include <cstdio> +#include <QMessageBox> + +#include "networkconfig.h" +#include "wpagui.h" + +enum { + AUTH_NONE_OPEN, + AUTH_NONE_WEP, + AUTH_NONE_WEP_SHARED, + AUTH_IEEE8021X, + AUTH_WPA_PSK, + AUTH_WPA_EAP, + AUTH_WPA2_PSK, + AUTH_WPA2_EAP +}; + +#define WPA_GUI_KEY_DATA "[key is configured]" + + +NetworkConfig::NetworkConfig(QWidget *parent, const char *, bool, Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + encrSelect->setEnabled(false); + connect(authSelect, SIGNAL(activated(int)), this, + SLOT(authChanged(int))); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(close())); + connect(addButton, SIGNAL(clicked()), this, SLOT(addNetwork())); + connect(encrSelect, SIGNAL(activated(const QString &)), this, + SLOT(encrChanged(const QString &))); + connect(removeButton, SIGNAL(clicked()), this, SLOT(removeNetwork())); + connect(eapSelect, SIGNAL(activated(int)), this, + SLOT(eapChanged(int))); + connect(useWpsButton, SIGNAL(clicked()), this, SLOT(useWps())); + + wpagui = NULL; + new_network = false; +} + + +NetworkConfig::~NetworkConfig() +{ +} + + +void NetworkConfig::languageChange() +{ + retranslateUi(this); +} + + +void NetworkConfig::paramsFromScanResults(QTreeWidgetItem *sel) +{ + new_network = true; + + /* SSID BSSID frequency signal flags */ + setWindowTitle(sel->text(0)); + ssidEdit->setText(sel->text(0)); + + QString flags = sel->text(4); + int auth, encr = 0; + if (flags.indexOf("[WPA2-EAP") >= 0) + auth = AUTH_WPA2_EAP; + else if (flags.indexOf("[WPA-EAP") >= 0) + auth = AUTH_WPA_EAP; + else if (flags.indexOf("[WPA2-PSK") >= 0) + auth = AUTH_WPA2_PSK; + else if (flags.indexOf("[WPA-PSK") >= 0) + auth = AUTH_WPA_PSK; + else + auth = AUTH_NONE_OPEN; + + if (flags.indexOf("-CCMP") >= 0) + encr = 1; + else if (flags.indexOf("-TKIP") >= 0) + encr = 0; + else if (flags.indexOf("WEP") >= 0) { + encr = 1; + if (auth == AUTH_NONE_OPEN) + auth = AUTH_NONE_WEP; + } else + encr = 0; + + authSelect->setCurrentIndex(auth); + authChanged(auth); + encrSelect->setCurrentIndex(encr); + + wepEnabled(auth == AUTH_NONE_WEP); + + getEapCapa(); + + if (flags.indexOf("[WPS") >= 0) + useWpsButton->setEnabled(true); + bssid = sel->text(1); +} + + +void NetworkConfig::authChanged(int sel) +{ + encrSelect->setEnabled(sel != AUTH_NONE_OPEN && sel != AUTH_NONE_WEP && + sel != AUTH_NONE_WEP_SHARED); + pskEdit->setEnabled(sel == AUTH_WPA_PSK || sel == AUTH_WPA2_PSK); + bool eap = sel == AUTH_IEEE8021X || sel == AUTH_WPA_EAP || + sel == AUTH_WPA2_EAP; + eapSelect->setEnabled(eap); + identityEdit->setEnabled(eap); + passwordEdit->setEnabled(eap); + cacertEdit->setEnabled(eap); + phase2Select->setEnabled(eap); + if (eap) + eapChanged(eapSelect->currentIndex()); + + while (encrSelect->count()) + encrSelect->removeItem(0); + + if (sel == AUTH_NONE_OPEN || sel == AUTH_NONE_WEP || + sel == AUTH_NONE_WEP_SHARED || sel == AUTH_IEEE8021X) { + encrSelect->addItem("None"); + encrSelect->addItem("WEP"); + encrSelect->setCurrentIndex(sel == AUTH_NONE_OPEN ? 0 : 1); + } else { + encrSelect->addItem("TKIP"); + encrSelect->addItem("CCMP"); + encrSelect->setCurrentIndex((sel == AUTH_WPA2_PSK || + sel == AUTH_WPA2_EAP) ? 1 : 0); + } + + wepEnabled(sel == AUTH_NONE_WEP || sel == AUTH_NONE_WEP_SHARED); +} + + +void NetworkConfig::eapChanged(int sel) +{ + QString prev_val = phase2Select->currentText(); + while (phase2Select->count()) + phase2Select->removeItem(0); + + QStringList inner; + inner << "PEAP" << "TTLS" << "FAST"; + if (!inner.contains(eapSelect->itemText(sel))) + return; + + phase2Select->addItem("[ any ]"); + + /* Add special cases based on outer method */ + if (eapSelect->currentText().compare("TTLS") == 0) { + phase2Select->addItem("PAP"); + phase2Select->addItem("CHAP"); + phase2Select->addItem("MSCHAP"); + phase2Select->addItem("MSCHAPv2"); + } else if (eapSelect->currentText().compare("FAST") == 0) + phase2Select->addItem("GTC(auth) + MSCHAPv2(prov)"); + + /* Add all enabled EAP methods that can be used in the tunnel */ + int i; + QStringList allowed; + allowed << "MSCHAPV2" << "MD5" << "GTC" << "TLS" << "OTP" << "SIM" + << "AKA"; + for (i = 0; i < eapSelect->count(); i++) { + if (allowed.contains(eapSelect->itemText(i))) { + phase2Select->addItem("EAP-" + eapSelect->itemText(i)); + } + } + + for (i = 0; i < phase2Select->count(); i++) { + if (phase2Select->itemText(i).compare(prev_val) == 0) { + phase2Select->setCurrentIndex(i); + break; + } + } +} + + +void NetworkConfig::addNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + int id; + int psklen = pskEdit->text().length(); + int auth = authSelect->currentIndex(); + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) { + if (psklen < 8 || psklen > 64) { + QMessageBox::warning( + this, + tr("WPA Pre-Shared Key Error"), + tr("WPA-PSK requires a passphrase of 8 to 63 " + "characters\n" + "or 64 hex digit PSK")); + pskEdit->setFocus(); + return; + } + } + + if (idstrEdit->isEnabled() && !idstrEdit->text().isEmpty()) { + QRegExp rx("^(\\w|-)+$"); + if (rx.indexIn(idstrEdit->text()) < 0) { + QMessageBox::warning( + this, tr("Network ID Error"), + tr("Network ID String contains non-word " + "characters.\n" + "It must be a simple string, " + "without spaces, containing\n" + "only characters in this range: " + "[A-Za-z0-9_-]\n")); + idstrEdit->setFocus(); + return; + } + } + + if (wpagui == NULL) + return; + + memset(reply, 0, sizeof(reply)); + reply_len = sizeof(reply) - 1; + + if (new_network) { + wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len); + if (reply[0] == 'F') { + QMessageBox::warning(this, "wpa_gui", + tr("Failed to add " + "network to wpa_supplicant\n" + "configuration.")); + return; + } + id = atoi(reply); + } else + id = edit_network_id; + + setNetworkParam(id, "ssid", ssidEdit->text().toAscii().constData(), + true); + + const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL; + switch (auth) { + case AUTH_NONE_OPEN: + case AUTH_NONE_WEP: + case AUTH_NONE_WEP_SHARED: + key_mgmt = "NONE"; + break; + case AUTH_IEEE8021X: + key_mgmt = "IEEE8021X"; + break; + case AUTH_WPA_PSK: + key_mgmt = "WPA-PSK"; + proto = "WPA"; + break; + case AUTH_WPA_EAP: + key_mgmt = "WPA-EAP"; + proto = "WPA"; + break; + case AUTH_WPA2_PSK: + key_mgmt = "WPA-PSK"; + proto = "WPA2"; + break; + case AUTH_WPA2_EAP: + key_mgmt = "WPA-EAP"; + proto = "WPA2"; + break; + } + + if (auth == AUTH_NONE_WEP_SHARED) + setNetworkParam(id, "auth_alg", "SHARED", false); + else + setNetworkParam(id, "auth_alg", "OPEN", false); + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP || + auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) { + int encr = encrSelect->currentIndex(); + if (encr == 0) + pairwise = "TKIP"; + else + pairwise = "CCMP"; + } + + if (proto) + setNetworkParam(id, "proto", proto, false); + if (key_mgmt) + setNetworkParam(id, "key_mgmt", key_mgmt, false); + if (pairwise) { + setNetworkParam(id, "pairwise", pairwise, false); + setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false); + } + if (pskEdit->isEnabled() && + strcmp(pskEdit->text().toAscii().constData(), + WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "psk", + pskEdit->text().toAscii().constData(), + psklen != 64); + if (eapSelect->isEnabled()) { + const char *eap = + eapSelect->currentText().toAscii().constData(); + setNetworkParam(id, "eap", eap, false); + if (strcmp(eap, "SIM") == 0 || strcmp(eap, "AKA") == 0) + setNetworkParam(id, "pcsc", "", true); + else + setNetworkParam(id, "pcsc", "NULL", false); + } + if (phase2Select->isEnabled()) { + QString eap = eapSelect->currentText(); + QString inner = phase2Select->currentText(); + char phase2[32]; + phase2[0] = '\0'; + if (eap.compare("PEAP") == 0) { + if (inner.startsWith("EAP-")) + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.right(inner.size() - 4). + toAscii().constData()); + } else if (eap.compare("TTLS") == 0) { + if (inner.startsWith("EAP-")) + snprintf(phase2, sizeof(phase2), "autheap=%s", + inner.right(inner.size() - 4). + toAscii().constData()); + else + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.toAscii().constData()); + } else if (eap.compare("FAST") == 0) { + const char *provisioning = NULL; + if (inner.startsWith("EAP-")) { + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.right(inner.size() - 4). + toAscii().constData()); + provisioning = "fast_provisioning=2"; + } else if (inner.compare("GTC(auth) + MSCHAPv2(prov)") + == 0) { + snprintf(phase2, sizeof(phase2), + "auth=GTC auth=MSCHAPV2"); + provisioning = "fast_provisioning=1"; + } else + provisioning = "fast_provisioning=3"; + if (provisioning) { + char blob[32]; + setNetworkParam(id, "phase1", provisioning, + true); + snprintf(blob, sizeof(blob), + "blob://fast-pac-%d", id); + setNetworkParam(id, "pac_file", blob, true); + } + } + if (phase2[0]) + setNetworkParam(id, "phase2", phase2, true); + else + setNetworkParam(id, "phase2", "NULL", false); + } else + setNetworkParam(id, "phase2", "NULL", false); + if (identityEdit->isEnabled() && identityEdit->text().length() > 0) + setNetworkParam(id, "identity", + identityEdit->text().toAscii().constData(), + true); + else + setNetworkParam(id, "identity", "NULL", false); + if (passwordEdit->isEnabled() && passwordEdit->text().length() > 0 && + strcmp(passwordEdit->text().toAscii().constData(), + WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "password", + passwordEdit->text().toAscii().constData(), + true); + else if (passwordEdit->text().length() == 0) + setNetworkParam(id, "password", "NULL", false); + if (cacertEdit->isEnabled() && cacertEdit->text().length() > 0) + setNetworkParam(id, "ca_cert", + cacertEdit->text().toAscii().constData(), + true); + else + setNetworkParam(id, "ca_cert", "NULL", false); + writeWepKey(id, wep0Edit, 0); + writeWepKey(id, wep1Edit, 1); + writeWepKey(id, wep2Edit, 2); + writeWepKey(id, wep3Edit, 3); + + if (wep0Radio->isEnabled() && wep0Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "0", false); + else if (wep1Radio->isEnabled() && wep1Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "1", false); + else if (wep2Radio->isEnabled() && wep2Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "2", false); + else if (wep3Radio->isEnabled() && wep3Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "3", false); + + if (idstrEdit->isEnabled() && idstrEdit->text().length() > 0) + setNetworkParam(id, "id_str", + idstrEdit->text().toAscii().constData(), + true); + else + setNetworkParam(id, "id_str", "NULL", false); + + if (prioritySpinBox->isEnabled()) { + QString prio; + prio = prio.setNum(prioritySpinBox->value()); + setNetworkParam(id, "priority", prio.toAscii().constData(), + false); + } + + snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", + tr("Failed to enable " + "network in wpa_supplicant\n" + "configuration.")); + /* Network was added, so continue anyway */ + } + wpagui->triggerUpdate(); + wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); + + close(); +} + + +void NetworkConfig::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; +} + + +int NetworkConfig::setNetworkParam(int id, const char *field, + const char *value, bool quote) +{ + char reply[10], cmd[256]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s", + id, field, quote ? "\"" : "", value, quote ? "\"" : ""); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + return strncmp(reply, "OK", 2) == 0 ? 0 : -1; +} + + +void NetworkConfig::encrChanged(const QString &) +{ +} + + +void NetworkConfig::wepEnabled(bool enabled) +{ + wep0Edit->setEnabled(enabled); + wep1Edit->setEnabled(enabled); + wep2Edit->setEnabled(enabled); + wep3Edit->setEnabled(enabled); + wep0Radio->setEnabled(enabled); + wep1Radio->setEnabled(enabled); + wep2Radio->setEnabled(enabled); + wep3Radio->setEnabled(enabled); +} + + +void NetworkConfig::writeWepKey(int network_id, QLineEdit *edit, int id) +{ + char buf[10]; + bool hex; + const char *txt, *pos; + size_t len; + + if (!edit->isEnabled() || edit->text().isEmpty()) + return; + + /* + * Assume hex key if only hex characters are present and length matches + * with 40, 104, or 128-bit key + */ + txt = edit->text().toAscii().constData(); + if (strcmp(txt, WPA_GUI_KEY_DATA) == 0) + return; + len = strlen(txt); + if (len == 0) + return; + pos = txt; + hex = true; + while (*pos) { + if (!((*pos >= '0' && *pos <= '9') || + (*pos >= 'a' && *pos <= 'f') || + (*pos >= 'A' && *pos <= 'F'))) { + hex = false; + break; + } + pos++; + } + if (hex && len != 10 && len != 26 && len != 32) + hex = false; + snprintf(buf, sizeof(buf), "wep_key%d", id); + setNetworkParam(network_id, buf, txt, !hex); +} + + +static int key_value_isset(const char *reply, size_t reply_len) +{ + return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0); +} + + +void NetworkConfig::paramsFromConfig(int network_id) +{ + int i, res; + + edit_network_id = network_id; + getEapCapa(); + + char reply[1024], cmd[256], *pos; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + ssidEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id); + reply_len = sizeof(reply) - 1; + int wpa = 0; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "RSN") || strstr(reply, "WPA2")) + wpa = 2; + else if (strstr(reply, "WPA")) + wpa = 1; + } + + int auth = AUTH_NONE_OPEN, encr = 0; + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "WPA-EAP")) + auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP; + else if (strstr(reply, "WPA-PSK")) + auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK; + else if (strstr(reply, "IEEE8021X")) { + auth = AUTH_IEEE8021X; + encr = 1; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "CCMP") && auth != AUTH_NONE_OPEN && + auth != AUTH_NONE_WEP && auth != AUTH_NONE_WEP_SHARED) + encr = 1; + else if (strstr(reply, "TKIP")) + encr = 0; + else if (strstr(reply, "WEP")) + encr = 1; + else + encr = 0; + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + pskEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + pskEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + identityEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + passwordEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + passwordEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + cacertEdit->setText(reply + 1); + } + + enum { NO_INNER, PEAP_INNER, TTLS_INNER, FAST_INNER } eap = NO_INNER; + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 1) { + reply[reply_len] = '\0'; + for (i = 0; i < eapSelect->count(); i++) { + if (eapSelect->itemText(i).compare(reply) == 0) { + eapSelect->setCurrentIndex(i); + if (strcmp(reply, "PEAP") == 0) + eap = PEAP_INNER; + else if (strcmp(reply, "TTLS") == 0) + eap = TTLS_INNER; + else if (strcmp(reply, "FAST") == 0) + eap = FAST_INNER; + break; + } + } + } + + if (eap != NO_INNER) { + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d phase2", + network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 1) { + reply[reply_len] = '\0'; + eapChanged(eapSelect->currentIndex()); + } else + eap = NO_INNER; + } + + char *val; + val = reply + 1; + while (*(val + 1)) + val++; + if (*val == '"') + *val = '\0'; + + switch (eap) { + case PEAP_INNER: + if (strncmp(reply, "\"auth=", 6)) + break; + val = reply + 2; + memcpy(val, "EAP-", 4); + break; + case TTLS_INNER: + if (strncmp(reply, "\"autheap=", 9) == 0) { + val = reply + 5; + memcpy(val, "EAP-", 4); + } else if (strncmp(reply, "\"auth=", 6) == 0) + val = reply + 6; + break; + case FAST_INNER: + if (strncmp(reply, "\"auth=", 6)) + break; + if (strcmp(reply + 6, "GTC auth=MSCHAPV2") == 0) { + val = (char *) "GTC(auth) + MSCHAPv2(prov)"; + break; + } + val = reply + 2; + memcpy(val, "EAP-", 4); + break; + case NO_INNER: + break; + } + + for (i = 0; i < phase2Select->count(); i++) { + if (phase2Select->itemText(i).compare(val) == 0) { + phase2Select->setCurrentIndex(i); + break; + } + } + + for (i = 0; i < 4; i++) { + QLineEdit *wepEdit; + switch (i) { + default: + case 0: + wepEdit = wep0Edit; + break; + case 1: + wepEdit = wep1Edit; + break; + case 2: + wepEdit = wep2Edit; + break; + case 3: + wepEdit = wep3Edit; + break; + } + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", + network_id, i); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + if (auth == AUTH_NONE_OPEN || auth == AUTH_IEEE8021X) { + if (auth == AUTH_NONE_OPEN) + auth = AUTH_NONE_WEP; + encr = 1; + } + + wepEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + if (auth == AUTH_NONE_OPEN || auth == AUTH_IEEE8021X) { + if (auth == AUTH_NONE_OPEN) + auth = AUTH_NONE_WEP; + encr = 1; + } + wepEdit->setText(WPA_GUI_KEY_DATA); + } + } + + if (auth == AUTH_NONE_WEP) { + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d auth_alg", + network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strcmp(reply, "SHARED") == 0) + auth = AUTH_NONE_WEP_SHARED; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) + { + reply[reply_len] = '\0'; + switch (atoi(reply)) { + case 0: + wep0Radio->setChecked(true); + break; + case 1: + wep1Radio->setChecked(true); + break; + case 2: + wep2Radio->setChecked(true); + break; + case 3: + wep3Radio->setChecked(true); + break; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + idstrEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d priority", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) + { + reply[reply_len] = '\0'; + prioritySpinBox->setValue(atoi(reply)); + } + + authSelect->setCurrentIndex(auth); + authChanged(auth); + encrSelect->setCurrentIndex(encr); + wepEnabled(auth == AUTH_NONE_WEP || auth == AUTH_NONE_WEP_SHARED); + + removeButton->setEnabled(true); + addButton->setText("Save"); +} + + +void NetworkConfig::removeNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + + if (QMessageBox::information( + this, "wpa_gui", + tr("This will permanently remove the network\n" + "from the configuration. Do you really want\n" + "to remove this network?"), + tr("Yes"), tr("No")) != 0) + return; + + snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", + tr("Failed to remove network from " + "wpa_supplicant\n" + "configuration.")); + } else { + wpagui->triggerUpdate(); + wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); + } + + close(); +} + + +void NetworkConfig::newNetwork() +{ + new_network = true; + getEapCapa(); +} + + +void NetworkConfig::getEapCapa() +{ + char reply[256]; + size_t reply_len; + + if (wpagui == NULL) + return; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("GET_CAPABILITY eap", reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + + QString res(reply); + QStringList types = res.split(QChar(' ')); + eapSelect->insertItems(-1, types); +} + + +void NetworkConfig::useWps() +{ + if (wpagui == NULL) + return; + wpagui->setBssFromScan(bssid); + wpagui->wpsDialog(); + close(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.h b/wpa_supplicant/wpa_gui-qt4/networkconfig.h new file mode 100644 index 0000000..0ceeb41 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.h @@ -0,0 +1,61 @@ +/* + * wpa_gui - NetworkConfig class + * Copyright (c) 2005-2006, 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. + */ + +#ifndef NETWORKCONFIG_H +#define NETWORKCONFIG_H + +#include <QObject> +#include "ui_networkconfig.h" + +class WpaGui; + +class NetworkConfig : public QDialog, public Ui::NetworkConfig +{ + Q_OBJECT + +public: + NetworkConfig(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~NetworkConfig(); + + virtual void paramsFromScanResults(QTreeWidgetItem *sel); + virtual void setWpaGui(WpaGui *_wpagui); + virtual int setNetworkParam(int id, const char *field, + const char *value, bool quote); + virtual void paramsFromConfig(int network_id); + virtual void newNetwork(); + +public slots: + virtual void authChanged(int sel); + virtual void addNetwork(); + virtual void encrChanged(const QString &sel); + virtual void writeWepKey(int network_id, QLineEdit *edit, int id); + virtual void removeNetwork(); + virtual void eapChanged(int sel); + virtual void useWps(); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; + int edit_network_id; + bool new_network; + QString bssid; + + virtual void wepEnabled(bool enabled); + virtual void getEapCapa(); +}; + +#endif /* NETWORKCONFIG_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.ui b/wpa_supplicant/wpa_gui-qt4/networkconfig.ui new file mode 100644 index 0000000..217a8ff --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.ui @@ -0,0 +1,435 @@ +<ui version="4.0" > + <class>NetworkConfig</class> + <widget class="QDialog" name="NetworkConfig" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>410</width> + <height>534</height> + </rect> + </property> + <property name="windowTitle" > + <string>NetworkConfig</string> + </property> + <layout class="QGridLayout" > + <item row="1" column="3" > + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="4" > + <widget class="QFrame" name="frame9" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="ssidLabel" > + <property name="text" > + <string>SSID</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="ssidEdit" > + <property name="toolTip" > + <string>Network name (Service Set IDentifier)</string> + </property> + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="authLabel" > + <property name="text" > + <string>Authentication</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="authSelect" > + <item> + <property name="text" > + <string>Plaintext (open / no authentication)</string> + </property> + </item> + <item> + <property name="text" > + <string>Static WEP (no authentication)</string> + </property> + </item> + <item> + <property name="text" > + <string>Static WEP (Shared Key authentication)</string> + </property> + </item> + <item> + <property name="text" > + <string>IEEE 802.1X</string> + </property> + </item> + <item> + <property name="text" > + <string>WPA-Personal (PSK)</string> + </property> + </item> + <item> + <property name="text" > + <string>WPA-Enterprise (EAP)</string> + </property> + </item> + <item> + <property name="text" > + <string>WPA2-Personal (PSK)</string> + </property> + </item> + <item> + <property name="text" > + <string>WPA2-Enterprise (EAP)</string> + </property> + </item> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="encrLabel" > + <property name="text" > + <string>Encryption</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QComboBox" name="encrSelect" > + <item> + <property name="text" > + <string>None</string> + </property> + </item> + <item> + <property name="text" > + <string>WEP</string> + </property> + </item> + <item> + <property name="text" > + <string>TKIP</string> + </property> + </item> + <item> + <property name="text" > + <string>CCMP</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="pskLabel" > + <property name="text" > + <string>PSK</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="pskEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>WPA/WPA2 pre-shared key or passphrase</string> + </property> + <property name="whatsThis" > + <string/> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="eapLabel" > + <property name="text" > + <string>EAP method</string> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QComboBox" name="eapSelect" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="identityLabel" > + <property name="text" > + <string>Identity</string> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLineEdit" name="identityEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>Username/Identity for EAP methods</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QLabel" name="passwordLabel" > + <property name="text" > + <string>Password</string> + </property> + </widget> + </item> + <item row="6" column="1" > + <widget class="QLineEdit" name="passwordEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>Password for EAP methods</string> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="7" column="0" > + <widget class="QLabel" name="cacertLabel" > + <property name="text" > + <string>CA certificate</string> + </property> + </widget> + </item> + <item row="7" column="1" > + <widget class="QLineEdit" name="cacertEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="8" column="0" colspan="2" > + <widget class="QGroupBox" name="wepBox" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="title" > + <string>WEP keys</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QRadioButton" name="wep0Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 0</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QRadioButton" name="wep1Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 1</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QRadioButton" name="wep3Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 3</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QRadioButton" name="wep2Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 2</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="wep0Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="wep1Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLineEdit" name="wep2Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="wep3Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="9" column="0" colspan="2" > + <widget class="QGroupBox" name="optionalSettingsBox" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="title" > + <string>Optional Settings</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="1" > + <widget class="QLineEdit" name="idstrEdit" > + <property name="toolTip" > + <string>Network Identification String</string> + </property> + </widget> + </item> + <item row="0" column="3" > + <widget class="QSpinBox" name="prioritySpinBox" > + <property name="toolTip" > + <string>Network Priority</string> + </property> + <property name="maximum" > + <number>10000</number> + </property> + <property name="singleStep" > + <number>10</number> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="idstrLabel" > + <property name="text" > + <string>IDString</string> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QLabel" name="priorityLabel" > + <property name="text" > + <string>Priority</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="phase2Label" > + <property name="text" > + <string>Inner auth</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="phase2Select" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="addButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="removeButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="useWpsButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>WPS</string> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <tabstops> + <tabstop>ssidEdit</tabstop> + <tabstop>authSelect</tabstop> + <tabstop>encrSelect</tabstop> + <tabstop>pskEdit</tabstop> + <tabstop>eapSelect</tabstop> + <tabstop>identityEdit</tabstop> + <tabstop>passwordEdit</tabstop> + <tabstop>cacertEdit</tabstop> + <tabstop>wep0Radio</tabstop> + <tabstop>wep0Edit</tabstop> + <tabstop>wep1Radio</tabstop> + <tabstop>wep1Edit</tabstop> + <tabstop>wep2Radio</tabstop> + <tabstop>wep2Edit</tabstop> + <tabstop>wep3Radio</tabstop> + <tabstop>wep3Edit</tabstop> + <tabstop>idstrEdit</tabstop> + <tabstop>prioritySpinBox</tabstop> + <tabstop>phase2Select</tabstop> + <tabstop>addButton</tabstop> + <tabstop>removeButton</tabstop> + <tabstop>cancelButton</tabstop> + </tabstops> + <includes> + <include location="global" >qtreewidget.h</include> + </includes> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/peers.cpp b/wpa_supplicant/wpa_gui-qt4/peers.cpp new file mode 100644 index 0000000..65bb17d --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/peers.cpp @@ -0,0 +1,1889 @@ +/* + * wpa_gui - Peers class + * Copyright (c) 2009-2010, Atheros Communications + * + * 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. + */ + +#include <cstdio> +#include <QImageReader> +#include <QMessageBox> + +#include "common/wpa_ctrl.h" +#include "wpagui.h" +#include "stringquery.h" +#include "peers.h" + + +enum { + peer_role_address = Qt::UserRole + 1, + peer_role_type, + peer_role_uuid, + peer_role_details, + peer_role_ifname, + peer_role_pri_dev_type, + peer_role_ssid, + peer_role_config_methods, + peer_role_dev_passwd_id, + peer_role_bss_id, + peer_role_selected_method, + peer_role_selected_pin, + peer_role_requested_method, + peer_role_network_id +}; + +enum selected_method { + SEL_METHOD_NONE, + SEL_METHOD_PIN_PEER_DISPLAY, + SEL_METHOD_PIN_LOCAL_DISPLAY +}; + +/* + * TODO: + * - add current AP info (e.g., from WPS) in station mode + */ + +enum peer_type { + PEER_TYPE_ASSOCIATED_STATION, + PEER_TYPE_AP, + PEER_TYPE_AP_WPS, + PEER_TYPE_WPS_PIN_NEEDED, + PEER_TYPE_P2P, + PEER_TYPE_P2P_CLIENT, + PEER_TYPE_P2P_GROUP, + PEER_TYPE_P2P_PERSISTENT_GROUP_GO, + PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT, + PEER_TYPE_P2P_INVITATION, + PEER_TYPE_WPS_ER_AP, + PEER_TYPE_WPS_ER_AP_UNCONFIGURED, + PEER_TYPE_WPS_ER_ENROLLEE, + PEER_TYPE_WPS_ENROLLEE +}; + + +Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + if (QImageReader::supportedImageFormats().contains(QByteArray("svg"))) + { + default_icon = new QIcon(":/icons/wpa_gui.svg"); + ap_icon = new QIcon(":/icons/ap.svg"); + laptop_icon = new QIcon(":/icons/laptop.svg"); + group_icon = new QIcon(":/icons/group.svg"); + invitation_icon = new QIcon(":/icons/invitation.svg"); + } else { + default_icon = new QIcon(":/icons/wpa_gui.png"); + ap_icon = new QIcon(":/icons/ap.png"); + laptop_icon = new QIcon(":/icons/laptop.png"); + group_icon = new QIcon(":/icons/group.png"); + invitation_icon = new QIcon(":/icons/invitation.png"); + } + + peers->setModel(&model); + peers->setResizeMode(QListView::Adjust); + peers->setDragEnabled(false); + peers->setSelectionMode(QAbstractItemView::NoSelection); + + peers->setContextMenuPolicy(Qt::CustomContextMenu); + connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(context_menu(const QPoint &))); + + wpagui = NULL; + hide_ap = false; +} + + +void Peers::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; + update_peers(); +} + + +Peers::~Peers() +{ + delete default_icon; + delete ap_icon; + delete laptop_icon; + delete group_icon; + delete invitation_icon; +} + + +void Peers::languageChange() +{ + retranslateUi(this); +} + + +QString Peers::ItemType(int type) +{ + QString title; + switch (type) { + case PEER_TYPE_ASSOCIATED_STATION: + title = tr("Associated station"); + break; + case PEER_TYPE_AP: + title = tr("AP"); + break; + case PEER_TYPE_AP_WPS: + title = tr("WPS AP"); + break; + case PEER_TYPE_WPS_PIN_NEEDED: + title = tr("WPS PIN needed"); + break; + case PEER_TYPE_P2P: + title = tr("P2P Device"); + break; + case PEER_TYPE_P2P_CLIENT: + title = tr("P2P Device (group client)"); + break; + case PEER_TYPE_P2P_GROUP: + title = tr("P2P Group"); + break; + case PEER_TYPE_P2P_PERSISTENT_GROUP_GO: + title = tr("P2P Persistent Group (GO)"); + break; + case PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT: + title = tr("P2P Persistent Group (client)"); + break; + case PEER_TYPE_P2P_INVITATION: + title = tr("P2P Invitation"); + break; + case PEER_TYPE_WPS_ER_AP: + title = tr("ER: WPS AP"); + break; + case PEER_TYPE_WPS_ER_AP_UNCONFIGURED: + title = tr("ER: WPS AP (Unconfigured)"); + break; + case PEER_TYPE_WPS_ER_ENROLLEE: + title = tr("ER: WPS Enrollee"); + break; + case PEER_TYPE_WPS_ENROLLEE: + title = tr("WPS Enrollee"); + break; + } + return title; +} + + +void Peers::context_menu(const QPoint &pos) +{ + QMenu *menu = new QMenu; + if (menu == NULL) + return; + + QModelIndex idx = peers->indexAt(pos); + if (idx.isValid()) { + ctx_item = model.itemFromIndex(idx); + int type = ctx_item->data(peer_role_type).toInt(); + menu->addAction(Peers::ItemType(type))->setEnabled(false); + menu->addSeparator(); + + int config_methods = -1; + QVariant var = ctx_item->data(peer_role_config_methods); + if (var.isValid()) + config_methods = var.toInt(); + + enum selected_method method = SEL_METHOD_NONE; + var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) + method = (enum selected_method) var.toInt(); + + if ((type == PEER_TYPE_ASSOCIATED_STATION || + type == PEER_TYPE_AP_WPS || + type == PEER_TYPE_WPS_PIN_NEEDED || + type == PEER_TYPE_WPS_ER_ENROLLEE || + type == PEER_TYPE_WPS_ENROLLEE) && + (config_methods == -1 || (config_methods & 0x010c))) { + menu->addAction(tr("Enter WPS PIN"), this, + SLOT(enter_pin())); + } + + if (type == PEER_TYPE_P2P || type == PEER_TYPE_P2P_CLIENT) { + menu->addAction(tr("P2P Connect"), this, + SLOT(ctx_p2p_connect())); + if (method == SEL_METHOD_NONE && + config_methods > -1 && + config_methods & 0x0080 /* PBC */ && + config_methods != 0x0080) + menu->addAction(tr("P2P Connect (PBC)"), this, + SLOT(connect_pbc())); + if (method == SEL_METHOD_NONE) { + menu->addAction(tr("P2P Request PIN"), this, + SLOT(ctx_p2p_req_pin())); + menu->addAction(tr("P2P Show PIN"), this, + SLOT(ctx_p2p_show_pin())); + } + + if (config_methods > -1 && (config_methods & 0x0100)) { + /* Peer has Keypad */ + menu->addAction(tr("P2P Display PIN"), this, + SLOT(ctx_p2p_display_pin())); + } + + if (config_methods > -1 && (config_methods & 0x000c)) { + /* Peer has Label or Display */ + menu->addAction(tr("P2P Enter PIN"), this, + SLOT(ctx_p2p_enter_pin())); + } + } + + if (type == PEER_TYPE_P2P_GROUP) { + menu->addAction(tr("Show passphrase"), this, + SLOT(ctx_p2p_show_passphrase())); + menu->addAction(tr("Remove P2P Group"), this, + SLOT(ctx_p2p_remove_group())); + } + + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT || + type == PEER_TYPE_P2P_INVITATION) { + menu->addAction(tr("Start group"), this, + SLOT(ctx_p2p_start_persistent())); + } + + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) { + menu->addAction(tr("Invite"), this, + SLOT(ctx_p2p_invite())); + } + + if (type == PEER_TYPE_P2P_INVITATION) { + menu->addAction(tr("Ignore"), this, + SLOT(ctx_p2p_delete())); + } + + if (type == PEER_TYPE_AP_WPS) { + menu->addAction(tr("Connect (PBC)"), this, + SLOT(connect_pbc())); + } + + if ((type == PEER_TYPE_ASSOCIATED_STATION || + type == PEER_TYPE_WPS_ER_ENROLLEE || + type == PEER_TYPE_WPS_ENROLLEE) && + config_methods >= 0 && (config_methods & 0x0080)) { + menu->addAction(tr("Enroll (PBC)"), this, + SLOT(connect_pbc())); + } + + if (type == PEER_TYPE_WPS_ER_AP) { + menu->addAction(tr("Learn Configuration"), this, + SLOT(learn_ap_config())); + } + + menu->addAction(tr("Properties"), this, SLOT(properties())); + } else { + ctx_item = NULL; + menu->addAction(QString(tr("Refresh")), this, + SLOT(ctx_refresh())); + menu->addAction(tr("Start P2P discovery"), this, + SLOT(ctx_p2p_start())); + menu->addAction(tr("Stop P2P discovery"), this, + SLOT(ctx_p2p_stop())); + menu->addAction(tr("P2P listen only"), this, + SLOT(ctx_p2p_listen())); + menu->addAction(tr("Start P2P group"), this, + SLOT(ctx_p2p_start_group())); + if (hide_ap) + menu->addAction(tr("Show AP entries"), this, + SLOT(ctx_show_ap())); + else + menu->addAction(tr("Hide AP entries"), this, + SLOT(ctx_hide_ap())); + } + + menu->exec(peers->mapToGlobal(pos)); +} + + +void Peers::enter_pin() +{ + if (ctx_item == NULL) + return; + + int peer_type = ctx_item->data(peer_role_type).toInt(); + QString uuid; + QString addr; + addr = ctx_item->data(peer_role_address).toString(); + if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) + uuid = ctx_item->data(peer_role_uuid).toString(); + + StringQuery input(tr("PIN:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) { + snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", + uuid.toAscii().constData(), + input.get_string().toAscii().constData(), + addr.toAscii().constData()); + } else { + snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", + addr.toAscii().constData(), + input.get_string().toAscii().constData()); + } + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to set the WPS PIN.")); + msg.exec(); + } +} + + +void Peers::ctx_refresh() +{ + update_peers(); +} + + +void Peers::ctx_p2p_start() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_FIND", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P discovery."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_stop() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + wpagui->ctrlRequest("P2P_STOP_FIND", reply, &reply_len); +} + + +void Peers::ctx_p2p_listen() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_LISTEN 3600", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P listen."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_start_group() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_GROUP_ADD", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P group."); + msg.exec(); + } +} + + +void Peers::add_station(QString info) +{ + QStringList lines = info.split(QRegExp("\\n")); + QString name; + + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("wpsDeviceName=")) + name = (*it).mid(pos); + else if ((*it).startsWith("p2p_device_name=")) + name = (*it).mid(pos); + } + + if (name.isEmpty()) + name = lines[0]; + + QStandardItem *item = new QStandardItem(*laptop_icon, name); + if (item) { + /* Remove WPS enrollee entry if one is still pending */ + if (model.rowCount() > 0) { + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_address, + lines[0]); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item; + item = model.itemFromIndex(lst[i]); + if (item == NULL) + continue; + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_WPS_ENROLLEE) { + model.removeRow(lst[i].row()); + break; + } + } + } + + item->setData(lines[0], peer_role_address); + item->setData(PEER_TYPE_ASSOCIATED_STATION, + peer_role_type); + item->setData(info, peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION)); + model.appendRow(item); + } +} + + +void Peers::add_stations() +{ + char reply[2048]; + size_t reply_len; + char cmd[30]; + int res; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0) + return; + + do { + reply[reply_len] = '\0'; + QString info(reply); + char *txt = reply; + while (*txt != '\0' && *txt != '\n') + txt++; + *txt++ = '\0'; + if (strncmp(reply, "FAIL", 4) == 0 || + strncmp(reply, "UNKNOWN", 7) == 0) + break; + + add_station(info); + + reply_len = sizeof(reply) - 1; + snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply); + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + } while (res >= 0); +} + + +void Peers::add_single_station(const char *addr) +{ + char reply[2048]; + size_t reply_len; + char cmd[30]; + + reply_len = sizeof(reply) - 1; + snprintf(cmd, sizeof(cmd), "STA %s", addr); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return; + + reply[reply_len] = '\0'; + QString info(reply); + char *txt = reply; + while (*txt != '\0' && *txt != '\n') + txt++; + *txt++ = '\0'; + if (strncmp(reply, "FAIL", 4) == 0 || + strncmp(reply, "UNKNOWN", 7) == 0) + return; + + add_station(info); +} + + +void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params) +{ + /* + * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0 + * dev_type=1-0050f204-1 dev_name='Wireless Client' + * config_methods=0x8c + */ + + QStringList items = + params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)")); + QString addr = ""; + QString name = ""; + int config_methods = 0; + QString dev_type; + + for (int i = 0; i < items.size(); i++) { + QString str = items.at(i); + int pos = str.indexOf('=') + 1; + if (str.startsWith("dev_name='")) + name = str.section('\'', 1, -2); + else if (str.startsWith("config_methods=")) + config_methods = + str.section('=', 1).toInt(0, 0); + else if (str.startsWith("dev=")) + addr = str.mid(pos); + else if (str.startsWith("dev_type=") && dev_type.isEmpty()) + dev_type = str.mid(pos); + } + + QStandardItem *item = find_addr(addr); + if (item) + return; + + item = new QStandardItem(*default_icon, name); + if (item) { + /* TODO: indicate somehow the relationship to the group owner + * (parent) */ + item->setData(addr, peer_role_address); + item->setData(config_methods, peer_role_config_methods); + item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type); + if (!dev_type.isEmpty()) + item->setData(dev_type, peer_role_pri_dev_type); + item->setData(items.join(QString("\n")), peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT)); + model.appendRow(item); + } +} + + +void Peers::remove_bss(int id) +{ + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_bss_id, + id); + if (lst.size() == 0) + return; + model.removeRow(lst[0].row()); +} + + +bool Peers::add_bss(const char *cmd) +{ + char reply[2048]; + size_t reply_len; + + if (hide_ap) + return false; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return false; + reply[reply_len] = '\0'; + + QString bss(reply); + if (bss.isEmpty() || bss.startsWith("FAIL")) + return false; + + QString ssid, bssid, flags, wps_name, pri_dev_type; + int id = -1; + + QStringList lines = bss.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("bssid=")) + bssid = (*it).mid(pos); + else if ((*it).startsWith("id=")) + id = (*it).mid(pos).toInt(); + else if ((*it).startsWith("flags=")) + flags = (*it).mid(pos); + else if ((*it).startsWith("ssid=")) + ssid = (*it).mid(pos); + else if ((*it).startsWith("wps_device_name=")) + wps_name = (*it).mid(pos); + else if ((*it).startsWith("wps_primary_device_type=")) + pri_dev_type = (*it).mid(pos); + } + + QString name = wps_name; + if (name.isEmpty()) + name = ssid + "\n" + bssid; + + QStandardItem *item = new QStandardItem(*ap_icon, name); + if (item) { + item->setData(bssid, peer_role_address); + if (id >= 0) + item->setData(id, peer_role_bss_id); + int type; + if (flags.contains("[WPS")) + type = PEER_TYPE_AP_WPS; + else + type = PEER_TYPE_AP; + item->setData(type, peer_role_type); + + for (int i = 0; i < lines.size(); i++) { + if (lines[i].length() > 60) { + lines[i].remove(60, lines[i].length()); + lines[i] += ".."; + } + } + item->setToolTip(ItemType(type)); + item->setData(lines.join("\n"), peer_role_details); + if (!pri_dev_type.isEmpty()) + item->setData(pri_dev_type, + peer_role_pri_dev_type); + if (!ssid.isEmpty()) + item->setData(ssid, peer_role_ssid); + model.appendRow(item); + + lines = bss.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + if ((*it).startsWith("p2p_group_client:")) + add_p2p_group_client(item, + (*it).mid(18)); + } + } + + return true; +} + + +void Peers::add_scan_results() +{ + int index; + char cmd[20]; + + index = 0; + while (wpagui) { + snprintf(cmd, sizeof(cmd), "BSS %d", index++); + if (index > 1000) + break; + + if (!add_bss(cmd)) + break; + } +} + + +void Peers::add_persistent(int id, const char *ssid, const char *bssid) +{ + char cmd[100]; + char reply[100]; + size_t reply_len; + int mode; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + mode = atoi(reply); + + QString name = ssid; + name = '[' + name + ']'; + + QStandardItem *item = new QStandardItem(*group_icon, name); + if (!item) + return; + + int type; + if (mode == 3) + type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO; + else + type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT; + item->setData(type, peer_role_type); + item->setToolTip(ItemType(type)); + item->setData(ssid, peer_role_ssid); + if (bssid && strcmp(bssid, "any") == 0) + bssid = NULL; + if (bssid) + item->setData(bssid, peer_role_address); + item->setData(id, peer_role_network_id); + item->setBackground(Qt::BDiagPattern); + + model.appendRow(item); +} + + +void Peers::add_persistent_groups() +{ + char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + + len = sizeof(buf) - 1; + if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0) + return; + + buf[len] = '\0'; + start = strchr(buf, '\n'); + if (start == NULL) + return; + start++; + + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + id = start; + ssid = strchr(id, '\t'); + if (ssid == NULL) + break; + *ssid++ = '\0'; + bssid = strchr(ssid, '\t'); + if (bssid == NULL) + break; + *bssid++ = '\0'; + flags = strchr(bssid, '\t'); + if (flags == NULL) + break; + *flags++ = '\0'; + + if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) + add_persistent(atoi(id), ssid, bssid); + + if (last) + break; + start = end + 1; + } +} + + +void Peers::update_peers() +{ + model.clear(); + if (wpagui == NULL) + return; + + char reply[20]; + size_t replylen = sizeof(reply) - 1; + wpagui->ctrlRequest("WPS_ER_START", reply, &replylen); + + add_stations(); + add_scan_results(); + add_persistent_groups(); +} + + +QStandardItem * Peers::find_addr(QString addr) +{ + if (model.rowCount() == 0) + return NULL; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_address, + addr); + if (lst.size() == 0) + return NULL; + return model.itemFromIndex(lst[0]); +} + + +QStandardItem * Peers::find_addr_type(QString addr, int type) +{ + if (model.rowCount() == 0) + return NULL; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_address, + addr); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item->data(peer_role_type).toInt() == type) + return item; + } + return NULL; +} + + +QStandardItem * Peers::find_uuid(QString uuid) +{ + if (model.rowCount() == 0) + return NULL; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid, + uuid); + if (lst.size() == 0) + return NULL; + return model.itemFromIndex(lst[0]); +} + + +void Peers::event_notify(WpaMsg msg) +{ + QString text = msg.getMsg(); + + if (text.startsWith(WPS_EVENT_PIN_NEEDED)) { + /* + * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7 + * 02:2a:c4:18:5b:f3 + * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1] + */ + QStringList items = text.split(' '); + QString uuid = items[1]; + QString addr = items[2]; + QString name = ""; + + QStandardItem *item = find_addr(addr); + if (item) + return; + + int pos = text.indexOf('['); + if (pos >= 0) { + int pos2 = text.lastIndexOf(']'); + if (pos2 >= pos) { + items = text.mid(pos + 1, pos2 - pos - 1). + split('|'); + name = items[0]; + items.append(addr); + } + } + + item = new QStandardItem(*laptop_icon, name); + if (item) { + item->setData(addr, peer_role_address); + item->setData(PEER_TYPE_WPS_PIN_NEEDED, + peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED)); + item->setData(items.join("\n"), peer_role_details); + item->setData(items[5], peer_role_pri_dev_type); + model.appendRow(item); + } + return; + } + + if (text.startsWith(AP_STA_CONNECTED)) { + /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */ + QStringList items = text.split(' '); + QString addr = items[1]; + QStandardItem *item = find_addr(addr); + if (item == NULL || item->data(peer_role_type).toInt() != + PEER_TYPE_ASSOCIATED_STATION) + add_single_station(addr.toAscii().constData()); + return; + } + + if (text.startsWith(AP_STA_DISCONNECTED)) { + /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */ + QStringList items = text.split(' '); + QString addr = items[1]; + + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_address, addr, -1); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item && item->data(peer_role_type).toInt() == + PEER_TYPE_ASSOCIATED_STATION) { + model.removeRow(lst[i].row()); + break; + } + } + return; + } + + if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) { + /* + * P2P-DEVICE-FOUND 02:b5:64:63:30:63 + * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1 + * name='Wireless Client' config_methods=0x84 dev_capab=0x21 + * group_capab=0x0 + */ + QStringList items = + text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)")); + QString addr = items[1]; + QString name = ""; + QString pri_dev_type; + int config_methods = 0; + for (int i = 0; i < items.size(); i++) { + QString str = items.at(i); + if (str.startsWith("name='")) + name = str.section('\'', 1, -2); + else if (str.startsWith("config_methods=")) + config_methods = + str.section('=', 1).toInt(0, 0); + else if (str.startsWith("pri_dev_type=")) + pri_dev_type = str.section('=', 1); + } + + QStandardItem *item = find_addr(addr); + if (item) { + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_P2P) + return; + } + + item = new QStandardItem(*default_icon, name); + if (item) { + item->setData(addr, peer_role_address); + item->setData(config_methods, + peer_role_config_methods); + item->setData(PEER_TYPE_P2P, peer_role_type); + if (!pri_dev_type.isEmpty()) + item->setData(pri_dev_type, + peer_role_pri_dev_type); + item->setData(items.join(QString("\n")), + peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P)); + model.appendRow(item); + } + + item = find_addr_type(addr, + PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT); + if (item) + item->setBackground(Qt::NoBrush); + } + + if (text.startsWith(P2P_EVENT_GROUP_STARTED)) { + /* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F" + * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7 + * [PERSISTENT] */ + QStringList items = text.split(' '); + if (items.size() < 4) + return; + + int pos = text.indexOf(" ssid=\""); + if (pos < 0) + return; + QString ssid = text.mid(pos + 7); + pos = ssid.indexOf(" passphrase=\""); + if (pos < 0) + pos = ssid.indexOf(" psk="); + if (pos >= 0) + ssid.truncate(pos); + pos = ssid.lastIndexOf('"'); + if (pos >= 0) + ssid.truncate(pos); + + QStandardItem *item = new QStandardItem(*group_icon, ssid); + if (item) { + item->setData(PEER_TYPE_P2P_GROUP, peer_role_type); + item->setData(items[1], peer_role_ifname); + QString details; + if (items[2] == "GO") { + details = tr("P2P GO for interface ") + + items[1]; + } else { + details = tr("P2P client for interface ") + + items[1]; + } + if (text.contains(" [PERSISTENT]")) + details += "\nPersistent group"; + item->setData(details, peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP)); + model.appendRow(item); + } + } + + if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) { + /* P2P-GROUP-REMOVED wlan0-p2p-0 GO */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_ifname, items[1]); + for (int i = 0; i < lst.size(); i++) + model.removeRow(lst[i].row()); + return; + } + + if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) { + /* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */ + QStringList items = text.split(' '); + if (items.size() < 3) + return; + QString addr = items[1]; + QString pin = items[2]; + + QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P); + if (item == NULL) + return; + item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY, + peer_role_selected_method); + item->setData(pin, peer_role_selected_pin); + QVariant var = item->data(peer_role_requested_method); + if (var.isValid() && + var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) { + ctx_item = item; + ctx_p2p_display_pin_pd(); + } + return; + } + + if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) { + /* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + QString addr = items[1]; + + QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P); + if (item == NULL) + return; + item->setData(SEL_METHOD_PIN_PEER_DISPLAY, + peer_role_selected_method); + QVariant var = item->data(peer_role_requested_method); + if (var.isValid() && + var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) { + ctx_item = item; + ctx_p2p_connect(); + } + return; + } + + if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) { + /* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */ + QStringList items = text.split(' '); + if (items.size() < 3) + return; + if (!items[1].startsWith("sa=") || + !items[2].startsWith("persistent=")) + return; + QString addr = items[1].mid(3); + int id = items[2].mid(11).toInt(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + QString name; + char *pos = strrchr(reply, '"'); + if (pos && reply[0] == '"') { + *pos = '\0'; + name = reply + 1; + } else + name = reply; + + QStandardItem *item; + item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION); + if (item) + model.removeRow(item->row()); + + item = new QStandardItem(*invitation_icon, name); + if (!item) + return; + item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION)); + item->setData(addr, peer_role_address); + item->setData(id, peer_role_network_id); + + model.appendRow(item); + + enable_persistent(id); + + return; + } + + if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) { + /* P2P-INVITATION-RESULT status=1 */ + /* TODO */ + return; + } + + if (text.startsWith(WPS_EVENT_ER_AP_ADD)) { + /* + * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 + * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1 + * |Very friendly name|Company|Long description of the model| + * WAP|http://w1.fi/|http://w1.fi/hostapd/ + */ + QStringList items = text.split(' '); + if (items.size() < 5) + return; + QString uuid = items[1]; + QString addr = items[2]; + QString pri_dev_type = items[3].mid(13); + int wps_state = items[4].mid(10).toInt(); + + int pos = text.indexOf('|'); + if (pos < 0) + return; + items = text.mid(pos + 1).split('|'); + if (items.size() < 1) + return; + + QStandardItem *item = find_uuid(uuid); + if (item) + return; + + item = new QStandardItem(*ap_icon, items[0]); + if (item) { + item->setData(uuid, peer_role_uuid); + item->setData(addr, peer_role_address); + int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP: + PEER_TYPE_WPS_ER_AP_UNCONFIGURED; + item->setData(type, peer_role_type); + item->setToolTip(ItemType(type)); + item->setData(pri_dev_type, peer_role_pri_dev_type); + item->setData(items.join(QString("\n")), + peer_role_details); + model.appendRow(item); + } + + return; + } + + if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) { + /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_uuid, items[1]); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item && + (item->data(peer_role_type).toInt() == + PEER_TYPE_WPS_ER_AP || + item->data(peer_role_type).toInt() == + PEER_TYPE_WPS_ER_AP_UNCONFIGURED)) + model.removeRow(lst[i].row()); + } + return; + } + + if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) { + /* + * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333 + * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0 + * pri_dev_type=1-0050F204-1 + * |Wireless Client|Company|cmodel|123|12345| + */ + QStringList items = text.split(' '); + if (items.size() < 3) + return; + QString uuid = items[1]; + QString addr = items[2]; + QString pri_dev_type = items[6].mid(13); + int config_methods = -1; + int dev_passwd_id = -1; + + for (int i = 3; i < items.size(); i++) { + int pos = items[i].indexOf('=') + 1; + if (pos < 1) + continue; + QString val = items[i].mid(pos); + if (items[i].startsWith("config_methods=")) { + config_methods = val.toInt(0, 0); + } else if (items[i].startsWith("dev_passwd_id=")) { + dev_passwd_id = val.toInt(); + } + } + + int pos = text.indexOf('|'); + if (pos < 0) + return; + items = text.mid(pos + 1).split('|'); + if (items.size() < 1) + return; + QString name = items[0]; + if (name.length() == 0) + name = addr; + + remove_enrollee_uuid(uuid); + + QStandardItem *item; + item = new QStandardItem(*laptop_icon, name); + if (item) { + item->setData(uuid, peer_role_uuid); + item->setData(addr, peer_role_address); + item->setData(PEER_TYPE_WPS_ER_ENROLLEE, + peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE)); + item->setData(items.join(QString("\n")), + peer_role_details); + item->setData(pri_dev_type, peer_role_pri_dev_type); + if (config_methods >= 0) + item->setData(config_methods, + peer_role_config_methods); + if (dev_passwd_id >= 0) + item->setData(dev_passwd_id, + peer_role_dev_passwd_id); + model.appendRow(item); + } + + return; + } + + if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) { + /* + * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333 + * 02:66:a0:ee:17:27 + */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + remove_enrollee_uuid(items[1]); + return; + } + + if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) { + /* TODO: need to time out this somehow or remove on successful + * WPS run, etc. */ + /* + * WPS-ENROLLEE-SEEN 02:00:00:00:01:00 + * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1 + * [Wireless Client] + * (MAC addr, UUID-E, pri dev type, config methods, + * dev passwd id, request type, [dev name]) + */ + QStringList items = text.split(' '); + if (items.size() < 7) + return; + QString addr = items[1]; + QString uuid = items[2]; + QString pri_dev_type = items[3]; + int config_methods = items[4].toInt(0, 0); + int dev_passwd_id = items[5].toInt(); + QString name; + + QStandardItem *item = find_addr(addr); + if (item) { + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_ASSOCIATED_STATION) + return; /* already associated */ + } + + int pos = text.indexOf('['); + if (pos >= 0) { + int pos2 = text.lastIndexOf(']'); + if (pos2 >= pos) { + QStringList items2 = + text.mid(pos + 1, pos2 - pos - 1). + split('|'); + name = items2[0]; + } + } + if (name.isEmpty()) + name = addr; + + item = find_uuid(uuid); + if (item) { + QVariant var = item->data(peer_role_config_methods); + QVariant var2 = item->data(peer_role_dev_passwd_id); + if ((var.isValid() && config_methods != var.toInt()) || + (var2.isValid() && dev_passwd_id != var2.toInt())) + remove_enrollee_uuid(uuid); + else + return; + } + + item = new QStandardItem(*laptop_icon, name); + if (item) { + item->setData(uuid, peer_role_uuid); + item->setData(addr, peer_role_address); + item->setData(PEER_TYPE_WPS_ENROLLEE, + peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE)); + item->setData(items.join(QString("\n")), + peer_role_details); + item->setData(pri_dev_type, peer_role_pri_dev_type); + item->setData(config_methods, + peer_role_config_methods); + item->setData(dev_passwd_id, peer_role_dev_passwd_id); + model.appendRow(item); + } + + return; + } + + if (text.startsWith(WPA_EVENT_BSS_ADDED)) { + /* CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + char cmd[20]; + snprintf(cmd, sizeof(cmd), "BSS ID-%d", items[1].toInt()); + add_bss(cmd); + return; + } + + if (text.startsWith(WPA_EVENT_BSS_REMOVED)) { + /* CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + remove_bss(items[1].toInt()); + return; + } +} + + +void Peers::ctx_p2p_connect() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg; + int config_methods = + ctx_item->data(peer_role_config_methods).toInt(); + enum selected_method method = SEL_METHOD_NONE; + QVariant var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) + method = (enum selected_method) var.toInt(); + if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) { + arg = ctx_item->data(peer_role_selected_pin).toString(); + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display", + addr.toAscii().constData(), + arg.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + arg); + } else if (method == SEL_METHOD_PIN_PEER_DISPLAY) { + StringQuery input(tr("PIN from peer display:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + } else if (config_methods == 0x0080 /* PBC */) { + arg = "pbc"; + } else { + StringQuery input(tr("PIN:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + } + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s", + addr.toAscii().constData(), + arg.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_req_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY, + peer_role_requested_method); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display", + addr.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to request PIN from peer.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_show_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY, + peer_role_requested_method); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad", + addr.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to request peer to enter PIN.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_display_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin", + addr.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + reply[reply_len] = '\0'; + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + reply); +} + + +void Peers::ctx_p2p_display_pin_pd() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg = ctx_item->data(peer_role_selected_pin).toString(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display", + addr.toAscii().constData(), + arg.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + reply[reply_len] = '\0'; + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + arg); +} + + +void Peers::ctx_p2p_enter_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg; + + StringQuery input(tr("PIN from peer:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad", + addr.toAscii().constData(), + arg.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_remove_group() +{ + if (ctx_item == NULL) + return; + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", + ctx_item->data(peer_role_ifname).toString().toAscii(). + constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to remove P2P Group."); + msg.exec(); + } +} + + +void Peers::closeEvent(QCloseEvent *) +{ + if (wpagui) { + char reply[20]; + size_t replylen = sizeof(reply) - 1; + wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen); + } +} + + +void Peers::done(int r) +{ + QDialog::done(r); + close(); +} + + +void Peers::remove_enrollee_uuid(QString uuid) +{ + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_uuid, uuid); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item == NULL) + continue; + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_WPS_ER_ENROLLEE || + type == PEER_TYPE_WPS_ENROLLEE) + model.removeRow(lst[i].row()); + } +} + + +void Peers::properties() +{ + if (ctx_item == NULL) + return; + + QMessageBox msg(this); + msg.setStandardButtons(QMessageBox::Ok); + msg.setDefaultButton(QMessageBox::Ok); + msg.setEscapeButton(QMessageBox::Ok); + msg.setWindowTitle(tr("Peer Properties")); + + int type = ctx_item->data(peer_role_type).toInt(); + QString title = Peers::ItemType(type); + + msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text()); + + QVariant var; + QString info; + + var = ctx_item->data(peer_role_address); + if (var.isValid()) + info += tr("Address: ") + var.toString() + QString("\n"); + + var = ctx_item->data(peer_role_uuid); + if (var.isValid()) + info += tr("UUID: ") + var.toString() + QString("\n"); + + var = ctx_item->data(peer_role_pri_dev_type); + if (var.isValid()) + info += tr("Primary Device Type: ") + var.toString() + + QString("\n"); + + var = ctx_item->data(peer_role_ssid); + if (var.isValid()) + info += tr("SSID: ") + var.toString() + QString("\n"); + + var = ctx_item->data(peer_role_config_methods); + if (var.isValid()) { + int methods = var.toInt(); + info += tr("Configuration Methods: "); + if (methods & 0x0001) + info += tr("[USBA]"); + if (methods & 0x0002) + info += tr("[Ethernet]"); + if (methods & 0x0004) + info += tr("[Label]"); + if (methods & 0x0008) + info += tr("[Display]"); + if (methods & 0x0010) + info += tr("[Ext. NFC Token]"); + if (methods & 0x0020) + info += tr("[Int. NFC Token]"); + if (methods & 0x0040) + info += tr("[NFC Interface]"); + if (methods & 0x0080) + info += tr("[Push Button]"); + if (methods & 0x0100) + info += tr("[Keypad]"); + info += "\n"; + } + + var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) { + enum selected_method method = + (enum selected_method) var.toInt(); + switch (method) { + case SEL_METHOD_NONE: + break; + case SEL_METHOD_PIN_PEER_DISPLAY: + info += tr("Selected Method: PIN on peer display\n"); + break; + case SEL_METHOD_PIN_LOCAL_DISPLAY: + info += tr("Selected Method: PIN on local display\n"); + break; + } + } + + var = ctx_item->data(peer_role_selected_pin); + if (var.isValid()) { + info += tr("PIN to enter on peer: ") + var.toString() + "\n"; + } + + var = ctx_item->data(peer_role_dev_passwd_id); + if (var.isValid()) { + info += tr("Device Password ID: ") + var.toString(); + switch (var.toInt()) { + case 0: + info += tr(" (Default PIN)"); + break; + case 1: + info += tr(" (User-specified PIN)"); + break; + case 2: + info += tr(" (Machine-specified PIN)"); + break; + case 3: + info += tr(" (Rekey)"); + break; + case 4: + info += tr(" (Push Button)"); + break; + case 5: + info += tr(" (Registrar-specified)"); + break; + } + info += "\n"; + } + + msg.setInformativeText(info); + + var = ctx_item->data(peer_role_details); + if (var.isValid()) + msg.setDetailedText(var.toString()); + + msg.exec(); +} + + +void Peers::connect_pbc() +{ + if (ctx_item == NULL) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + int peer_type = ctx_item->data(peer_role_type).toInt(); + if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) { + snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s", + ctx_item->data(peer_role_uuid).toString().toAscii(). + constData()); + } else if (peer_type == PEER_TYPE_P2P || + peer_type == PEER_TYPE_P2P_CLIENT) { + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc", + ctx_item->data(peer_role_address).toString(). + toAscii().constData()); + } else { + snprintf(cmd, sizeof(cmd), "WPS_PBC"); + } + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to start WPS PBC.")); + msg.exec(); + } +} + + +void Peers::learn_ap_config() +{ + if (ctx_item == NULL) + return; + + QString uuid = ctx_item->data(peer_role_uuid).toString(); + + StringQuery input(tr("AP PIN:")); + input.setWindowTitle(tr("AP PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s", + uuid.toAscii().constData(), + input.get_string().toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to start learning AP configuration.")); + msg.exec(); + } +} + + +void Peers::ctx_hide_ap() +{ + hide_ap = true; + + if (model.rowCount() == 0) + return; + + do { + QModelIndexList lst; + lst = model.match(model.index(0, 0), + peer_role_type, PEER_TYPE_AP); + if (lst.size() == 0) { + lst = model.match(model.index(0, 0), + peer_role_type, PEER_TYPE_AP_WPS); + if (lst.size() == 0) + break; + } + + model.removeRow(lst[0].row()); + } while (1); +} + + +void Peers::ctx_show_ap() +{ + hide_ap = false; + add_scan_results(); +} + + +void Peers::ctx_p2p_show_passphrase() +{ + char reply[64]; + size_t reply_len; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to get P2P group passphrase."); + msg.exec(); + } else { + reply[reply_len] = '\0'; + QMessageBox::information(this, tr("Passphrase"), + tr("P2P group passphrase:\n") + + reply); + } +} + + +void Peers::ctx_p2p_start_persistent() +{ + if (ctx_item == NULL) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d", + ctx_item->data(peer_role_network_id).toInt()); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to start persistent P2P Group.")); + msg.exec(); + } else if (ctx_item->data(peer_role_type).toInt() == + PEER_TYPE_P2P_INVITATION) + model.removeRow(ctx_item->row()); +} + + +void Peers::ctx_p2p_invite() +{ + if (ctx_item == NULL) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d", + ctx_item->data(peer_role_network_id).toInt()); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to invite peer to start persistent " + "P2P Group.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_delete() +{ + if (ctx_item == NULL) + return; + model.removeRow(ctx_item->row()); +} + + +void Peers::enable_persistent(int id) +{ + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_network_id, id); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) + item->setBackground(Qt::NoBrush); + } +} diff --git a/wpa_supplicant/wpa_gui-qt4/peers.h b/wpa_supplicant/wpa_gui-qt4/peers.h new file mode 100644 index 0000000..a715395 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/peers.h @@ -0,0 +1,96 @@ +/* + * wpa_gui - Peers class + * Copyright (c) 2009-2010, Atheros Communications + * + * 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 PEERS_H +#define PEERS_H + +#include <QObject> +#include <QStandardItemModel> +#include "wpamsg.h" +#include "ui_peers.h" + +class WpaGui; + +class Peers : public QDialog, public Ui::Peers +{ + Q_OBJECT + +public: + Peers(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~Peers(); + void setWpaGui(WpaGui *_wpagui); + void event_notify(WpaMsg msg); + +public slots: + virtual void context_menu(const QPoint &pos); + virtual void enter_pin(); + virtual void connect_pbc(); + virtual void learn_ap_config(); + virtual void ctx_refresh(); + virtual void ctx_p2p_start(); + virtual void ctx_p2p_stop(); + virtual void ctx_p2p_listen(); + virtual void ctx_p2p_start_group(); + virtual void ctx_p2p_remove_group(); + virtual void ctx_p2p_connect(); + virtual void ctx_p2p_req_pin(); + virtual void ctx_p2p_show_pin(); + virtual void ctx_p2p_display_pin(); + virtual void ctx_p2p_display_pin_pd(); + virtual void ctx_p2p_enter_pin(); + virtual void properties(); + virtual void ctx_hide_ap(); + virtual void ctx_show_ap(); + virtual void ctx_p2p_show_passphrase(); + virtual void ctx_p2p_start_persistent(); + virtual void ctx_p2p_invite(); + virtual void ctx_p2p_delete(); + +protected slots: + virtual void languageChange(); + virtual void closeEvent(QCloseEvent *event); + +private: + void add_station(QString info); + void add_stations(); + void add_single_station(const char *addr); + bool add_bss(const char *cmd); + void remove_bss(int id); + void add_scan_results(); + void add_persistent(int id, const char *ssid, const char *bssid); + void add_persistent_groups(); + void update_peers(); + QStandardItem * find_addr(QString addr); + QStandardItem * find_addr_type(QString addr, int type); + void add_p2p_group_client(QStandardItem *parent, QString params); + QStandardItem * find_uuid(QString uuid); + void done(int r); + void remove_enrollee_uuid(QString uuid); + QString ItemType(int type); + void enable_persistent(int id); + + WpaGui *wpagui; + QStandardItemModel model; + QIcon *default_icon; + QIcon *ap_icon; + QIcon *laptop_icon; + QIcon *group_icon; + QIcon *invitation_icon; + QStandardItem *ctx_item; + + bool hide_ap; +}; + +#endif /* PEERS_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/peers.ui b/wpa_supplicant/wpa_gui-qt4/peers.ui new file mode 100644 index 0000000..9508c25 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/peers.ui @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Peers</class> + <widget class="QDialog" name="Peers"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Peers</string> + </property> + <layout class="QGridLayout"> + <item row="0" column="0"> + <widget class="QListView" name="peers"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="mouseTracking"> + <bool>true</bool> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="viewMode"> + <enum>QListView::IconMode</enum> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp new file mode 100644 index 0000000..459aa8c --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp @@ -0,0 +1,144 @@ +/* + * wpa_gui - ScanResults class + * Copyright (c) 2005-2006, 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. + */ + +#include <cstdio> + +#include "scanresults.h" +#include "wpagui.h" +#include "networkconfig.h" + + +ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + connect(scanButton, SIGNAL(clicked()), this, SLOT(scanRequest())); + connect(scanResultsWidget, + SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, + SLOT(bssSelected(QTreeWidgetItem *))); + + wpagui = NULL; + scanResultsWidget->setItemsExpandable(FALSE); + scanResultsWidget->setRootIsDecorated(FALSE); +} + + +ScanResults::~ScanResults() +{ +} + + +void ScanResults::languageChange() +{ + retranslateUi(this); +} + + +void ScanResults::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; + updateResults(); +} + + +void ScanResults::updateResults() +{ + char reply[2048]; + size_t reply_len; + int index; + char cmd[20]; + + scanResultsWidget->clear(); + + index = 0; + while (wpagui) { + snprintf(cmd, sizeof(cmd), "BSS %d", index++); + if (index > 1000) + break; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + break; + reply[reply_len] = '\0'; + + QString bss(reply); + if (bss.isEmpty() || bss.startsWith("FAIL")) + break; + + QString ssid, bssid, freq, signal, flags; + + QStringList lines = bss.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("bssid=")) + bssid = (*it).mid(pos); + else if ((*it).startsWith("freq=")) + freq = (*it).mid(pos); + else if ((*it).startsWith("qual=")) + signal = (*it).mid(pos); + else if ((*it).startsWith("flags=")) + flags = (*it).mid(pos); + else if ((*it).startsWith("ssid=")) + ssid = (*it).mid(pos); + } + + QTreeWidgetItem *item = new QTreeWidgetItem(scanResultsWidget); + if (item) { + item->setText(0, ssid); + item->setText(1, bssid); + item->setText(2, freq); + item->setText(3, signal); + item->setText(4, flags); + } + + if (bssid.isEmpty()) + break; + } +} + + +void ScanResults::scanRequest() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) + return; + + wpagui->ctrlRequest("SCAN", reply, &reply_len); +} + + +void ScanResults::getResults() +{ + updateResults(); +} + + +void ScanResults::bssSelected(QTreeWidgetItem *sel) +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(wpagui); + nc->paramsFromScanResults(sel); + nc->show(); + nc->exec(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.h b/wpa_supplicant/wpa_gui-qt4/scanresults.h new file mode 100644 index 0000000..2c4a1b0 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.h @@ -0,0 +1,46 @@ +/* + * wpa_gui - ScanResults class + * Copyright (c) 2005-2006, 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. + */ + +#ifndef SCANRESULTS_H +#define SCANRESULTS_H + +#include <QObject> +#include "ui_scanresults.h" + +class WpaGui; + +class ScanResults : public QDialog, public Ui::ScanResults +{ + Q_OBJECT + +public: + ScanResults(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~ScanResults(); + +public slots: + virtual void setWpaGui(WpaGui *_wpagui); + virtual void updateResults(); + virtual void scanRequest(); + virtual void getResults(); + virtual void bssSelected(QTreeWidgetItem *sel); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; +}; + +#endif /* SCANRESULTS_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.ui b/wpa_supplicant/wpa_gui-qt4/scanresults.ui new file mode 100644 index 0000000..81e405e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.ui @@ -0,0 +1,94 @@ +<ui version="4.0" > + <class>ScanResults</class> + <widget class="QDialog" name="ScanResults" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>452</width> + <height>244</height> + </rect> + </property> + <property name="windowTitle" > + <string>Scan results</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QTreeWidget" name="scanResultsWidget" > + <property name="editTriggers" > + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="uniformRowHeights" > + <bool>true</bool> + </property> + <property name="sortingEnabled" > + <bool>true</bool> + </property> + <property name="columnCount" > + <number>5</number> + </property> + <column> + <property name="text" > + <string>SSID</string> + </property> + </column> + <column> + <property name="text" > + <string>BSSID</string> + </property> + </column> + <column> + <property name="text" > + <string>frequency</string> + </property> + </column> + <column> + <property name="text" > + <string>signal</string> + </property> + </column> + <column> + <property name="text" > + <string>flags</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="scanButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="closeButton" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/stringquery.cpp b/wpa_supplicant/wpa_gui-qt4/stringquery.cpp new file mode 100644 index 0000000..1ca98d9 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/stringquery.cpp @@ -0,0 +1,37 @@ +/* + * wpa_gui - StringQuery class + * Copyright (c) 2009, Atheros Communications + * + * 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. + */ + +#include <cstdio> +#include <QLabel> + +#include "stringquery.h" + + +StringQuery::StringQuery(QString label) +{ + edit = new QLineEdit; + edit->setFocus(); + QGridLayout *layout = new QGridLayout; + layout->addWidget(new QLabel(label), 0, 0); + layout->addWidget(edit, 0, 1); + setLayout(layout); + + connect(edit, SIGNAL(returnPressed()), this, SLOT(accept())); +} + + +QString StringQuery::get_string() +{ + return edit->text(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/stringquery.h b/wpa_supplicant/wpa_gui-qt4/stringquery.h new file mode 100644 index 0000000..1b68217 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/stringquery.h @@ -0,0 +1,34 @@ +/* + * wpa_gui - StringQuery class + * Copyright (c) 2009, Atheros Communications + * + * 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 STRINGQUERY_H +#define STRINGQUERY_H + +#include <QDialog> +#include <QLineEdit> +#include <QGridLayout> + +class StringQuery : public QDialog +{ + Q_OBJECT + +public: + StringQuery(QString label); + QString get_string(); + +private: + QLineEdit *edit; +}; + +#endif /* STRINGQUERY_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp new file mode 100644 index 0000000..345f965 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp @@ -0,0 +1,100 @@ +/* + * wpa_gui - UserDataRequest class + * Copyright (c) 2005-2006, 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. + */ + +#include "userdatarequest.h" +#include "wpagui.h" +#include "common/wpa_ctrl.h" + + +UserDataRequest::UserDataRequest(QWidget *parent, const char *, bool, + Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(buttonOk, SIGNAL(clicked()), this, SLOT(sendReply())); + connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(queryEdit, SIGNAL(returnPressed()), this, SLOT(sendReply())); +} + + +UserDataRequest::~UserDataRequest() +{ +} + + +void UserDataRequest::languageChange() +{ + retranslateUi(this); +} + + +int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg) +{ + char *tmp, *pos, *pos2; + wpagui = _wpagui; + tmp = strdup(reqMsg); + if (tmp == NULL) + return -1; + pos = strchr(tmp, '-'); + if (pos == NULL) { + free(tmp); + return -1; + } + *pos++ = '\0'; + field = tmp; + pos2 = strchr(pos, ':'); + if (pos2 == NULL) { + free(tmp); + return -1; + } + *pos2++ = '\0'; + + networkid = atoi(pos); + queryInfo->setText(pos2); + if (strcmp(tmp, "PASSWORD") == 0) { + queryField->setText(tr("Password: ")); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "NEW_PASSWORD") == 0) { + queryField->setText(tr("New password: ")); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "IDENTITY") == 0) + queryField->setText(tr("Identity: ")); + else if (strcmp(tmp, "PASSPHRASE") == 0) { + queryField->setText(tr("Private key passphrase: ")); + queryEdit->setEchoMode(QLineEdit::Password); + } else + queryField->setText(field + ":"); + free(tmp); + + return 0; +} + + +void UserDataRequest::sendReply() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) { + reject(); + return; + } + + QString cmd = QString(WPA_CTRL_RSP) + field + '-' + + QString::number(networkid) + ':' + + queryEdit->text(); + wpagui->ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + accept(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.h b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h new file mode 100644 index 0000000..2b6e837 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h @@ -0,0 +1,46 @@ +/* + * wpa_gui - UserDataRequest class + * Copyright (c) 2005-2006, 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. + */ + +#ifndef USERDATAREQUEST_H +#define USERDATAREQUEST_H + +#include <QObject> +#include "ui_userdatarequest.h" + +class WpaGui; + +class UserDataRequest : public QDialog, public Ui::UserDataRequest +{ + Q_OBJECT + +public: + UserDataRequest(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WFlags fl = 0); + ~UserDataRequest(); + + int setParams(WpaGui *_wpagui, const char *reqMsg); + +public slots: + virtual void sendReply(); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; + int networkid; + QString field; +}; + +#endif /* USERDATAREQUEST_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui b/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui new file mode 100644 index 0000000..1de2a26 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui @@ -0,0 +1,109 @@ +<ui version="4.0" stdsetdef="1" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>UserDataRequest</class> + <widget class="QDialog" name="UserDataRequest" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>216</width> + <height>103</height> + </rect> + </property> + <property name="windowTitle" > + <string>Authentication credentials required</string> + </property> + <property name="sizeGripEnabled" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QLabel" name="queryInfo" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QLabel" name="queryField" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="queryEdit" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <item> + <spacer name="spacer4" > + <property name="sizeHint" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + <property name="sizeType" > + <enum>Expanding</enum> + </property> + <property name="orientation" > + <enum>Horizontal</enum> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="buttonOk" > + <property name="text" > + <string>&OK</string> + </property> + <property name="shortcut" > + <string/> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCancel" > + <property name="text" > + <string>&Cancel</string> + </property> + <property name="shortcut" > + <string/> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop b/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop new file mode 100644 index 0000000..ccc7d87 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Name=wpa_gui +Comment=Graphical user interface for wpa_supplicant +Exec=wpa_gui +Icon=wpa_gui +GenericName=wpa_supplicant user interface +Terminal=false +Type=Application +Categories=Qt;Network; diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro new file mode 100644 index 0000000..85848d7 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro @@ -0,0 +1,68 @@ +TEMPLATE = app +LANGUAGE = C++ +TRANSLATIONS = lang/wpa_gui_de.ts + +CONFIG += qt warn_on release + +DEFINES += CONFIG_CTRL_IFACE + +win32 { + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c +} else:win32-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static -mwindows + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c + RESOURCES += icons_png.qrc +} else:win32-x-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static -mwindows + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + DEFINES += _X86_ + SOURCES += ../../src/utils/os_win32.c + RESOURCES += icons_png.qrc +} else { + DEFINES += CONFIG_CTRL_IFACE_UNIX + SOURCES += ../../src/utils/os_unix.c +} + +INCLUDEPATH += . .. ../../src ../../src/utils + +HEADERS += wpamsg.h \ + wpagui.h \ + eventhistory.h \ + scanresults.h \ + userdatarequest.h \ + networkconfig.h \ + addinterface.h \ + peers.h \ + stringquery.h + +SOURCES += main.cpp \ + wpagui.cpp \ + eventhistory.cpp \ + scanresults.cpp \ + userdatarequest.cpp \ + networkconfig.cpp \ + addinterface.cpp \ + peers.cpp \ + stringquery.cpp \ + ../../src/common/wpa_ctrl.c + +RESOURCES += icons.qrc + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.ui \ + peers.ui + + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp new file mode 100644 index 0000000..97bf5ac --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp @@ -0,0 +1,1748 @@ +/* + * wpa_gui - WpaGui class + * Copyright (c) 2005-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. + */ + +#ifdef __MINGW32__ +/* Need to get getopt() */ +#include <unistd.h> +#endif + +#ifdef CONFIG_NATIVE_WINDOWS +#include <windows.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include <cstdio> +#include <QMessageBox> +#include <QCloseEvent> +#include <QImageReader> +#include <QSettings> + +#include "wpagui.h" +#include "dirent.h" +#include "common/wpa_ctrl.h" +#include "userdatarequest.h" +#include "networkconfig.h" + +#if 1 +/* Silence stdout */ +#define printf wpagui_printf +static int wpagui_printf(const char *, ...) +{ + return 0; +} +#endif + +WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags) + : QMainWindow(parent), app(_app) +{ + setupUi(this); + +#ifdef CONFIG_NATIVE_WINDOWS + fileStopServiceAction = new QAction(this); + fileStopServiceAction->setObjectName("Stop Service"); + fileStopServiceAction->setIconText(tr("Stop Service")); + fileMenu->insertAction(actionWPS, fileStopServiceAction); + + fileStartServiceAction = new QAction(this); + fileStartServiceAction->setObjectName("Start Service"); + fileStartServiceAction->setIconText(tr("Start Service")); + fileMenu->insertAction(fileStopServiceAction, fileStartServiceAction); + + connect(fileStartServiceAction, SIGNAL(triggered()), this, + SLOT(startService())); + connect(fileStopServiceAction, SIGNAL(triggered()), this, + SLOT(stopService())); + + addInterfaceAction = new QAction(this); + addInterfaceAction->setIconText(tr("Add Interface")); + fileMenu->insertAction(fileStartServiceAction, addInterfaceAction); + + connect(addInterfaceAction, SIGNAL(triggered()), this, + SLOT(addInterface())); +#endif /* CONFIG_NATIVE_WINDOWS */ + + (void) statusBar(); + + /* + * Disable WPS tab by default; it will be enabled if wpa_supplicant is + * built with WPS support. + */ + wpsTab->setEnabled(false); + wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), false); + + connect(fileEventHistoryAction, SIGNAL(triggered()), this, + SLOT(eventHistory())); + connect(fileSaveConfigAction, SIGNAL(triggered()), this, + SLOT(saveConfig())); + connect(actionWPS, SIGNAL(triggered()), this, SLOT(wpsDialog())); + connect(actionPeers, SIGNAL(triggered()), this, SLOT(peersDialog())); + connect(fileExitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(networkAddAction, SIGNAL(triggered()), this, + SLOT(addNetwork())); + connect(networkEditAction, SIGNAL(triggered()), this, + SLOT(editSelectedNetwork())); + connect(networkRemoveAction, SIGNAL(triggered()), this, + SLOT(removeSelectedNetwork())); + connect(networkEnableAllAction, SIGNAL(triggered()), this, + SLOT(enableAllNetworks())); + connect(networkDisableAllAction, SIGNAL(triggered()), this, + SLOT(disableAllNetworks())); + connect(networkRemoveAllAction, SIGNAL(triggered()), this, + SLOT(removeAllNetworks())); + connect(helpIndexAction, SIGNAL(triggered()), this, SLOT(helpIndex())); + connect(helpContentsAction, SIGNAL(triggered()), this, + SLOT(helpContents())); + connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(helpAbout())); + connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect())); + connect(scanButton, SIGNAL(clicked()), this, SLOT(scan())); + connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB())); + connect(adapterSelect, SIGNAL(activated(const QString&)), this, + SLOT(selectAdapter(const QString&))); + connect(networkSelect, SIGNAL(activated(const QString&)), this, + SLOT(selectNetwork(const QString&))); + connect(addNetworkButton, SIGNAL(clicked()), this, SLOT(addNetwork())); + connect(editNetworkButton, SIGNAL(clicked()), this, + SLOT(editListedNetwork())); + connect(removeNetworkButton, SIGNAL(clicked()), this, + SLOT(removeListedNetwork())); + connect(networkList, SIGNAL(itemSelectionChanged()), this, + SLOT(updateNetworkDisabledStatus())); + connect(enableRadioButton, SIGNAL(toggled(bool)), this, + SLOT(enableListedNetwork(bool))); + connect(disableRadioButton, SIGNAL(toggled(bool)), this, + SLOT(disableListedNetwork(bool))); + connect(scanNetworkButton, SIGNAL(clicked()), this, SLOT(scan())); + connect(networkList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), + this, SLOT(editListedNetwork())); + connect(wpaguiTab, SIGNAL(currentChanged(int)), this, + SLOT(tabChanged(int))); + connect(wpsPbcButton, SIGNAL(clicked()), this, SLOT(wpsPbc())); + connect(wpsPinButton, SIGNAL(clicked()), this, SLOT(wpsGeneratePin())); + connect(wpsApPinEdit, SIGNAL(textChanged(const QString &)), this, + SLOT(wpsApPinChanged(const QString &))); + connect(wpsApPinButton, SIGNAL(clicked()), this, SLOT(wpsApPin())); + + eh = NULL; + scanres = NULL; + peers = NULL; + add_iface = NULL; + udr = NULL; + tray_icon = NULL; + startInTray = false; + ctrl_iface = NULL; + ctrl_conn = NULL; + monitor_conn = NULL; + msgNotifier = NULL; + ctrl_iface_dir = strdup("/var/run/wpa_supplicant"); + + parse_argv(); + +#ifndef QT_NO_SESSIONMANAGER + if (app->isSessionRestored()) { + QSettings settings("wpa_supplicant", "wpa_gui"); + settings.beginGroup("state"); + if (app->sessionId().compare(settings.value("session_id"). + toString()) == 0) + startInTray = settings.value("in_tray").toBool(); + settings.endGroup(); + } +#endif + + if (QSystemTrayIcon::isSystemTrayAvailable()) + createTrayIcon(startInTray); + else + show(); + + connectedToService = false; + textStatus->setText(tr("connecting to wpa_supplicant")); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(ping())); + timer->setSingleShot(FALSE); + timer->start(1000); + + if (openCtrlConnection(ctrl_iface) < 0) { + printf("Failed to open control connection to " + "wpa_supplicant.\n"); + } + + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +WpaGui::~WpaGui() +{ + delete msgNotifier; + + if (monitor_conn) { + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = NULL; + } + + if (peers) { + peers->close(); + delete peers; + peers = NULL; + } + + if (add_iface) { + add_iface->close(); + delete add_iface; + add_iface = NULL; + } + + if (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + free(ctrl_iface); + ctrl_iface = NULL; + + free(ctrl_iface_dir); + ctrl_iface_dir = NULL; +} + + +void WpaGui::languageChange() +{ + retranslateUi(this); +} + + +void WpaGui::parse_argv() +{ + int c; + for (;;) { + c = getopt(qApp->argc(), qApp->argv(), "i:p:t"); + if (c < 0) + break; + switch (c) { + case 'i': + free(ctrl_iface); + ctrl_iface = strdup(optarg); + break; + case 'p': + free(ctrl_iface_dir); + ctrl_iface_dir = strdup(optarg); + break; + case 't': + startInTray = true; + break; + } + } +} + + +int WpaGui::openCtrlConnection(const char *ifname) +{ + char *cfile; + int flen; + char buf[2048], *pos, *pos2; + size_t len; + + if (ifname) { + if (ifname != ctrl_iface) { + free(ctrl_iface); + ctrl_iface = strdup(ifname); + } + } else { +#ifdef CONFIG_CTRL_IFACE_UDP + free(ctrl_iface); + ctrl_iface = strdup("udp"); +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + free(ctrl_iface); + ctrl_iface = NULL; + if (dir) { + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", + dent->d_name); + ctrl_iface = strdup(dent->d_name); + break; + } + closedir(dir); + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + + free(ctrl_iface); + ctrl_iface = NULL; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl) { + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, + &len, NULL); + if (ret >= 0) { + connectedToService = true; + buf[len] = '\0'; + pos = strchr(buf, '\n'); + if (pos) + *pos = '\0'; + ctrl_iface = strdup(buf); + } + wpa_ctrl_close(ctrl); + } +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + } + + if (ctrl_iface == NULL) { +#ifdef CONFIG_NATIVE_WINDOWS + static bool first = true; + if (first && !serviceRunning()) { + first = false; + if (QMessageBox::warning( + this, qAppName(), + tr("wpa_supplicant service is not " + "running.\n" + "Do you want to start it?"), + QMessageBox::Yes | QMessageBox::No) == + QMessageBox::Yes) + startService(); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return -1; + } + +#ifdef CONFIG_CTRL_IFACE_UNIX + flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface); +#else /* CONFIG_CTRL_IFACE_UNIX */ + flen = strlen(ctrl_iface) + 1; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s", ctrl_iface); +#endif /* CONFIG_CTRL_IFACE_UNIX */ + + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (monitor_conn) { + delete msgNotifier; + msgNotifier = NULL; + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + + printf("Trying to connect to '%s'\n", cfile); + ctrl_conn = wpa_ctrl_open(cfile); + if (ctrl_conn == NULL) { + free(cfile); + return -1; + } + monitor_conn = wpa_ctrl_open(cfile); + free(cfile); + if (monitor_conn == NULL) { + wpa_ctrl_close(ctrl_conn); + return -1; + } + if (wpa_ctrl_attach(monitor_conn)) { + printf("Failed to attach to wpa_supplicant\n"); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + return -1; + } + +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) + msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn), + QSocketNotifier::Read, this); + connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs())); +#endif + + adapterSelect->clear(); + adapterSelect->addItem(ctrl_iface); + adapterSelect->setCurrentIndex(0); + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >= + 0) { + buf[len] = '\0'; + pos = buf; + while (*pos) { + pos2 = strchr(pos, '\n'); + if (pos2) + *pos2 = '\0'; + if (strcmp(pos, ctrl_iface) != 0) + adapterSelect->addItem(pos); + if (pos2) + pos = pos2 + 1; + else + break; + } + } + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl_conn, "GET_CAPABILITY eap", 18, buf, &len, + NULL) >= 0) { + buf[len] = '\0'; + + QString res(buf); + QStringList types = res.split(QChar(' ')); + bool wps = types.contains("WSC"); + actionWPS->setEnabled(wps); + wpsTab->setEnabled(wps); + wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), wps); + } + + return 0; +} + + +int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen) +{ + int ret; + + if (ctrl_conn == NULL) + return -3; + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, NULL); + if (ret == -2) + printf("'%s' command timed out.\n", cmd); + else if (ret < 0) + printf("'%s' command failed.\n", cmd); + + return ret; +} + + +QString WpaGui::wpaStateTranslate(char *state) +{ + if (!strcmp(state, "DISCONNECTED")) + return tr("Disconnected"); + else if (!strcmp(state, "INACTIVE")) + return tr("Inactive"); + else if (!strcmp(state, "SCANNING")) + return tr("Scanning"); + else if (!strcmp(state, "AUTHENTICATING")) + return tr("Authenticating"); + else if (!strcmp(state, "ASSOCIATING")) + return tr("Associating"); + else if (!strcmp(state, "ASSOCIATED")) + return tr("Associated"); + else if (!strcmp(state, "4WAY_HANDSHAKE")) + return tr("4-Way Handshake"); + else if (!strcmp(state, "GROUP_HANDSHAKE")) + return tr("Group Handshake"); + else if (!strcmp(state, "COMPLETED")) + return tr("Completed"); + else + return tr("Unknown"); +} + + +void WpaGui::updateStatus() +{ + char buf[2048], *start, *end, *pos; + size_t len; + + pingsToStatusUpdate = 10; + + len = sizeof(buf) - 1; + if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) { + textStatus->setText(tr("Could not get status from " + "wpa_supplicant")); + textAuthentication->clear(); + textEncryption->clear(); + textSsid->clear(); + textBssid->clear(); + textIpAddress->clear(); + +#ifdef CONFIG_NATIVE_WINDOWS + static bool first = true; + if (first && connectedToService && + (ctrl_iface == NULL || *ctrl_iface == '\0')) { + first = false; + if (QMessageBox::information( + this, qAppName(), + tr("No network interfaces in use.\n" + "Would you like to add one?"), + QMessageBox::Yes | QMessageBox::No) == + QMessageBox::Yes) + addInterface(); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return; + } + + buf[len] = '\0'; + + bool auth_updated = false, ssid_updated = false; + bool bssid_updated = false, ipaddr_updated = false; + bool status_updated = false; + char *pairwise_cipher = NULL, *group_cipher = NULL; + char *mode = NULL; + + start = buf; + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + pos = strchr(start, '='); + if (pos) { + *pos++ = '\0'; + if (strcmp(start, "bssid") == 0) { + bssid_updated = true; + textBssid->setText(pos); + } else if (strcmp(start, "ssid") == 0) { + ssid_updated = true; + textSsid->setText(pos); + } else if (strcmp(start, "ip_address") == 0) { + ipaddr_updated = true; + textIpAddress->setText(pos); + } else if (strcmp(start, "wpa_state") == 0) { + status_updated = true; + textStatus->setText(wpaStateTranslate(pos)); + } else if (strcmp(start, "key_mgmt") == 0) { + auth_updated = true; + textAuthentication->setText(pos); + /* TODO: could add EAP status to this */ + } else if (strcmp(start, "pairwise_cipher") == 0) { + pairwise_cipher = pos; + } else if (strcmp(start, "group_cipher") == 0) { + group_cipher = pos; + } else if (strcmp(start, "mode") == 0) { + mode = pos; + } + } + + if (last) + break; + start = end + 1; + } + if (status_updated && mode) + textStatus->setText(textStatus->text() + " (" + mode + ")"); + + if (pairwise_cipher || group_cipher) { + QString encr; + if (pairwise_cipher && group_cipher && + strcmp(pairwise_cipher, group_cipher) != 0) { + encr.append(pairwise_cipher); + encr.append(" + "); + encr.append(group_cipher); + } else if (pairwise_cipher) { + encr.append(pairwise_cipher); + } else { + encr.append(group_cipher); + encr.append(" [group key only]"); + } + textEncryption->setText(encr); + } else + textEncryption->clear(); + + if (!status_updated) + textStatus->clear(); + if (!auth_updated) + textAuthentication->clear(); + if (!ssid_updated) + textSsid->clear(); + if (!bssid_updated) + textBssid->clear(); + if (!ipaddr_updated) + textIpAddress->clear(); +} + + +void WpaGui::updateNetworks() +{ + char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + int first_active = -1; + int was_selected = -1; + bool current = false; + + if (!networkMayHaveChanged) + return; + + if (networkList->currentRow() >= 0) + was_selected = networkList->currentRow(); + + networkSelect->clear(); + networkList->clear(); + + if (ctrl_conn == NULL) + return; + + len = sizeof(buf) - 1; + if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0) + return; + + buf[len] = '\0'; + start = strchr(buf, '\n'); + if (start == NULL) + return; + start++; + + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + id = start; + ssid = strchr(id, '\t'); + if (ssid == NULL) + break; + *ssid++ = '\0'; + bssid = strchr(ssid, '\t'); + if (bssid == NULL) + break; + *bssid++ = '\0'; + flags = strchr(bssid, '\t'); + if (flags == NULL) + break; + *flags++ = '\0'; + + if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) { + if (last) + break; + start = end + 1; + continue; + } + + QString network(id); + network.append(": "); + network.append(ssid); + networkSelect->addItem(network); + networkList->addItem(network); + + if (strstr(flags, "[CURRENT]")) { + networkSelect->setCurrentIndex(networkSelect->count() - + 1); + current = true; + } else if (first_active < 0 && + strstr(flags, "[DISABLED]") == NULL) + first_active = networkSelect->count() - 1; + + if (last) + break; + start = end + 1; + } + + if (networkSelect->count() > 1) + networkSelect->addItem(tr("Select any network")); + + if (!current && first_active >= 0) + networkSelect->setCurrentIndex(first_active); + + if (was_selected >= 0 && networkList->count() > 0) { + if (was_selected < networkList->count()) + networkList->setCurrentRow(was_selected); + else + networkList->setCurrentRow(networkList->count() - 1); + } + else + networkList->setCurrentRow(networkSelect->currentIndex()); + + networkMayHaveChanged = false; +} + + +void WpaGui::helpIndex() +{ + printf("helpIndex\n"); +} + + +void WpaGui::helpContents() +{ + printf("helpContents\n"); +} + + +void WpaGui::helpAbout() +{ + QMessageBox::about(this, "wpa_gui for wpa_supplicant", + "Copyright (c) 2003-2011,\n" + "Jouni Malinen <j@w1.fi>\n" + "and contributors.\n" + "\n" + "This program is free software. You can\n" + "distribute it and/or modify it under the terms " + "of\n" + "the GNU General Public License version 2.\n" + "\n" + "Alternatively, this software may be distributed\n" + "under the terms of the BSD license.\n" + "\n" + "This product includes software developed\n" + "by the OpenSSL Project for use in the\n" + "OpenSSL Toolkit (http://www.openssl.org/)\n"); +} + + +void WpaGui::disconnect() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("DISCONNECT", reply, &reply_len); + stopWpsRun(false); +} + + +void WpaGui::scan() +{ + if (scanres) { + scanres->close(); + delete scanres; + } + + scanres = new ScanResults(); + if (scanres == NULL) + return; + scanres->setWpaGui(this); + scanres->show(); + scanres->exec(); +} + + +void WpaGui::eventHistory() +{ + if (eh) { + eh->close(); + delete eh; + } + + eh = new EventHistory(); + if (eh == NULL) + return; + eh->addEvents(msgs); + eh->show(); + eh->exec(); +} + + +void WpaGui::ping() +{ + char buf[10]; + size_t len; + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + /* + * QSocketNotifier cannot be used with Windows named pipes, so use a + * timer to check for received messages for now. This could be + * optimized be doing something specific to named pipes or Windows + * events, but it is not clear what would be the best way of doing that + * in Qt. + */ + receiveMsgs(); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + + if (scanres && !scanres->isVisible()) { + delete scanres; + scanres = NULL; + } + + if (eh && !eh->isVisible()) { + delete eh; + eh = NULL; + } + + if (udr && !udr->isVisible()) { + delete udr; + udr = NULL; + } + + len = sizeof(buf) - 1; + if (ctrlRequest("PING", buf, &len) < 0) { + printf("PING failed - trying to reconnect\n"); + if (openCtrlConnection(ctrl_iface) >= 0) { + printf("Reconnected successfully\n"); + pingsToStatusUpdate = 0; + } + } + + pingsToStatusUpdate--; + if (pingsToStatusUpdate <= 0) { + updateStatus(); + updateNetworks(); + } + +#ifndef CONFIG_CTRL_IFACE_NAMED_PIPE + /* Use less frequent pings and status updates when the main window is + * hidden (running in taskbar). */ + int interval = isHidden() ? 5000 : 1000; + if (timer->interval() != interval) + timer->setInterval(interval); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +} + + +static int str_match(const char *a, const char *b) +{ + return strncmp(a, b, strlen(b)) == 0; +} + + +void WpaGui::processMsg(char *msg) +{ + char *pos = msg, *pos2; + int priority = 2; + + if (*pos == '<') { + /* skip priority */ + pos++; + priority = atoi(pos); + pos = strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + WpaMsg wm(pos, priority); + if (eh) + eh->addEvent(wm); + if (peers) + peers->event_notify(wm); + msgs.append(wm); + while (msgs.count() > 100) + msgs.pop_front(); + + /* Update last message with truncated version of the event */ + if (strncmp(pos, "CTRL-", 5) == 0) { + pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' '); + if (pos2) + pos2++; + else + pos2 = pos; + } else + pos2 = pos; + QString lastmsg = pos2; + lastmsg.truncate(40); + textLastMessage->setText(lastmsg); + + pingsToStatusUpdate = 0; + networkMayHaveChanged = true; + + if (str_match(pos, WPA_CTRL_REQ)) + processCtrlReq(pos + strlen(WPA_CTRL_REQ)); + else if (str_match(pos, WPA_EVENT_SCAN_RESULTS) && scanres) + scanres->updateResults(); + else if (str_match(pos, WPA_EVENT_DISCONNECTED)) + showTrayMessage(QSystemTrayIcon::Information, 3, + tr("Disconnected from network.")); + else if (str_match(pos, WPA_EVENT_CONNECTED)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + tr("Connection to network established.")); + QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus())); + stopWpsRun(true); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PBC)) { + wpsStatusText->setText(tr("WPS AP in active PBC mode found")); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); + wpsInstructions->setText(tr("Press the PBC button on the " + "screen to start registration")); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PIN)) { + wpsStatusText->setText(tr("WPS AP with recently selected " + "registrar")); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_AUTH)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS) AP\n" + "indicating this client is authorized."); + wpsStatusText->setText("WPS AP indicating this client is " + "authorized"); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) { + wpsStatusText->setText(tr("WPS AP detected")); + } else if (str_match(pos, WPS_EVENT_OVERLAP)) { + wpsStatusText->setText(tr("PBC mode overlap detected")); + wpsInstructions->setText(tr("More than one AP is currently in " + "active WPS PBC mode. Wait couple " + "of minutes and try again")); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_CRED_RECEIVED)) { + wpsStatusText->setText(tr("Network configuration received")); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPA_EVENT_EAP_METHOD)) { + if (strstr(pos, "(WSC)")) + wpsStatusText->setText(tr("Registration started")); + } else if (str_match(pos, WPS_EVENT_M2D)) { + wpsStatusText->setText(tr("Registrar does not yet know PIN")); + } else if (str_match(pos, WPS_EVENT_FAIL)) { + wpsStatusText->setText(tr("Registration failed")); + } else if (str_match(pos, WPS_EVENT_SUCCESS)) { + wpsStatusText->setText(tr("Registration succeeded")); + } +} + + +void WpaGui::processCtrlReq(const char *req) +{ + if (udr) { + udr->close(); + delete udr; + } + udr = new UserDataRequest(); + if (udr == NULL) + return; + if (udr->setParams(this, req) < 0) { + delete udr; + udr = NULL; + return; + } + udr->show(); + udr->exec(); +} + + +void WpaGui::receiveMsgs() +{ + char buf[256]; + size_t len; + + while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) { + len = sizeof(buf) - 1; + if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) { + buf[len] = '\0'; + processMsg(buf); + } + } +} + + +void WpaGui::connectB() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("REASSOCIATE", reply, &reply_len); +} + + +void WpaGui::selectNetwork( const QString &sel ) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.contains(QRegExp("^\\d+:"))) + cmd.truncate(cmd.indexOf(':')); + else + cmd = "any"; + cmd.prepend("SELECT_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); + stopWpsRun(false); +} + + +void WpaGui::enableNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.contains(QRegExp("^\\d+:"))) + cmd.truncate(cmd.indexOf(':')); + else if (!cmd.startsWith("all")) { + printf("Invalid editNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.prepend("ENABLE_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::disableNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.contains(QRegExp("^\\d+:"))) + cmd.truncate(cmd.indexOf(':')); + else if (!cmd.startsWith("all")) { + printf("Invalid editNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.prepend("DISABLE_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::editNetwork(const QString &sel) +{ + QString cmd(sel); + int id = -1; + + if (cmd.contains(QRegExp("^\\d+:"))) { + cmd.truncate(cmd.indexOf(':')); + id = cmd.toInt(); + } + + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + + if (id >= 0) + nc->paramsFromConfig(id); + else + nc->newNetwork(); + + nc->show(); + nc->exec(); +} + + +void WpaGui::editSelectedNetwork() +{ + if (networkSelect->count() < 1) { + QMessageBox::information( + this, tr("No Networks"), + tr("There are no networks to edit.\n")); + return; + } + QString sel(networkSelect->currentText()); + editNetwork(sel); +} + + +void WpaGui::editListedNetwork() +{ + if (networkList->currentRow() < 0) { + QMessageBox::information(this, tr("Select A Network"), + tr("Select a network from the list to" + " edit it.\n")); + return; + } + QString sel(networkList->currentItem()->text()); + editNetwork(sel); +} + + +void WpaGui::triggerUpdate() +{ + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +void WpaGui::addNetwork() +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + nc->newNetwork(); + nc->show(); + nc->exec(); +} + + +void WpaGui::removeNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.contains(QRegExp("^\\d+:"))) + cmd.truncate(cmd.indexOf(':')); + else if (!cmd.startsWith("all")) { + printf("Invalid editNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.prepend("REMOVE_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::removeSelectedNetwork() +{ + if (networkSelect->count() < 1) { + QMessageBox::information(this, tr("No Networks"), + tr("There are no networks to remove." + "\n")); + return; + } + QString sel(networkSelect->currentText()); + removeNetwork(sel); +} + + +void WpaGui::removeListedNetwork() +{ + if (networkList->currentRow() < 0) { + QMessageBox::information(this, tr("Select A Network"), + tr("Select a network from the list " + "to remove it.\n")); + return; + } + QString sel(networkList->currentItem()->text()); + removeNetwork(sel); +} + + +void WpaGui::enableAllNetworks() +{ + QString sel("all"); + enableNetwork(sel); +} + + +void WpaGui::disableAllNetworks() +{ + QString sel("all"); + disableNetwork(sel); +} + + +void WpaGui::removeAllNetworks() +{ + QString sel("all"); + removeNetwork(sel); +} + + +int WpaGui::getNetworkDisabled(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply) - 1; + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid getNetworkDisabled '%s'\n", + cmd.toAscii().constData()); + return -1; + } + cmd.truncate(pos); + cmd.prepend("GET_NETWORK "); + cmd.append(" disabled"); + + if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) >= 0 + && reply_len >= 1) { + reply[reply_len] = '\0'; + if (!str_match(reply, "FAIL")) + return atoi(reply); + } + + return -1; +} + + +void WpaGui::updateNetworkDisabledStatus() +{ + if (networkList->currentRow() < 0) + return; + + QString sel(networkList->currentItem()->text()); + + switch (getNetworkDisabled(sel)) { + case 0: + if (!enableRadioButton->isChecked()) + enableRadioButton->setChecked(true); + return; + case 1: + if (!disableRadioButton->isChecked()) + disableRadioButton->setChecked(true); + return; + } +} + + +void WpaGui::enableListedNetwork(bool enabled) +{ + if (networkList->currentRow() < 0 || !enabled) + return; + + QString sel(networkList->currentItem()->text()); + + if (getNetworkDisabled(sel) == 1) + enableNetwork(sel); +} + + +void WpaGui::disableListedNetwork(bool disabled) +{ + if (networkList->currentRow() < 0 || !disabled) + return; + + QString sel(networkList->currentItem()->text()); + + if (getNetworkDisabled(sel) == 0) + disableNetwork(sel); +} + + +void WpaGui::saveConfig() +{ + char buf[10]; + size_t len; + + len = sizeof(buf) - 1; + ctrlRequest("SAVE_CONFIG", buf, &len); + + buf[len] = '\0'; + + if (str_match(buf, "FAIL")) + QMessageBox::warning( + this, tr("Failed to save configuration"), + tr("The configuration could not be saved.\n" + "\n" + "The update_config=1 configuration option\n" + "must be used for configuration saving to\n" + "be permitted.\n")); + else + QMessageBox::information( + this, tr("Saved configuration"), + tr("The current configuration was saved." + "\n")); +} + + +void WpaGui::selectAdapter( const QString & sel ) +{ + if (openCtrlConnection(sel.toAscii().constData()) < 0) + printf("Failed to open control connection to " + "wpa_supplicant.\n"); + updateStatus(); + updateNetworks(); +} + + +void WpaGui::createTrayIcon(bool trayOnly) +{ + QApplication::setQuitOnLastWindowClosed(false); + + tray_icon = new QSystemTrayIcon(this); + tray_icon->setToolTip(qAppName() + tr(" - wpa_supplicant user interface")); + if (QImageReader::supportedImageFormats().contains(QByteArray("svg"))) + tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg")); + else + tray_icon->setIcon(QIcon(":/icons/wpa_gui.png")); + + connect(tray_icon, + SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); + + ackTrayIcon = false; + + tray_menu = new QMenu(this); + + disconnectAction = new QAction(tr("&Disconnect"), this); + reconnectAction = new QAction(tr("Re&connect"), this); + connect(disconnectAction, SIGNAL(triggered()), this, + SLOT(disconnect())); + connect(reconnectAction, SIGNAL(triggered()), this, + SLOT(connectB())); + tray_menu->addAction(disconnectAction); + tray_menu->addAction(reconnectAction); + tray_menu->addSeparator(); + + eventAction = new QAction(tr("&Event History"), this); + scanAction = new QAction(tr("Scan &Results"), this); + statAction = new QAction(tr("S&tatus"), this); + connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory())); + connect(scanAction, SIGNAL(triggered()), this, SLOT(scan())); + connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus())); + tray_menu->addAction(eventAction); + tray_menu->addAction(scanAction); + tray_menu->addAction(statAction); + tray_menu->addSeparator(); + + showAction = new QAction(tr("&Show Window"), this); + hideAction = new QAction(tr("&Hide Window"), this); + quitAction = new QAction(tr("&Quit"), this); + connect(showAction, SIGNAL(triggered()), this, SLOT(show())); + connect(hideAction, SIGNAL(triggered()), this, SLOT(hide())); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + tray_menu->addAction(showAction); + tray_menu->addAction(hideAction); + tray_menu->addSeparator(); + tray_menu->addAction(quitAction); + + tray_icon->setContextMenu(tray_menu); + + tray_icon->show(); + + if (!trayOnly) + show(); + inTray = trayOnly; +} + + +void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec, + const QString & msg) +{ + if (!QSystemTrayIcon::supportsMessages()) + return; + + if (isVisible() || !tray_icon || !tray_icon->isVisible()) + return; + + tray_icon->showMessage(qAppName(), msg, type, sec * 1000); +} + + +void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how) + { + switch (how) { + /* use close() here instead of hide() and allow the + * custom closeEvent handler take care of children */ + case QSystemTrayIcon::Trigger: + ackTrayIcon = true; + if (isVisible()) { + close(); + inTray = true; + } else { + show(); + inTray = false; + } + break; + case QSystemTrayIcon::MiddleClick: + showTrayStatus(); + break; + default: + break; + } +} + + +void WpaGui::showTrayStatus() +{ + char buf[2048]; + size_t len; + + len = sizeof(buf) - 1; + if (ctrlRequest("STATUS", buf, &len) < 0) + return; + buf[len] = '\0'; + + QString msg, status(buf); + + QStringList lines = status.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("bssid=")) + msg.append("BSSID:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("ssid=")) + msg.append("SSID: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("pairwise_cipher=")) + msg.append("PAIR: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("group_cipher=")) + msg.append("GROUP:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("key_mgmt=")) + msg.append("AUTH: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("wpa_state=")) + msg.append("STATE:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("ip_address=")) + msg.append("IP: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("Supplicant PAE state=")) + msg.append("PAE: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("EAP state=")) + msg.append("EAP: \t" + (*it).mid(pos) + "\n"); + } + + if (!msg.isEmpty()) + showTrayMessage(QSystemTrayIcon::Information, 10, msg); +} + + +void WpaGui::closeEvent(QCloseEvent *event) +{ + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = NULL; + } + + if (peers) { + peers->close(); + delete peers; + peers = NULL; + } + + if (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + if (tray_icon && !ackTrayIcon) { + /* give user a visual hint that the tray icon exists */ + if (QSystemTrayIcon::supportsMessages()) { + hide(); + showTrayMessage(QSystemTrayIcon::Information, 3, + qAppName() + + tr(" will keep running in " + "the system tray.")); + } else { + QMessageBox::information(this, qAppName() + + tr(" systray"), + tr("The program will keep " + "running in the system " + "tray.")); + } + ackTrayIcon = true; + } + + event->accept(); +} + + +void WpaGui::wpsDialog() +{ + wpaguiTab->setCurrentWidget(wpsTab); +} + + +void WpaGui::peersDialog() +{ + if (peers) { + peers->close(); + delete peers; + } + + peers = new Peers(); + if (peers == NULL) + return; + peers->setWpaGui(this); + peers->show(); + peers->exec(); +} + + +void WpaGui::tabChanged(int index) +{ + if (index != 2) + return; + + if (wpsRunning) + return; + + wpsApPinEdit->setEnabled(!bssFromScan.isEmpty()); + if (bssFromScan.isEmpty()) + wpsApPinButton->setEnabled(false); +} + + +void WpaGui::wpsPbc() +{ + char reply[20]; + size_t reply_len = sizeof(reply); + + if (ctrlRequest("WPS_PBC", reply, &reply_len) < 0) + return; + + wpsPinEdit->setEnabled(false); + if (wpsStatusText->text().compare(tr("WPS AP in active PBC mode found"))) { + wpsInstructions->setText(tr("Press the push button on the AP to " + "start the PBC mode.")); + } else { + wpsInstructions->setText(tr("If you have not yet done so, press " + "the push button on the AP to start " + "the PBC mode.")); + } + wpsStatusText->setText(tr("Waiting for Registrar")); + wpsRunning = true; +} + + +void WpaGui::wpsGeneratePin() +{ + char reply[20]; + size_t reply_len = sizeof(reply) - 1; + + if (ctrlRequest("WPS_PIN any", reply, &reply_len) < 0) + return; + + reply[reply_len] = '\0'; + + wpsPinEdit->setText(reply); + wpsPinEdit->setEnabled(true); + wpsInstructions->setText(tr("Enter the generated PIN into the Registrar " + "(either the internal one in the AP or an " + "external one).")); + wpsStatusText->setText(tr("Waiting for Registrar")); + wpsRunning = true; +} + + +void WpaGui::setBssFromScan(const QString &bssid) +{ + bssFromScan = bssid; + wpsApPinEdit->setEnabled(!bssFromScan.isEmpty()); + wpsApPinButton->setEnabled(wpsApPinEdit->text().length() == 8); + wpsStatusText->setText(tr("WPS AP selected from scan results")); + wpsInstructions->setText(tr("If you want to use an AP device PIN, e.g., " + "from a label in the device, enter the eight " + "digit AP PIN and click Use AP PIN button.")); +} + + +void WpaGui::wpsApPinChanged(const QString &text) +{ + wpsApPinButton->setEnabled(text.length() == 8); +} + + +void WpaGui::wpsApPin() +{ + char reply[20]; + size_t reply_len = sizeof(reply); + + QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text()); + if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) < 0) + return; + + wpsStatusText->setText(tr("Waiting for AP/Enrollee")); + wpsRunning = true; +} + + +void WpaGui::stopWpsRun(bool success) +{ + if (wpsRunning) + wpsStatusText->setText(success ? tr("Connected to the network") : + tr("Stopped")); + else + wpsStatusText->setText(""); + wpsPinEdit->setEnabled(false); + wpsInstructions->setText(""); + wpsRunning = false; + bssFromScan = ""; + wpsApPinEdit->setEnabled(false); + wpsApPinButton->setEnabled(false); +} + + +#ifdef CONFIG_NATIVE_WINDOWS + +#ifndef WPASVC_NAME +#define WPASVC_NAME TEXT("wpasvc") +#endif + +class ErrorMsg : public QMessageBox { +public: + ErrorMsg(QWidget *parent, DWORD last_err = GetLastError()); + void showMsg(QString msg); +private: + DWORD err; +}; + +ErrorMsg::ErrorMsg(QWidget *parent, DWORD last_err) : + QMessageBox(parent), err(last_err) +{ + setWindowTitle(tr("wpa_gui error")); + setIcon(QMessageBox::Warning); +} + +void ErrorMsg::showMsg(QString msg) +{ + LPTSTR buf; + + setText(msg); + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, 0, (LPTSTR) (void *) &buf, + 0, NULL) > 0) { + QString msg = QString::fromWCharArray(buf); + setInformativeText(QString("[%1] %2").arg(err).arg(msg)); + LocalFree(buf); + } else { + setInformativeText(QString("[%1]").arg(err)); + } + + exec(); +} + + +void WpaGui::startService() +{ + SC_HANDLE svc, scm; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + ErrorMsg(this).showMsg(tr("OpenSCManager failed")); + return; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_START); + if (!svc) { + ErrorMsg(this).showMsg(tr("OpenService failed")); + CloseServiceHandle(scm); + return; + } + + if (!StartService(svc, 0, NULL)) { + ErrorMsg(this).showMsg(tr("Failed to start wpa_supplicant " + "service")); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); +} + + +void WpaGui::stopService() +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + ErrorMsg(this).showMsg(tr("OpenSCManager failed")); + return; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_STOP); + if (!svc) { + ErrorMsg(this).showMsg(tr("OpenService failed")); + CloseServiceHandle(scm); + return; + } + + if (!ControlService(svc, SERVICE_CONTROL_STOP, &status)) { + ErrorMsg(this).showMsg(tr("Failed to stop wpa_supplicant " + "service")); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); +} + + +bool WpaGui::serviceRunning() +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + bool running = false; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return false; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS); + if (!svc) { + printf("OpenService failed: %d\n\n", (int) GetLastError()); + CloseServiceHandle(scm); + return false; + } + + if (QueryServiceStatus(svc, &status)) { + if (status.dwCurrentState != SERVICE_STOPPED) + running = true; + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + return running; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + +void WpaGui::addInterface() +{ + if (add_iface) { + add_iface->close(); + delete add_iface; + } + add_iface = new AddInterface(this, this); + add_iface->show(); + add_iface->exec(); +} + + +#ifndef QT_NO_SESSIONMANAGER +void WpaGui::saveState() +{ + QSettings settings("wpa_supplicant", "wpa_gui"); + settings.beginGroup("state"); + settings.setValue("session_id", app->sessionId()); + settings.setValue("in_tray", inTray); + settings.endGroup(); +} +#endif diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.h b/wpa_supplicant/wpa_gui-qt4/wpagui.h new file mode 100644 index 0000000..2e1af8e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.h @@ -0,0 +1,150 @@ +/* + * wpa_gui - WpaGui class + * Copyright (c) 2005-2006, 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. + */ + +#ifndef WPAGUI_H +#define WPAGUI_H + +#include <QSystemTrayIcon> +#include <QObject> +#include "ui_wpagui.h" +#include "addinterface.h" + +class UserDataRequest; + + +class WpaGui : public QMainWindow, public Ui::WpaGui +{ + Q_OBJECT + +public: + WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0, + Qt::WFlags fl = 0); + ~WpaGui(); + + virtual int ctrlRequest(const char *cmd, char *buf, size_t *buflen); + virtual void triggerUpdate(); + virtual void editNetwork(const QString &sel); + virtual void removeNetwork(const QString &sel); + virtual void enableNetwork(const QString &sel); + virtual void disableNetwork(const QString &sel); + virtual int getNetworkDisabled(const QString &sel); + void setBssFromScan(const QString &bssid); +#ifndef QT_NO_SESSIONMANAGER + void saveState(); +#endif + +public slots: + virtual void parse_argv(); + virtual void updateStatus(); + virtual void updateNetworks(); + virtual void helpIndex(); + virtual void helpContents(); + virtual void helpAbout(); + virtual void disconnect(); + virtual void scan(); + virtual void eventHistory(); + virtual void ping(); + virtual void processMsg(char *msg); + virtual void processCtrlReq(const char *req); + virtual void receiveMsgs(); + virtual void connectB(); + virtual void selectNetwork(const QString &sel); + virtual void editSelectedNetwork(); + virtual void editListedNetwork(); + virtual void removeSelectedNetwork(); + virtual void removeListedNetwork(); + virtual void addNetwork(); + virtual void enableAllNetworks(); + virtual void disableAllNetworks(); + virtual void removeAllNetworks(); + virtual void saveConfig(); + virtual void selectAdapter(const QString &sel); + virtual void updateNetworkDisabledStatus(); + virtual void enableListedNetwork(bool); + virtual void disableListedNetwork(bool); + virtual void showTrayMessage(QSystemTrayIcon::MessageIcon type, + int sec, const QString &msg); + virtual void showTrayStatus(); + virtual void wpsDialog(); + virtual void peersDialog(); + virtual void tabChanged(int index); + virtual void wpsPbc(); + virtual void wpsGeneratePin(); + virtual void wpsApPinChanged(const QString &text); + virtual void wpsApPin(); +#ifdef CONFIG_NATIVE_WINDOWS + virtual void startService(); + virtual void stopService(); +#endif /* CONFIG_NATIVE_WINDOWS */ + virtual void addInterface(); + +protected slots: + virtual void languageChange(); + virtual void trayActivated(QSystemTrayIcon::ActivationReason how); + virtual void closeEvent(QCloseEvent *event); + +private: + ScanResults *scanres; + Peers *peers; + bool networkMayHaveChanged; + char *ctrl_iface; + EventHistory *eh; + struct wpa_ctrl *ctrl_conn; + QSocketNotifier *msgNotifier; + QTimer *timer; + int pingsToStatusUpdate; + WpaMsgList msgs; + char *ctrl_iface_dir; + struct wpa_ctrl *monitor_conn; + UserDataRequest *udr; + QAction *disconnectAction; + QAction *reconnectAction; + QAction *eventAction; + QAction *scanAction; + QAction *statAction; + QAction *showAction; + QAction *hideAction; + QAction *quitAction; + QMenu *tray_menu; + QSystemTrayIcon *tray_icon; + QString wpaStateTranslate(char *state); + void createTrayIcon(bool); + bool ackTrayIcon; + bool startInTray; + + int openCtrlConnection(const char *ifname); + + bool wpsRunning; + + QString bssFromScan; + + void stopWpsRun(bool success); + +#ifdef CONFIG_NATIVE_WINDOWS + QAction *fileStartServiceAction; + QAction *fileStopServiceAction; + + bool serviceRunning(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + QAction *addInterfaceAction; + AddInterface *add_iface; + + bool connectedToService; + + QApplication *app; + bool inTray; +}; + +#endif /* WPAGUI_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.ui b/wpa_supplicant/wpa_gui-qt4/wpagui.ui new file mode 100644 index 0000000..9f9039f --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.ui @@ -0,0 +1,524 @@ +<ui version="4.0" > + <class>WpaGui</class> + <widget class="QMainWindow" name="WpaGui" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>345</width> + <height>330</height> + </rect> + </property> + <property name="windowTitle" > + <string>wpa_gui</string> + </property> + <property name="windowIcon" > + <iconset resource="icons.qrc" > + <normaloff>:/icons/wpa_gui.svg</normaloff>:/icons/wpa_gui.svg</iconset> + </property> + <widget class="QWidget" name="widget" > + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="adapterLabel" > + <property name="text" > + <string>Adapter:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QComboBox" name="adapterSelect" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="networkLabel" > + <property name="text" > + <string>Network:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="networkSelect" /> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QTabWidget" name="wpaguiTab" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="statusTab" > + <attribute name="title" > + <string>Current Status</string> + </attribute> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="5" > + <widget class="QFrame" name="frame3" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="statusLabel" > + <property name="text" > + <string>Status:</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="lastMessageLabel" > + <property name="text" > + <string>Last message:</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="authenticationLabel" > + <property name="text" > + <string>Authentication:</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="encryptionLabel" > + <property name="text" > + <string>Encryption:</string> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="ssidLabel" > + <property name="text" > + <string>SSID:</string> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="bssidLabel" > + <property name="text" > + <string>BSSID:</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QLabel" name="ipAddressLabel" > + <property name="text" > + <string>IP address:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="textStatus" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLabel" name="textLastMessage" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="textAuthentication" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="textEncryption" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLabel" name="textSsid" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLabel" name="textBssid" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="6" column="1" > + <widget class="QLabel" name="textIpAddress" > + <property name="text" > + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="connectButton" > + <property name="text" > + <string>Connect</string> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="disconnectButton" > + <property name="text" > + <string>Disconnect</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="scanButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + <item row="1" column="4" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="networkconfigTab" > + <attribute name="title" > + <string>Manage Networks</string> + </attribute> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="5" > + <widget class="QListWidget" name="networkList" > + <property name="selectionRectVisible" > + <bool>true</bool> + </property> + </widget> + </item> + <item rowspan="2" row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>61</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QRadioButton" name="enableRadioButton" > + <property name="text" > + <string>Enabled</string> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="editNetworkButton" > + <property name="text" > + <string>Edit</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="removeNetworkButton" > + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + <item rowspan="2" row="1" column="4" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>61</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" > + <widget class="QRadioButton" name="disableRadioButton" > + <property name="text" > + <string>Disabled</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="QPushButton" name="addNetworkButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QPushButton" name="scanNetworkButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="wpsTab" > + <attribute name="title" > + <string>WPS</string> + </attribute> + <layout class="QGridLayout" name="wpsGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Status:</string> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLabel" name="wpsStatusText" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QPushButton" name="wpsPbcButton" > + <property name="text" > + <string>PBC - push button</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QPushButton" name="wpsPinButton" > + <property name="text" > + <string>Generate PIN</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>PIN:</string> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QLineEdit" name="wpsPinEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2" > + <widget class="QPushButton" name="wpsApPinButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Use AP PIN</string> + </property> + </widget> + </item> + <item row="3" column="2" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>AP PIN:</string> + </property> + </widget> + </item> + <item row="3" column="3" > + <widget class="QLineEdit" name="wpsApPinEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="4" column="0" colspan="4" > + <widget class="QTextEdit" name="wpsInstructions" > + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="MenuBar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>345</width> + <height>24</height> + </rect> + </property> + <widget class="QMenu" name="fileMenu" > + <property name="title" > + <string>&File</string> + </property> + <addaction name="fileEventHistoryAction" /> + <addaction name="fileSaveConfigAction" /> + <addaction name="actionWPS" /> + <addaction name="actionPeers" /> + <addaction name="separator" /> + <addaction name="fileExitAction" /> + </widget> + <widget class="QMenu" name="networkMenu" > + <property name="title" > + <string>&Network</string> + </property> + <addaction name="networkAddAction" /> + <addaction name="networkEditAction" /> + <addaction name="networkRemoveAction" /> + <addaction name="separator" /> + <addaction name="networkEnableAllAction" /> + <addaction name="networkDisableAllAction" /> + <addaction name="networkRemoveAllAction" /> + </widget> + <widget class="QMenu" name="helpMenu" > + <property name="title" > + <string>&Help</string> + </property> + <addaction name="helpContentsAction" /> + <addaction name="helpIndexAction" /> + <addaction name="separator" /> + <addaction name="helpAboutAction" /> + </widget> + <addaction name="fileMenu" /> + <addaction name="networkMenu" /> + <addaction name="helpMenu" /> + </widget> + <action name="fileEventHistoryAction" > + <property name="text" > + <string>Event &History</string> + </property> + </action> + <action name="fileSaveConfigAction" > + <property name="text" > + <string>&Save Configuration</string> + </property> + <property name="shortcut" > + <string>Ctrl+S</string> + </property> + </action> + <action name="fileExitAction" > + <property name="text" > + <string>E&xit</string> + </property> + <property name="shortcut" > + <string>Ctrl+Q</string> + </property> + </action> + <action name="networkAddAction" > + <property name="text" > + <string>&Add</string> + </property> + </action> + <action name="networkEditAction" > + <property name="text" > + <string>&Edit</string> + </property> + </action> + <action name="networkRemoveAction" > + <property name="text" > + <string>&Remove</string> + </property> + </action> + <action name="networkEnableAllAction" > + <property name="text" > + <string>E&nable All</string> + </property> + </action> + <action name="networkDisableAllAction" > + <property name="text" > + <string>&Disable All</string> + </property> + </action> + <action name="networkRemoveAllAction" > + <property name="text" > + <string>Re&move All</string> + </property> + </action> + <action name="helpContentsAction" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Contents...</string> + </property> + </action> + <action name="helpIndexAction" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Index...</string> + </property> + </action> + <action name="helpAboutAction" > + <property name="text" > + <string>&About</string> + </property> + </action> + <action name="actionWPS" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Wi-Fi Protected Setup</string> + </property> + </action> + <action name="actionPeers" > + <property name="text" > + <string>&Peers</string> + </property> + </action> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <includes> + <include location="global" >qtimer.h</include> + <include location="global" >qsocketnotifier.h</include> + <include location="local" >wpamsg.h</include> + <include location="local" >eventhistory.h</include> + <include location="local" >scanresults.h</include> + <include location="local" >peers.h</include> + </includes> + <resources> + <include location="icons.qrc" /> + </resources> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/wpamsg.h b/wpa_supplicant/wpa_gui-qt4/wpamsg.h new file mode 100644 index 0000000..4950b21 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpamsg.h @@ -0,0 +1,41 @@ +/* + * wpa_gui - WpaMsg class for storing event messages + * Copyright (c) 2005-2006, 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. + */ + +#ifndef WPAMSG_H +#define WPAMSG_H + +#include <QDateTime> +#include <QLinkedList> + +class WpaMsg { +public: + WpaMsg(const QString &_msg, int _priority = 2) + : msg(_msg), priority(_priority) + { + timestamp = QDateTime::currentDateTime(); + } + + QString getMsg() const { return msg; } + int getPriority() const { return priority; } + QDateTime getTimestamp() const { return timestamp; } + +private: + QString msg; + int priority; + QDateTime timestamp; +}; + +typedef QLinkedList<WpaMsg> WpaMsgList; + +#endif /* WPAMSG_H */ diff --git a/wpa_supplicant/wpa_gui/.gitignore b/wpa_supplicant/wpa_gui/.gitignore new file mode 100644 index 0000000..4df64a9 --- /dev/null +++ b/wpa_supplicant/wpa_gui/.gitignore @@ -0,0 +1,3 @@ +.moc +.obj +.ui diff --git a/wpa_supplicant/wpa_gui/eventhistory.ui b/wpa_supplicant/wpa_gui/eventhistory.ui new file mode 100644 index 0000000..3735fb7 --- /dev/null +++ b/wpa_supplicant/wpa_gui/eventhistory.ui @@ -0,0 +1,125 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>EventHistory</class> +<widget class="QDialog"> + <property name="name"> + <cstring>EventHistory</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>533</width> + <height>285</height> + </rect> + </property> + <property name="caption"> + <string>Event history</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Timestamp</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Message</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>eventListView</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="resizePolicy"> + <enum>Manual</enum> + </property> + <property name="selectionMode"> + <enum>NoSelection</enum> + </property> + <property name="resizeMode"> + <enum>LastColumn</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout30</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>closeButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>EventHistory</receiver> + <slot>close()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in declaration">wpamsg.h</include> + <include location="local" impldecl="in implementation">eventhistory.ui.h</include> +</includes> +<slots> + <slot>addEvents( WpaMsgList msgs )</slot> + <slot>addEvent( WpaMsg msg )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/eventhistory.ui.h b/wpa_supplicant/wpa_gui/eventhistory.ui.h new file mode 100644 index 0000000..cb2caab --- /dev/null +++ b/wpa_supplicant/wpa_gui/eventhistory.ui.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +void EventHistory::init() +{ +} + + +void EventHistory::destroy() +{ +} + + +void EventHistory::addEvents(WpaMsgList msgs) +{ + WpaMsgList::iterator it; + for (it = msgs.begin(); it != msgs.end(); it++) { + addEvent(*it); + } +} + + +void EventHistory::addEvent(WpaMsg msg) +{ + Q3ListViewItem *item; + item = new Q3ListViewItem(eventListView, + msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"), + msg.getMsg()); + if (item == NULL) + return; + eventListView->setSelected(item, false); +} diff --git a/wpa_supplicant/wpa_gui/main.cpp b/wpa_supplicant/wpa_gui/main.cpp new file mode 100644 index 0000000..a78473a --- /dev/null +++ b/wpa_supplicant/wpa_gui/main.cpp @@ -0,0 +1,30 @@ +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <qapplication.h> +#include "wpagui.h" + +int main( int argc, char ** argv ) +{ + QApplication a( argc, argv ); + WpaGui w; + int ret; + +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + printf("Could not find a usable WinSock.dll\n"); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + w.show(); + a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) ); + ret = a.exec(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return ret; +} diff --git a/wpa_supplicant/wpa_gui/networkconfig.ui b/wpa_supplicant/wpa_gui/networkconfig.ui new file mode 100644 index 0000000..019ecf7 --- /dev/null +++ b/wpa_supplicant/wpa_gui/networkconfig.ui @@ -0,0 +1,475 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NetworkConfig</class> +<widget class="QDialog"> + <property name="name"> + <cstring>NetworkConfig</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>380</width> + <height>430</height> + </rect> + </property> + <property name="caption"> + <string>NetworkConfig</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton" row="1" column="3"> + <property name="name"> + <cstring>cancelButton</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + <widget class="QFrame" row="0" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>frame9</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel0</cstring> + </property> + <property name="text"> + <string>SSID</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>ssidEdit</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Network name (Service Set IDentifier)</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Network ID</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>idstrEdit</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Network Identification String</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Authentication</string> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Plaintext or static WEP</string> + </property> + </item> + <item> + <property name="text"> + <string>IEEE 802.1X</string> + </property> + </item> + <item> + <property name="text"> + <string>WPA-Personal (PSK)</string> + </property> + </item> + <item> + <property name="text"> + <string>WPA-Enterprise (EAP)</string> + </property> + </item> + <item> + <property name="text"> + <string>WPA2-Personal (PSK)</string> + </property> + </item> + <item> + <property name="text"> + <string>WPA2-Enterprise (EAP)</string> + </property> + </item> + <property name="name"> + <cstring>authSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Encryption</string> + </property> + </widget> + <widget class="QComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>WEP</string> + </property> + </item> + <item> + <property name="text"> + <string>TKIP</string> + </property> + </item> + <item> + <property name="text"> + <string>CCMP</string> + </property> + </item> + <property name="name"> + <cstring>encrSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>PSK</string> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1"> + <property name="name"> + <cstring>pskEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>WPA/WPA2 pre-shared key or passphrase</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>EAP method</string> + </property> + </widget> + <widget class="QComboBox" row="5" column="1"> + <property name="name"> + <cstring>eapSelect</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Identity</string> + </property> + </widget> + <widget class="QLineEdit" row="6" column="1"> + <property name="name"> + <cstring>identityEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Username/Identity for EAP methods</string> + </property> + </widget> + <widget class="QLabel" row="7" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Password</string> + </property> + </widget> + <widget class="QLineEdit" row="7" column="1"> + <property name="name"> + <cstring>passwordEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Password for EAP methods</string> + </property> + </widget> + <widget class="QLabel" row="8" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>CA certificate</string> + </property> + </widget> + <widget class="QLineEdit" row="8" column="1"> + <property name="name"> + <cstring>cacertEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QButtonGroup" row="9" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>WEP keys</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>wep0Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 0</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>wep1Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 1</string> + </property> + </widget> + <widget class="QRadioButton" row="3" column="0"> + <property name="name"> + <cstring>wep3Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 3</string> + </property> + </widget> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>wep2Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 2</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>wep0Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>wep1Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>wep2Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>wep3Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>130</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="1" column="1"> + <property name="name"> + <cstring>addButton</cstring> + </property> + <property name="text"> + <string>Add</string> + </property> + </widget> + <widget class="QPushButton" row="1" column="2"> + <property name="name"> + <cstring>removeButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>authSelect</sender> + <signal>activated(int)</signal> + <receiver>NetworkConfig</receiver> + <slot>authChanged(int)</slot> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>addButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>addNetwork()</slot> + </connection> + <connection> + <sender>encrSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>NetworkConfig</receiver> + <slot>encrChanged(const QString&)</slot> + </connection> + <connection> + <sender>removeButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>removeNetwork()</slot> + </connection> +</connections> +<tabstops> + <tabstop>ssidEdit</tabstop> + <tabstop>idstrEdit</tabstop> + <tabstop>authSelect</tabstop> + <tabstop>encrSelect</tabstop> + <tabstop>pskEdit</tabstop> + <tabstop>eapSelect</tabstop> + <tabstop>identityEdit</tabstop> + <tabstop>passwordEdit</tabstop> + <tabstop>cacertEdit</tabstop> + <tabstop>wep0Radio</tabstop> + <tabstop>wep1Radio</tabstop> + <tabstop>wep2Radio</tabstop> + <tabstop>wep3Radio</tabstop> + <tabstop>wep0Edit</tabstop> + <tabstop>wep1Edit</tabstop> + <tabstop>wep2Edit</tabstop> + <tabstop>wep3Edit</tabstop> + <tabstop>addButton</tabstop> + <tabstop>removeButton</tabstop> + <tabstop>cancelButton</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in declaration">qlistview.h</include> + <include location="global" impldecl="in implementation">qmessagebox.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">networkconfig.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">int edit_network_id;</variable> + <variable access="private">bool new_network;</variable> +</variables> +<slots> + <slot>authChanged( int sel )</slot> + <slot>addNetwork()</slot> + <slot>encrChanged( const QString & sel )</slot> + <slot>writeWepKey( int network_id, QLineEdit * edit, int id )</slot> + <slot>removeNetwork()</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function>paramsFromScanResults( QListViewItem * sel )</function> + <function>setWpaGui( WpaGui * _wpagui )</function> + <function returnType="int">setNetworkParam( int id, const char * field, const char * value, bool quote )</function> + <function access="private">wepEnabled( bool enabled )</function> + <function>paramsFromConfig( int network_id )</function> + <function>newNetwork()</function> + <function access="private">getEapCapa()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/networkconfig.ui.h b/wpa_supplicant/wpa_gui/networkconfig.ui.h new file mode 100644 index 0000000..501d5d2 --- /dev/null +++ b/wpa_supplicant/wpa_gui/networkconfig.ui.h @@ -0,0 +1,552 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include <stdlib.h> + +enum { + AUTH_NONE = 0, + AUTH_IEEE8021X = 1, + AUTH_WPA_PSK = 2, + AUTH_WPA_EAP = 3, + AUTH_WPA2_PSK = 4, + AUTH_WPA2_EAP = 5 +}; + +#define WPA_GUI_KEY_DATA "[key is configured]" + +void NetworkConfig::init() +{ + wpagui = NULL; + new_network = false; +} + +void NetworkConfig::paramsFromScanResults(Q3ListViewItem *sel) +{ + new_network = true; + + /* SSID BSSID frequency signal flags */ + setCaption(sel->text(0)); + ssidEdit->setText(sel->text(0)); + + QString flags = sel->text(4); + int auth, encr = 0; + if (flags.find("[WPA2-EAP") >= 0) + auth = AUTH_WPA2_EAP; + else if (flags.find("[WPA-EAP") >= 0) + auth = AUTH_WPA_EAP; + else if (flags.find("[WPA2-PSK") >= 0) + auth = AUTH_WPA2_PSK; + else if (flags.find("[WPA-PSK") >= 0) + auth = AUTH_WPA_PSK; + else + auth = AUTH_NONE; + + if (flags.find("-CCMP") >= 0) + encr = 1; + else if (flags.find("-TKIP") >= 0) + encr = 0; + else if (flags.find("WEP") >= 0) + encr = 1; + else + encr = 0; + + authSelect->setCurrentItem(auth); + authChanged(auth); + encrSelect->setCurrentItem(encr); + + getEapCapa(); +} + + +void NetworkConfig::authChanged(int sel) +{ + pskEdit->setEnabled(sel == AUTH_WPA_PSK || sel == AUTH_WPA2_PSK); + bool eap = sel == AUTH_IEEE8021X || sel == AUTH_WPA_EAP || + sel == AUTH_WPA2_EAP; + eapSelect->setEnabled(eap); + identityEdit->setEnabled(eap); + passwordEdit->setEnabled(eap); + cacertEdit->setEnabled(eap); + + while (encrSelect->count()) + encrSelect->removeItem(0); + + if (sel == AUTH_NONE || sel == AUTH_IEEE8021X) { + encrSelect->insertItem("None"); + encrSelect->insertItem("WEP"); + encrSelect->setCurrentItem(sel == AUTH_NONE ? 0 : 1); + } else { + encrSelect->insertItem("TKIP"); + encrSelect->insertItem("CCMP"); + encrSelect->setCurrentItem((sel == AUTH_WPA2_PSK || + sel == AUTH_WPA2_EAP) ? 1 : 0); + } + + wepEnabled(sel == AUTH_IEEE8021X); +} + + +void NetworkConfig::addNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + int id; + int psklen = pskEdit->text().length(); + int auth = authSelect->currentItem(); + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) { + if (psklen < 8 || psklen > 64) { + QMessageBox::warning(this, "wpa_gui", "WPA-PSK requires a passphrase " + "of 8 to 63 characters\n" + "or 64 hex digit PSK"); + return; + } + } + + if (wpagui == NULL) + return; + + memset(reply, 0, sizeof(reply)); + reply_len = sizeof(reply) - 1; + + if (new_network) { + wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len); + if (reply[0] == 'F') { + QMessageBox::warning(this, "wpa_gui", "Failed to add network to wpa_supplicant\n" + "configuration."); + return; + } + id = atoi(reply); + } else { + id = edit_network_id; + } + + setNetworkParam(id, "ssid", ssidEdit->text().ascii(), true); + + if (idstrEdit->isEnabled()) + setNetworkParam(id, "id_str", idstrEdit->text().ascii(), true); + + const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL; + switch (auth) { + case AUTH_NONE: + key_mgmt = "NONE"; + break; + case AUTH_IEEE8021X: + key_mgmt = "IEEE8021X"; + break; + case AUTH_WPA_PSK: + key_mgmt = "WPA-PSK"; + proto = "WPA"; + break; + case AUTH_WPA_EAP: + key_mgmt = "WPA-EAP"; + proto = "WPA"; + break; + case AUTH_WPA2_PSK: + key_mgmt = "WPA-PSK"; + proto = "WPA2"; + break; + case AUTH_WPA2_EAP: + key_mgmt = "WPA-EAP"; + proto = "WPA2"; + break; + } + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP || + auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) { + int encr = encrSelect->currentItem(); + if (encr == 0) + pairwise = "TKIP"; + else + pairwise = "CCMP"; + } + + if (proto) + setNetworkParam(id, "proto", proto, false); + if (key_mgmt) + setNetworkParam(id, "key_mgmt", key_mgmt, false); + if (pairwise) { + setNetworkParam(id, "pairwise", pairwise, false); + setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false); + } + if (pskEdit->isEnabled() && + strcmp(pskEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "psk", pskEdit->text().ascii(), psklen != 64); + if (eapSelect->isEnabled()) + setNetworkParam(id, "eap", eapSelect->currentText().ascii(), false); + if (identityEdit->isEnabled()) + setNetworkParam(id, "identity", identityEdit->text().ascii(), true); + if (passwordEdit->isEnabled() && + strcmp(passwordEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "password", passwordEdit->text().ascii(), true); + if (cacertEdit->isEnabled()) + setNetworkParam(id, "ca_cert", cacertEdit->text().ascii(), true); + writeWepKey(id, wep0Edit, 0); + writeWepKey(id, wep1Edit, 1); + writeWepKey(id, wep2Edit, 2); + writeWepKey(id, wep3Edit, 3); + + if (wep0Radio->isEnabled() && wep0Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "0", false); + else if (wep1Radio->isEnabled() && wep1Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "1", false); + else if (wep2Radio->isEnabled() && wep2Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "2", false); + else if (wep3Radio->isEnabled() && wep3Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "3", false); + + snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", "Failed to enable network in wpa_supplicant\n" + "configuration."); + /* Network was added, so continue anyway */ + } + wpagui->triggerUpdate(); + wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); + + close(); +} + + +void NetworkConfig::setWpaGui( WpaGui *_wpagui ) +{ + wpagui = _wpagui; +} + + +int NetworkConfig::setNetworkParam(int id, const char *field, const char *value, bool quote) +{ + char reply[10], cmd[256]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s", + id, field, quote ? "\"" : "", value, quote ? "\"" : ""); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + return strncmp(reply, "OK", 2) == 0 ? 0 : -1; +} + + +void NetworkConfig::encrChanged( const QString &sel ) +{ + wepEnabled(sel.find("WEP") == 0); +} + + +void NetworkConfig::wepEnabled( bool enabled ) +{ + wep0Edit->setEnabled(enabled); + wep1Edit->setEnabled(enabled); + wep2Edit->setEnabled(enabled); + wep3Edit->setEnabled(enabled); + wep0Radio->setEnabled(enabled); + wep1Radio->setEnabled(enabled); + wep2Radio->setEnabled(enabled); + wep3Radio->setEnabled(enabled); +} + + +void NetworkConfig::writeWepKey( int network_id, QLineEdit *edit, int id ) +{ + char buf[10]; + bool hex; + const char *txt, *pos; + size_t len; + + if (!edit->isEnabled() || edit->text().isEmpty()) + return; + + /* + * Assume hex key if only hex characters are present and length matches + * with 40, 104, or 128-bit key + */ + txt = edit->text().ascii(); + if (strcmp(txt, WPA_GUI_KEY_DATA) == 0) + return; + len = strlen(txt); + if (len == 0) + return; + pos = txt; + hex = true; + while (*pos) { + if (!((*pos >= '0' && *pos <= '9') || (*pos >= 'a' && *pos <= 'f') || + (*pos >= 'A' && *pos <= 'F'))) { + hex = false; + break; + } + pos++; + } + if (hex && len != 10 && len != 26 && len != 32) + hex = false; + snprintf(buf, sizeof(buf), "wep_key%d", id); + setNetworkParam(network_id, buf, txt, !hex); +} + + +static int key_value_isset(const char *reply, size_t reply_len) +{ + return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0); +} + + +void NetworkConfig::paramsFromConfig( int network_id ) +{ + int i, res; + + edit_network_id = network_id; + getEapCapa(); + + char reply[1024], cmd[256], *pos; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + ssidEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + idstrEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id); + reply_len = sizeof(reply) - 1; + int wpa = 0; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "RSN") || strstr(reply, "WPA2")) + wpa = 2; + else if (strstr(reply, "WPA")) + wpa = 1; + } + + int auth = AUTH_NONE, encr = 0; + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "WPA-EAP")) + auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP; + else if (strstr(reply, "WPA-PSK")) + auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK; + else if (strstr(reply, "IEEE8021X")) { + auth = AUTH_IEEE8021X; + encr = 1; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "CCMP") && auth != AUTH_NONE) + encr = 1; + else if (strstr(reply, "TKIP")) + encr = 0; + else if (strstr(reply, "WEP")) + encr = 1; + else + encr = 0; + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + pskEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + pskEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + identityEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + passwordEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + passwordEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && + reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + cacertEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) { + reply[reply_len] = '\0'; + for (i = 0; i < eapSelect->count(); i++) { + if (eapSelect->text(i).compare(reply) == 0) { + eapSelect->setCurrentItem(i); + break; + } + } + } + + for (i = 0; i < 4; i++) { + QLineEdit *wepEdit; + switch (i) { + default: + case 0: + wepEdit = wep0Edit; + break; + case 1: + wepEdit = wep1Edit; + break; + case 2: + wepEdit = wep2Edit; + break; + case 3: + wepEdit = wep3Edit; + break; + } + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", network_id, i); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + + wepEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + wepEdit->setText(WPA_GUI_KEY_DATA); + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) { + reply[reply_len] = '\0'; + switch (atoi(reply)) { + case 0: + wep0Radio->setChecked(true); + break; + case 1: + wep1Radio->setChecked(true); + break; + case 2: + wep2Radio->setChecked(true); + break; + case 3: + wep3Radio->setChecked(true); + break; + } + } + + authSelect->setCurrentItem(auth); + authChanged(auth); + encrSelect->setCurrentItem(encr); + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + wepEnabled(encr == 1); + + removeButton->setEnabled(true); + addButton->setText("Save"); +} + + +void NetworkConfig::removeNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + + if (QMessageBox::information(this, "wpa_gui", + "This will permanently remove the network\n" + "from the configuration. Do you really want\n" + "to remove this network?", "Yes", "No") != 0) + return; + + snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", + "Failed to remove network from wpa_supplicant\n" + "configuration."); + } else { + wpagui->triggerUpdate(); + wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); + } + + close(); +} + + +void NetworkConfig::newNetwork() +{ + new_network = true; + getEapCapa(); +} + + +void NetworkConfig::getEapCapa() +{ + char reply[256]; + size_t reply_len; + + if (wpagui == NULL) + return; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("GET_CAPABILITY eap", reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + + QString res(reply); + QStringList types = QStringList::split(QChar(' '), res); + eapSelect->insertStringList(types); +} diff --git a/wpa_supplicant/wpa_gui/scanresults.ui b/wpa_supplicant/wpa_gui/scanresults.ui new file mode 100644 index 0000000..dea305b --- /dev/null +++ b/wpa_supplicant/wpa_gui/scanresults.ui @@ -0,0 +1,179 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ScanResults</class> +<widget class="QDialog"> + <property name="name"> + <cstring>ScanResults</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>452</width> + <height>225</height> + </rect> + </property> + <property name="caption"> + <string>Scan results</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>SSID</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>BSSID</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>frequency</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>signal</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>flags</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>scanResultsView</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout24</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>50</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>scanButton</cstring> + </property> + <property name="text"> + <string>Scan</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>closeButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>ScanResults</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>scanButton</sender> + <signal>clicked()</signal> + <receiver>ScanResults</receiver> + <slot>scanRequest()</slot> + </connection> + <connection> + <sender>scanResultsView</sender> + <signal>doubleClicked(QListViewItem*)</signal> + <receiver>ScanResults</receiver> + <slot>bssSelected(QListViewItem*)</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">common/wpa_ctrl.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">networkconfig.h</include> + <include location="local" impldecl="in implementation">scanresults.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">QTimer *timer;</variable> +</variables> +<slots> + <slot>setWpaGui( WpaGui * _wpagui )</slot> + <slot>updateResults()</slot> + <slot>scanRequest()</slot> + <slot>getResults()</slot> + <slot>bssSelected( QListViewItem * sel )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/scanresults.ui.h b/wpa_supplicant/wpa_gui/scanresults.ui.h new file mode 100644 index 0000000..530d2e6 --- /dev/null +++ b/wpa_supplicant/wpa_gui/scanresults.ui.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +void ScanResults::init() +{ + wpagui = NULL; +} + + +void ScanResults::destroy() +{ + delete timer; +} + + +void ScanResults::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; + updateResults(); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(getResults())); + timer->start(10000, FALSE); +} + + +void ScanResults::updateResults() +{ + char reply[8192]; + size_t reply_len; + + if (wpagui == NULL) + return; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("SCAN_RESULTS", reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + + scanResultsView->clear(); + + QString res(reply); + QStringList lines = QStringList::split(QChar('\n'), res); + bool first = true; + for (QStringList::Iterator it = lines.begin(); it != lines.end(); it++) { + if (first) { + first = false; + continue; + } + + QStringList cols = QStringList::split(QChar('\t'), *it, true); + QString ssid, bssid, freq, signal, flags; + bssid = cols.count() > 0 ? cols[0] : ""; + freq = cols.count() > 1 ? cols[1] : ""; + signal = cols.count() > 2 ? cols[2] : ""; + flags = cols.count() > 3 ? cols[3] : ""; + ssid = cols.count() > 4 ? cols[4] : ""; + new Q3ListViewItem(scanResultsView, ssid, bssid, freq, signal, flags); + } +} + + +void ScanResults::scanRequest() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) + return; + + wpagui->ctrlRequest("SCAN", reply, &reply_len); +} + + +void ScanResults::getResults() +{ + updateResults(); +} + + + + +void ScanResults::bssSelected( Q3ListViewItem * sel ) +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(wpagui); + nc->paramsFromScanResults(sel); + nc->show(); + nc->exec(); +} diff --git a/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling b/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling new file mode 100755 index 0000000..e173b00 --- /dev/null +++ b/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling @@ -0,0 +1,11 @@ +#!/bin/sh + +# qmake seems to be forcing include and lib paths from the original build +# and I have no idea how to change these. For now, just override the +# directories in the Makefile.Release file after qmake run. + +qmake -spec /q/jm/qt4-win/4.0.0/mkspecs/win32-g++ wpa_gui.pro -o Makefile +cat Makefile.Release | + sed s%qt4/lib%qt4-win/4.0.0/lib%g | + sed s%qt4/include%qt4-win/4.0.0/include%g > tmp.Makefile.Release && +mv -f tmp.Makefile.Release Makefile.Release diff --git a/wpa_supplicant/wpa_gui/userdatarequest.ui b/wpa_supplicant/wpa_gui/userdatarequest.ui new file mode 100644 index 0000000..6106b1e --- /dev/null +++ b/wpa_supplicant/wpa_gui/userdatarequest.ui @@ -0,0 +1,163 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>UserDataRequest</class> +<widget class="QDialog"> + <property name="name"> + <cstring>UserDataRequest</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>216</width> + <height>103</height> + </rect> + </property> + <property name="caption"> + <string>Authentication credentials required</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>queryInfo</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout28</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>queryField</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>queryEdit</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout27</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>UserDataRequest</receiver> + <slot>sendReply()</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>UserDataRequest</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>queryEdit</sender> + <signal>returnPressed()</signal> + <receiver>UserDataRequest</receiver> + <slot>sendReply()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">common/wpa_ctrl.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">userdatarequest.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">int networkid;</variable> + <variable access="private">QString field;</variable> +</variables> +<slots> + <slot>sendReply()</slot> +</slots> +<functions> + <function specifier="non virtual" returnType="int">setParams( WpaGui * _wpagui, const char * reqMsg )</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/userdatarequest.ui.h b/wpa_supplicant/wpa_gui/userdatarequest.ui.h new file mode 100644 index 0000000..66d4478 --- /dev/null +++ b/wpa_supplicant/wpa_gui/userdatarequest.ui.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include <stdlib.h> + +int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg) +{ + char *tmp, *pos, *pos2; + wpagui = _wpagui; + tmp = strdup(reqMsg); + if (tmp == NULL) + return -1; + pos = strchr(tmp, '-'); + if (pos == NULL) { + free(tmp); + return -1; + } + *pos++ = '\0'; + field = tmp; + pos2 = strchr(pos, ':'); + if (pos2 == NULL) { + free(tmp); + return -1; + } + *pos2++ = '\0'; + + networkid = atoi(pos); + queryInfo->setText(pos2); + if (strcmp(tmp, "PASSWORD") == 0) { + queryField->setText("Password: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "NEW_PASSWORD") == 0) { + queryField->setText("New password: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "IDENTITY") == 0) + queryField->setText("Identity: "); + else if (strcmp(tmp, "PASSPHRASE") == 0) { + queryField->setText("Private key passphrase: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else + queryField->setText(field + ":"); + free(tmp); + + return 0; +} + + +void UserDataRequest::sendReply() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) { + reject(); + return; + } + + QString cmd = QString(WPA_CTRL_RSP) + field + '-' + + QString::number(networkid) + ':' + + queryEdit->text(); + wpagui->ctrlRequest(cmd.ascii(), reply, &reply_len); + accept(); +} diff --git a/wpa_supplicant/wpa_gui/wpa_gui.pro b/wpa_supplicant/wpa_gui/wpa_gui.pro new file mode 100644 index 0000000..a42a4ac --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpa_gui.pro @@ -0,0 +1,50 @@ +TEMPLATE = app +LANGUAGE = C++ + +CONFIG += qt warn_on release + +DEFINES += CONFIG_CTRL_IFACE + +win32 { + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c +} else:win32-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c +} else { + DEFINES += CONFIG_CTRL_IFACE_UNIX + SOURCES += ../../src/utils/os_unix.c +} + +INCLUDEPATH += . .. ../../src ../../src/utils + +HEADERS += wpamsg.h + +SOURCES += main.cpp \ + ../../src/common/wpa_ctrl.c + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.ui + + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} + +qtver = $$[QT_VERSION] +isEmpty( qtver ) { + message(Compiling for Qt 3.x) + DEFINES += Q3ListViewItem=QListViewItem +} else { + message(Compiling for Qt $$qtver) + QT += qt3support + CONFIG += uic3 +} diff --git a/wpa_supplicant/wpa_gui/wpagui.ui b/wpa_supplicant/wpa_gui/wpagui.ui new file mode 100644 index 0000000..b49d96b --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpagui.ui @@ -0,0 +1,471 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>WpaGui</class> +<widget class="QMainWindow"> + <property name="name"> + <cstring>WpaGui</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>279</width> + <height>308</height> + </rect> + </property> + <property name="caption"> + <string>wpa_gui</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel16</cstring> + </property> + <property name="text"> + <string>Adapter:</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>adapterSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Network:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>networkSelect</cstring> + </property> + </widget> + <widget class="QFrame" row="2" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Status:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Last message:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Authentication:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Encryption:</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>SSID:</string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>BSSID:</string> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>IP address:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>textStatus</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLastMessage</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>textAuthentication</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="3" column="1"> + <property name="name"> + <cstring>textEncryption</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="4" column="1"> + <property name="name"> + <cstring>textSsid</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="1"> + <property name="name"> + <cstring>textBssid</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="6" column="1"> + <property name="name"> + <cstring>textIpAddress</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </grid> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="3" column="1"> + <property name="name"> + <cstring>connectButton</cstring> + </property> + <property name="text"> + <string>Connect</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="2"> + <property name="name"> + <cstring>disconnectButton</cstring> + </property> + <property name="text"> + <string>Disconnect</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="3"> + <property name="name"> + <cstring>scanButton</cstring> + </property> + <property name="text"> + <string>Scan</string> + </property> + </widget> + </grid> +</widget> +<menubar> + <property name="name"> + <cstring>MenuBar</cstring> + </property> + <item text="&File" name="fileMenu"> + <separator/> + <action name="fileEventHistoryAction"/> + <action name="fileAdd_NetworkAction"/> + <action name="fileEdit_networkAction"/> + <separator/> + <action name="fileExitAction"/> + </item> + <item text="&Help" name="helpMenu"> + <action name="helpContentsAction"/> + <action name="helpIndexAction"/> + <separator/> + <action name="helpAboutAction"/> + </item> +</menubar> +<toolbars> +</toolbars> +<actions> + <action> + <property name="name"> + <cstring>fileExitAction</cstring> + </property> + <property name="text"> + <string>Exit</string> + </property> + <property name="menuText"> + <string>E&xit</string> + </property> + <property name="accel"> + <string>Ctrl+Q</string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpContentsAction</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Contents</string> + </property> + <property name="menuText"> + <string>&Contents...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpIndexAction</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Index</string> + </property> + <property name="menuText"> + <string>&Index...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpAboutAction</cstring> + </property> + <property name="text"> + <string>About</string> + </property> + <property name="menuText"> + <string>&About</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileEventHistoryAction</cstring> + </property> + <property name="text"> + <string>Event History</string> + </property> + <property name="menuText"> + <string>Event &History</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileAdd_NetworkAction</cstring> + </property> + <property name="text"> + <string>Add Network</string> + </property> + <property name="menuText"> + <string>&Add Network</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileEdit_networkAction</cstring> + </property> + <property name="text"> + <string>Edit Network</string> + </property> + <property name="menuText"> + <string>&Edit Network</string> + </property> + </action> +</actions> +<connections> + <connection> + <sender>helpIndexAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpIndex()</slot> + </connection> + <connection> + <sender>helpContentsAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpContents()</slot> + </connection> + <connection> + <sender>helpAboutAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpAbout()</slot> + </connection> + <connection> + <sender>fileExitAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>disconnectButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>disconnect()</slot> + </connection> + <connection> + <sender>scanButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>scan()</slot> + </connection> + <connection> + <sender>connectButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>connectB()</slot> + </connection> + <connection> + <sender>fileEventHistoryAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>eventHistory()</slot> + </connection> + <connection> + <sender>networkSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>WpaGui</receiver> + <slot>selectNetwork(const QString&)</slot> + </connection> + <connection> + <sender>fileEdit_networkAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>editNetwork()</slot> + </connection> + <connection> + <sender>fileAdd_NetworkAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>addNetwork()</slot> + </connection> + <connection> + <sender>adapterSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>WpaGui</receiver> + <slot>selectAdapter(const QString&)</slot> + </connection> +</connections> +<includes> + <include location="global" impldecl="in declaration">qtimer.h</include> + <include location="global" impldecl="in declaration">qsocketnotifier.h</include> + <include location="local" impldecl="in declaration">wpamsg.h</include> + <include location="local" impldecl="in declaration">eventhistory.h</include> + <include location="local" impldecl="in declaration">scanresults.h</include> + <include location="local" impldecl="in implementation">common/wpa_ctrl.h</include> + <include location="global" impldecl="in implementation">dirent.h</include> + <include location="global" impldecl="in implementation">qmessagebox.h</include> + <include location="global" impldecl="in implementation">qapplication.h</include> + <include location="local" impldecl="in implementation">userdatarequest.h</include> + <include location="local" impldecl="in implementation">networkconfig.h</include> + <include location="local" impldecl="in implementation">wpagui.ui.h</include> +</includes> +<forwards> + <forward>class UserDataRequest;</forward> +</forwards> +<variables> + <variable access="private">ScanResults *scanres;</variable> + <variable access="private">bool networkMayHaveChanged;</variable> + <variable access="private">char *ctrl_iface;</variable> + <variable access="private">EventHistory *eh;</variable> + <variable access="private">struct wpa_ctrl *ctrl_conn;</variable> + <variable access="private">QSocketNotifier *msgNotifier;</variable> + <variable access="private">QTimer *timer;</variable> + <variable access="private">int pingsToStatusUpdate;</variable> + <variable access="private">WpaMsgList msgs;</variable> + <variable access="private">char *ctrl_iface_dir;</variable> + <variable access="private">struct wpa_ctrl *monitor_conn;</variable> + <variable access="private">UserDataRequest *udr;</variable> +</variables> +<slots> + <slot>parse_argv()</slot> + <slot>updateStatus()</slot> + <slot>updateNetworks()</slot> + <slot>helpIndex()</slot> + <slot>helpContents()</slot> + <slot>helpAbout()</slot> + <slot>disconnect()</slot> + <slot>scan()</slot> + <slot>eventHistory()</slot> + <slot>ping()</slot> + <slot>processMsg( char * msg )</slot> + <slot>processCtrlReq( const char * req )</slot> + <slot>receiveMsgs()</slot> + <slot>connectB()</slot> + <slot>selectNetwork( const QString & sel )</slot> + <slot>editNetwork()</slot> + <slot>addNetwork()</slot> + <slot>selectAdapter( const QString & sel )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> + <function access="private" specifier="non virtual" returnType="int">openCtrlConnection( const char * ifname )</function> + <function returnType="int">ctrlRequest( const char * cmd, char * buf, size_t * buflen )</function> + <function>triggerUpdate()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/wpagui.ui.h b/wpa_supplicant/wpa_gui/wpagui.ui.h new file mode 100644 index 0000000..678ff1b --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpagui.ui.h @@ -0,0 +1,730 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + + +#ifdef __MINGW32__ +/* Need to get getopt() */ +#include <unistd.h> +#endif + +#include <stdlib.h> + +void WpaGui::init() +{ + eh = NULL; + scanres = NULL; + udr = NULL; + ctrl_iface = NULL; + ctrl_conn = NULL; + monitor_conn = NULL; + msgNotifier = NULL; + ctrl_iface_dir = strdup("/var/run/wpa_supplicant"); + + parse_argv(); + + textStatus->setText("connecting to wpa_supplicant"); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(ping())); + timer->start(1000, FALSE); + + if (openCtrlConnection(ctrl_iface) < 0) { + printf("Failed to open control connection to wpa_supplicant.\n"); + } + + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +void WpaGui::destroy() +{ + delete msgNotifier; + + if (monitor_conn) { + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = NULL; + } + + if (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + free(ctrl_iface); + ctrl_iface = NULL; + + free(ctrl_iface_dir); + ctrl_iface_dir = NULL; +} + + +void WpaGui::parse_argv() +{ + int c; + for (;;) { + c = getopt(qApp->argc(), qApp->argv(), "i:p:"); + if (c < 0) + break; + switch (c) { + case 'i': + free(ctrl_iface); + ctrl_iface = strdup(optarg); + break; + case 'p': + free(ctrl_iface_dir); + ctrl_iface_dir = strdup(optarg); + break; + } + } +} + + +int WpaGui::openCtrlConnection(const char *ifname) +{ + char *cfile; + int flen; + char buf[2048], *pos, *pos2; + size_t len; + + if (ifname) { + if (ifname != ctrl_iface) { + free(ctrl_iface); + ctrl_iface = strdup(ifname); + } + } else { +#ifdef CONFIG_CTRL_IFACE_UDP + free(ctrl_iface); + ctrl_iface = strdup("udp"); +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + free(ctrl_iface); + ctrl_iface = NULL; + if (dir) { + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", dent->d_name); + ctrl_iface = strdup(dent->d_name); + break; + } + closedir(dir); + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + + free(ctrl_iface); + ctrl_iface = NULL; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl) { + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL); + if (ret >= 0) { + buf[len] = '\0'; + pos = strchr(buf, '\n'); + if (pos) + *pos = '\0'; + ctrl_iface = strdup(buf); + } + wpa_ctrl_close(ctrl); + } +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + } + + if (ctrl_iface == NULL) + return -1; + +#ifdef CONFIG_CTRL_IFACE_UNIX + flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface); +#else /* CONFIG_CTRL_IFACE_UNIX */ + flen = strlen(ctrl_iface) + 1; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s", ctrl_iface); +#endif /* CONFIG_CTRL_IFACE_UNIX */ + + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (monitor_conn) { + delete msgNotifier; + msgNotifier = NULL; + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + + printf("Trying to connect to '%s'\n", cfile); + ctrl_conn = wpa_ctrl_open(cfile); + if (ctrl_conn == NULL) { + free(cfile); + return -1; + } + monitor_conn = wpa_ctrl_open(cfile); + free(cfile); + if (monitor_conn == NULL) { + wpa_ctrl_close(ctrl_conn); + return -1; + } + if (wpa_ctrl_attach(monitor_conn)) { + printf("Failed to attach to wpa_supplicant\n"); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + return -1; + } + +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) + msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn), + QSocketNotifier::Read, this); + connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs())); +#endif + + adapterSelect->clear(); + adapterSelect->insertItem(ctrl_iface); + adapterSelect->setCurrentItem(0); + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >= 0) { + buf[len] = '\0'; + pos = buf; + while (*pos) { + pos2 = strchr(pos, '\n'); + if (pos2) + *pos2 = '\0'; + if (strcmp(pos, ctrl_iface) != 0) + adapterSelect->insertItem(pos); + if (pos2) + pos = pos2 + 1; + else + break; + } + } + + return 0; +} + + +static void wpa_gui_msg_cb(char *msg, size_t) +{ + /* This should not happen anymore since two control connections are used. */ + printf("missed message: %s\n", msg); +} + + +int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen) +{ + int ret; + + if (ctrl_conn == NULL) + return -3; + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, + wpa_gui_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + } + + return ret; +} + + +void WpaGui::updateStatus() +{ + char buf[2048], *start, *end, *pos; + size_t len; + + pingsToStatusUpdate = 10; + + len = sizeof(buf) - 1; + if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) { + textStatus->setText("Could not get status from wpa_supplicant"); + textAuthentication->clear(); + textEncryption->clear(); + textSsid->clear(); + textBssid->clear(); + textIpAddress->clear(); + return; + } + + buf[len] = '\0'; + + bool auth_updated = false, ssid_updated = false; + bool bssid_updated = false, ipaddr_updated = false; + bool status_updated = false; + char *pairwise_cipher = NULL, *group_cipher = NULL; + + start = buf; + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + pos = strchr(start, '='); + if (pos) { + *pos++ = '\0'; + if (strcmp(start, "bssid") == 0) { + bssid_updated = true; + textBssid->setText(pos); + } else if (strcmp(start, "ssid") == 0) { + ssid_updated = true; + textSsid->setText(pos); + } else if (strcmp(start, "ip_address") == 0) { + ipaddr_updated = true; + textIpAddress->setText(pos); + } else if (strcmp(start, "wpa_state") == 0) { + status_updated = true; + textStatus->setText(pos); + } else if (strcmp(start, "key_mgmt") == 0) { + auth_updated = true; + textAuthentication->setText(pos); + /* TODO: could add EAP status to this */ + } else if (strcmp(start, "pairwise_cipher") == 0) { + pairwise_cipher = pos; + } else if (strcmp(start, "group_cipher") == 0) { + group_cipher = pos; + } + } + + if (last) + break; + start = end + 1; + } + + if (pairwise_cipher || group_cipher) { + QString encr; + if (pairwise_cipher && group_cipher && + strcmp(pairwise_cipher, group_cipher) != 0) { + encr.append(pairwise_cipher); + encr.append(" + "); + encr.append(group_cipher); + } else if (pairwise_cipher) { + encr.append(pairwise_cipher); + } else { + encr.append(group_cipher); + encr.append(" [group key only]"); + } + textEncryption->setText(encr); + } else + textEncryption->clear(); + + if (!status_updated) + textStatus->clear(); + if (!auth_updated) + textAuthentication->clear(); + if (!ssid_updated) + textSsid->clear(); + if (!bssid_updated) + textBssid->clear(); + if (!ipaddr_updated) + textIpAddress->clear(); +} + + +void WpaGui::updateNetworks() +{ + char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + int first_active = -1; + bool selected = false; + + if (!networkMayHaveChanged) + return; + + networkSelect->clear(); + + if (ctrl_conn == NULL) + return; + + len = sizeof(buf) - 1; + if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0) + return; + + buf[len] = '\0'; + start = strchr(buf, '\n'); + if (start == NULL) + return; + start++; + + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + id = start; + ssid = strchr(id, '\t'); + if (ssid == NULL) + break; + *ssid++ = '\0'; + bssid = strchr(ssid, '\t'); + if (bssid == NULL) + break; + *bssid++ = '\0'; + flags = strchr(bssid, '\t'); + if (flags == NULL) + break; + *flags++ = '\0'; + + QString network(id); + network.append(": "); + network.append(ssid); + networkSelect->insertItem(network); + + if (strstr(flags, "[CURRENT]")) { + networkSelect->setCurrentItem(networkSelect->count() - 1); + selected = true; + } else if (first_active < 0 && strstr(flags, "[DISABLED]") == NULL) + first_active = networkSelect->count() - 1; + + if (last) + break; + start = end + 1; + } + + if (!selected && first_active >= 0) + networkSelect->setCurrentItem(first_active); + + networkMayHaveChanged = false; +} + + +void WpaGui::helpIndex() +{ + printf("helpIndex\n"); +} + + +void WpaGui::helpContents() +{ + printf("helpContents\n"); +} + + +void WpaGui::helpAbout() +{ + QMessageBox::about(this, "wpa_gui for wpa_supplicant", + "Copyright (c) 2003-2008,\n" + "Jouni Malinen <j@w1.fi>\n" + "and contributors.\n" + "\n" + "This program is free software. You can\n" + "distribute it and/or modify it under the terms of\n" + "the GNU General Public License version 2.\n" + "\n" + "Alternatively, this software may be distributed\n" + "under the terms of the BSD license.\n" + "\n" + "This product includes software developed\n" + "by the OpenSSL Project for use in the\n" + "OpenSSL Toolkit (http://www.openssl.org/)\n"); +} + + +void WpaGui::disconnect() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("DISCONNECT", reply, &reply_len); +} + + +void WpaGui::scan() +{ + if (scanres) { + scanres->close(); + delete scanres; + } + + scanres = new ScanResults(); + if (scanres == NULL) + return; + scanres->setWpaGui(this); + scanres->show(); + scanres->exec(); +} + + +void WpaGui::eventHistory() +{ + if (eh) { + eh->close(); + delete eh; + } + + eh = new EventHistory(); + if (eh == NULL) + return; + eh->addEvents(msgs); + eh->show(); + eh->exec(); +} + + +void WpaGui::ping() +{ + char buf[10]; + size_t len; + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + /* + * QSocketNotifier cannot be used with Windows named pipes, so use a timer + * to check for received messages for now. This could be optimized be doing + * something specific to named pipes or Windows events, but it is not clear + * what would be the best way of doing that in Qt. + */ + receiveMsgs(); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + + if (scanres && !scanres->isVisible()) { + delete scanres; + scanres = NULL; + } + + if (eh && !eh->isVisible()) { + delete eh; + eh = NULL; + } + + if (udr && !udr->isVisible()) { + delete udr; + udr = NULL; + } + + len = sizeof(buf) - 1; + if (ctrlRequest("PING", buf, &len) < 0) { + printf("PING failed - trying to reconnect\n"); + if (openCtrlConnection(ctrl_iface) >= 0) { + printf("Reconnected successfully\n"); + pingsToStatusUpdate = 0; + } + } + + pingsToStatusUpdate--; + if (pingsToStatusUpdate <= 0) { + updateStatus(); + updateNetworks(); + } +} + + +static int str_match(const char *a, const char *b) +{ + return strncmp(a, b, strlen(b)) == 0; +} + + +void WpaGui::processMsg(char *msg) +{ + char *pos = msg, *pos2; + int priority = 2; + + if (*pos == '<') { + /* skip priority */ + pos++; + priority = atoi(pos); + pos = strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + WpaMsg wm(pos, priority); + if (eh) + eh->addEvent(wm); + msgs.append(wm); + while (msgs.count() > 100) + msgs.pop_front(); + + /* Update last message with truncated version of the event */ + if (strncmp(pos, "CTRL-", 5) == 0) { + pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' '); + if (pos2) + pos2++; + else + pos2 = pos; + } else + pos2 = pos; + QString lastmsg = pos2; + lastmsg.truncate(40); + textLastMessage->setText(lastmsg); + + pingsToStatusUpdate = 0; + networkMayHaveChanged = true; + + if (str_match(pos, WPA_CTRL_REQ)) + processCtrlReq(pos + strlen(WPA_CTRL_REQ)); +} + + +void WpaGui::processCtrlReq(const char *req) +{ + if (udr) { + udr->close(); + delete udr; + } + udr = new UserDataRequest(); + if (udr == NULL) + return; + if (udr->setParams(this, req) < 0) { + delete udr; + udr = NULL; + return; + } + udr->show(); + udr->exec(); +} + + +void WpaGui::receiveMsgs() +{ + char buf[256]; + size_t len; + + while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) { + len = sizeof(buf) - 1; + if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) { + buf[len] = '\0'; + processMsg(buf); + } + } +} + + +void WpaGui::connectB() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("REASSOCIATE", reply, &reply_len); +} + + +void WpaGui::selectNetwork( const QString &sel ) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + int pos = cmd.find(':'); + if (pos < 0) { + printf("Invalid selectNetwork '%s'\n", cmd.ascii()); + return; + } + cmd.truncate(pos); + cmd.prepend("SELECT_NETWORK "); + ctrlRequest(cmd.ascii(), reply, &reply_len); +} + + +void WpaGui::editNetwork() +{ + QString sel(networkSelect->currentText()); + int pos = sel.find(':'); + if (pos < 0) { + printf("Invalid selectNetwork '%s'\n", sel.ascii()); + return; + } + sel.truncate(pos); + + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + + nc->paramsFromConfig(sel.toInt()); + nc->show(); + nc->exec(); +} + + +void WpaGui::triggerUpdate() +{ + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +void WpaGui::addNetwork() +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + nc->newNetwork(); + nc->show(); + nc->exec(); +} + + +void WpaGui::selectAdapter( const QString & sel ) +{ + if (openCtrlConnection(sel.ascii()) < 0) + printf("Failed to open control connection to wpa_supplicant.\n"); + updateStatus(); + updateNetworks(); +} diff --git a/wpa_supplicant/wpa_gui/wpamsg.h b/wpa_supplicant/wpa_gui/wpamsg.h new file mode 100644 index 0000000..f3fce06 --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpamsg.h @@ -0,0 +1,34 @@ +#ifndef WPAMSG_H +#define WPAMSG_H + +class WpaMsg; + +#if QT_VERSION >= 0x040000 +#include <QDateTime> +#include <QLinkedList> +typedef QLinkedList<WpaMsg> WpaMsgList; +#else +#include <qdatetime.h> +typedef QValueList<WpaMsg> WpaMsgList; +#endif + +class WpaMsg { +public: + WpaMsg() {} + WpaMsg(const QString &_msg, int _priority = 2) + : msg(_msg), priority(_priority) + { + timestamp = QDateTime::currentDateTime(); + } + + QString getMsg() const { return msg; } + int getPriority() const { return priority; } + QDateTime getTimestamp() const { return timestamp; } + +private: + QString msg; + int priority; + QDateTime timestamp; +}; + +#endif /* WPAMSG_H */ diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c new file mode 100644 index 0000000..67465aa --- /dev/null +++ b/wpa_supplicant/wpa_passphrase.c @@ -0,0 +1,73 @@ +/* + * WPA Supplicant - ASCII passphrase to WPA PSK tool + * Copyright (c) 2003-2005, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha1.h" + + +int main(int argc, char *argv[]) +{ + unsigned char psk[32]; + int i; + char *ssid, *passphrase, buf[64], *pos; + + if (argc < 2) { + printf("usage: wpa_passphrase <ssid> [passphrase]\n" + "\nIf passphrase is left out, it will be read from " + "stdin\n"); + return 1; + } + + ssid = argv[1]; + + if (argc > 2) { + passphrase = argv[2]; + } else { + printf("# reading passphrase from stdin\n"); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + printf("Failed to read passphrase\n"); + return 1; + } + buf[sizeof(buf) - 1] = '\0'; + pos = buf; + while (*pos != '\0') { + if (*pos == '\r' || *pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + passphrase = buf; + } + + if (os_strlen(passphrase) < 8 || os_strlen(passphrase) > 63) { + printf("Passphrase must be 8..63 characters\n"); + return 1; + } + + pbkdf2_sha1(passphrase, ssid, os_strlen(ssid), 4096, psk, 32); + + printf("network={\n"); + printf("\tssid=\"%s\"\n", ssid); + printf("\t#psk=\"%s\"\n", passphrase); + printf("\tpsk="); + for (i = 0; i < 32; i++) + printf("%02x", psk[i]); + printf("\n"); + printf("}\n"); + + return 0; +} diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c new file mode 100644 index 0000000..d2a991b --- /dev/null +++ b/wpa_supplicant/wpa_priv.c @@ -0,0 +1,1040 @@ +/* + * WPA Supplicant / privileged helper program + * Copyright (c) 2007-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. + */ + +#include "includes.h" +#ifdef __linux__ +#include <fcntl.h> +#endif /* __linux__ */ +#include <sys/un.h> +#include <sys/stat.h> + +#include "common.h" +#include "eloop.h" +#include "common/version.h" +#include "drivers/driver.h" +#include "l2_packet/l2_packet.h" +#include "common/privsep_commands.h" +#include "common/ieee802_11_defs.h" + + +struct wpa_priv_interface { + struct wpa_priv_interface *next; + char *driver_name; + char *ifname; + char *sock_name; + int fd; + + struct wpa_driver_ops *driver; + void *drv_priv; + struct sockaddr_un drv_addr; + int wpas_registered; + + /* TODO: add support for multiple l2 connections */ + struct l2_packet_data *l2; + struct sockaddr_un l2_addr; +}; + + +static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->drv_priv) { + wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance"); + if (iface->driver->deinit) + iface->driver->deinit(iface->drv_priv); + iface->drv_priv = NULL; + iface->wpas_registered = 0; + } + + if (iface->l2) { + wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " + "instance"); + l2_packet_deinit(iface->l2); + iface->l2 = NULL; + } + + if (iface->driver->init == NULL) + return; + + iface->drv_priv = iface->driver->init(iface, iface->ifname); + if (iface->drv_priv == NULL) { + wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper"); + return; + } + + wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface " + "'%s'", iface->driver_name, iface->ifname); + + os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr)); + iface->wpas_registered = 1; + + if (iface->driver->set_param && + iface->driver->set_param(iface->drv_priv, NULL) < 0) { + wpa_printf(MSG_ERROR, "Driver interface rejected param"); + } +} + + +static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->drv_priv) { + if (iface->driver->deinit) + iface->driver->deinit(iface->drv_priv); + iface->drv_priv = NULL; + iface->wpas_registered = 0; + } +} + + +static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface, + char *buf, size_t len) +{ + struct wpa_driver_scan_params params; + + if (iface->drv_priv == NULL) + return; + + os_memset(¶ms, 0, sizeof(params)); + if (len) { + params.ssids[0].ssid = (u8 *) buf; + params.ssids[0].ssid_len = len; + params.num_ssids = 1; + } + + if (iface->driver->scan2) + iface->driver->scan2(iface->drv_priv, ¶ms); +} + + +static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + struct wpa_scan_results *res; + u8 *buf = NULL, *pos, *end; + int val; + size_t i; + + res = iface->driver->get_scan_results2(iface->drv_priv); + if (res == NULL) + goto fail; + + buf = os_malloc(60000); + if (buf == NULL) + goto fail; + pos = buf; + end = buf + 60000; + val = res->num; + os_memcpy(pos, &val, sizeof(int)); + pos += sizeof(int); + + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + val = sizeof(*r) + r->ie_len; + if (end - pos < (int) sizeof(int) + val) + break; + os_memcpy(pos, &val, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, r, val); + pos += val; + } + + sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from, + sizeof(*from)); + + os_free(buf); + wpa_scan_results_free(res); + return; + +fail: + os_free(buf); + wpa_scan_results_free(res); + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->drv_priv == NULL) + return; + + if (iface->driver->get_scan_results2) + wpa_priv_get_scan_results2(iface, from); + else + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, + sizeof(*from)); +} + + +static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, + void *buf, size_t len) +{ + struct wpa_driver_associate_params params; + struct privsep_cmd_associate *assoc; + u8 *bssid; + int res; + + if (iface->drv_priv == NULL || iface->driver->associate == NULL) + return; + + if (len < sizeof(*assoc)) { + wpa_printf(MSG_DEBUG, "Invalid association request"); + return; + } + + assoc = buf; + if (sizeof(*assoc) + assoc->wpa_ie_len > len) { + wpa_printf(MSG_DEBUG, "Association request overflow"); + return; + } + + os_memset(¶ms, 0, sizeof(params)); + bssid = assoc->bssid; + if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5]) + params.bssid = bssid; + params.ssid = assoc->ssid; + if (assoc->ssid_len > 32) + return; + params.ssid_len = assoc->ssid_len; + params.freq = assoc->freq; + if (assoc->wpa_ie_len) { + params.wpa_ie = (u8 *) (assoc + 1); + params.wpa_ie_len = assoc->wpa_ie_len; + } + params.pairwise_suite = assoc->pairwise_suite; + params.group_suite = assoc->group_suite; + params.key_mgmt_suite = assoc->key_mgmt_suite; + params.auth_alg = assoc->auth_alg; + params.mode = assoc->mode; + + res = iface->driver->associate(iface->drv_priv, ¶ms); + wpa_printf(MSG_DEBUG, "drv->associate: res=%d", res); +} + + +static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + u8 bssid[ETH_ALEN]; + + if (iface->drv_priv == NULL) + goto fail; + + if (iface->driver->get_bssid == NULL || + iface->driver->get_bssid(iface->drv_priv, bssid) < 0) + goto fail; + + sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from, + sizeof(*from)); + return; + +fail: + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + u8 ssid[sizeof(int) + 32]; + int res; + + if (iface->drv_priv == NULL) + goto fail; + + if (iface->driver->get_ssid == NULL) + goto fail; + + res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]); + if (res < 0 || res > 32) + goto fail; + os_memcpy(ssid, &res, sizeof(int)); + + sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from, + sizeof(*from)); + return; + +fail: + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface, + void *buf, size_t len) +{ + struct privsep_cmd_set_key *params; + int res; + + if (iface->drv_priv == NULL || iface->driver->set_key == NULL) + return; + + if (len != sizeof(*params)) { + wpa_printf(MSG_DEBUG, "Invalid set_key request"); + return; + } + + params = buf; + + res = iface->driver->set_key(iface->ifname, iface->drv_priv, + params->alg, + params->addr, params->key_idx, + params->set_tx, + params->seq_len ? params->seq : NULL, + params->seq_len, + params->key_len ? params->key : NULL, + params->key_len); + wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res); +} + + +static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + struct wpa_driver_capa capa; + + if (iface->drv_priv == NULL) + goto fail; + + if (iface->driver->get_capa == NULL || + iface->driver->get_capa(iface->drv_priv, &capa) < 0) + goto fail; + + sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from, + sizeof(*from)); + return; + +fail: + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); +} + + +static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct wpa_priv_interface *iface = ctx; + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = (u8 *) src_addr; + io[0].iov_len = ETH_ALEN; + io[1].iov_base = (u8 *) buf; + io[1].iov_len = len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + msg.msg_name = &iface->l2_addr; + msg.msg_namelen = sizeof(iface->l2_addr); + + if (sendmsg(iface->fd, &msg, 0) < 0) { + perror("sendmsg(l2 rx)"); + } +} + + +static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface, + struct sockaddr_un *from, + void *buf, size_t len) +{ + int *reg_cmd = buf; + u8 own_addr[ETH_ALEN]; + int res; + u16 proto; + + if (len != 2 * sizeof(int)) { + wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu", + (unsigned long) len); + return; + } + + proto = reg_cmd[0]; + if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { + wpa_printf(MSG_DEBUG, "Refused l2_packet connection for " + "ethertype 0x%x", proto); + return; + } + + if (iface->l2) { + wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " + "instance"); + l2_packet_deinit(iface->l2); + iface->l2 = NULL; + } + + os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr)); + + iface->l2 = l2_packet_init(iface->ifname, NULL, proto, + wpa_priv_l2_rx, iface, reg_cmd[1]); + if (iface->l2 == NULL) { + wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet " + "instance for protocol %d", proto); + return; + } + + if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) { + wpa_printf(MSG_DEBUG, "Failed to get own address from " + "l2_packet"); + l2_packet_deinit(iface->l2); + iface->l2 = NULL; + return; + } + + res = sendto(iface->fd, own_addr, ETH_ALEN, 0, + (struct sockaddr *) from, sizeof(*from)); + wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res); +} + + +static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->l2) { + l2_packet_deinit(iface->l2); + iface->l2 = NULL; + } +} + + +static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface, + struct sockaddr_un *from) +{ + if (iface->l2) + l2_packet_notify_auth_start(iface->l2); +} + + +static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface, + struct sockaddr_un *from, + void *buf, size_t len) +{ + u8 *dst_addr; + u16 proto; + int res; + + if (iface->l2 == NULL) + return; + + if (len < ETH_ALEN + 2) { + wpa_printf(MSG_DEBUG, "Too short L2 send packet (len=%lu)", + (unsigned long) len); + return; + } + + dst_addr = buf; + os_memcpy(&proto, buf + ETH_ALEN, 2); + + if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { + wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype " + "0x%x", proto); + return; + } + + res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2, + len - ETH_ALEN - 2); + wpa_printf(MSG_DEBUG, "L2 send: res=%d", res); +} + + +static void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface, + char *buf) +{ + if (iface->drv_priv == NULL || iface->driver->set_country == NULL || + *buf == '\0') + return; + + iface->driver->set_country(iface->drv_priv, buf); +} + + +static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_priv_interface *iface = eloop_ctx; + char buf[2000], *pos; + void *cmd_buf; + size_t cmd_len; + int res, cmd; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, + &fromlen); + if (res < 0) { + perror("recvfrom"); + return; + } + + if (res < (int) sizeof(int)) { + wpa_printf(MSG_DEBUG, "Too short command (len=%d)", res); + return; + } + + os_memcpy(&cmd, buf, sizeof(int)); + wpa_printf(MSG_DEBUG, "Command %d for interface %s", + cmd, iface->ifname); + cmd_buf = &buf[sizeof(int)]; + cmd_len = res - sizeof(int); + + switch (cmd) { + case PRIVSEP_CMD_REGISTER: + wpa_priv_cmd_register(iface, &from); + break; + case PRIVSEP_CMD_UNREGISTER: + wpa_priv_cmd_unregister(iface, &from); + break; + case PRIVSEP_CMD_SCAN: + wpa_priv_cmd_scan(iface, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_GET_SCAN_RESULTS: + wpa_priv_cmd_get_scan_results(iface, &from); + break; + case PRIVSEP_CMD_ASSOCIATE: + wpa_priv_cmd_associate(iface, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_GET_BSSID: + wpa_priv_cmd_get_bssid(iface, &from); + break; + case PRIVSEP_CMD_GET_SSID: + wpa_priv_cmd_get_ssid(iface, &from); + break; + case PRIVSEP_CMD_SET_KEY: + wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_GET_CAPA: + wpa_priv_cmd_get_capa(iface, &from); + break; + case PRIVSEP_CMD_L2_REGISTER: + wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_L2_UNREGISTER: + wpa_priv_cmd_l2_unregister(iface, &from); + break; + case PRIVSEP_CMD_L2_NOTIFY_AUTH_START: + wpa_priv_cmd_l2_notify_auth_start(iface, &from); + break; + case PRIVSEP_CMD_L2_SEND: + wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len); + break; + case PRIVSEP_CMD_SET_COUNTRY: + pos = cmd_buf; + if (pos + cmd_len >= buf + sizeof(buf)) + break; + pos[cmd_len] = '\0'; + wpa_priv_cmd_set_country(iface, pos); + break; + } +} + + +static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) +{ + if (iface->drv_priv && iface->driver->deinit) + iface->driver->deinit(iface->drv_priv); + + if (iface->fd >= 0) { + eloop_unregister_read_sock(iface->fd); + close(iface->fd); + unlink(iface->sock_name); + } + + if (iface->l2) + l2_packet_deinit(iface->l2); + + os_free(iface->ifname); + os_free(iface->driver_name); + os_free(iface->sock_name); + os_free(iface); +} + + +extern struct wpa_driver_ops *wpa_drivers[]; + +static struct wpa_priv_interface * +wpa_priv_interface_init(const char *dir, const char *params) +{ + struct wpa_priv_interface *iface; + char *pos; + size_t len; + struct sockaddr_un addr; + int i; + + pos = os_strchr(params, ':'); + if (pos == NULL) + return NULL; + + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) + return NULL; + iface->fd = -1; + + len = pos - params; + iface->driver_name = os_malloc(len + 1); + if (iface->driver_name == NULL) { + wpa_priv_interface_deinit(iface); + return NULL; + } + os_memcpy(iface->driver_name, params, len); + iface->driver_name[len] = '\0'; + + for (i = 0; wpa_drivers[i]; i++) { + if (os_strcmp(iface->driver_name, + wpa_drivers[i]->name) == 0) { + iface->driver = wpa_drivers[i]; + break; + } + } + if (iface->driver == NULL) { + wpa_printf(MSG_ERROR, "Unsupported driver '%s'", + iface->driver_name); + wpa_priv_interface_deinit(iface); + return NULL; + } + + pos++; + iface->ifname = os_strdup(pos); + if (iface->ifname == NULL) { + wpa_priv_interface_deinit(iface); + return NULL; + } + + len = os_strlen(dir) + 1 + os_strlen(iface->ifname); + iface->sock_name = os_malloc(len + 1); + if (iface->sock_name == NULL) { + wpa_priv_interface_deinit(iface); + return NULL; + } + + os_snprintf(iface->sock_name, len + 1, "%s/%s", dir, iface->ifname); + if (os_strlen(iface->sock_name) >= sizeof(addr.sun_path)) { + wpa_priv_interface_deinit(iface); + return NULL; + } + + iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (iface->fd < 0) { + perror("socket(PF_UNIX)"); + wpa_priv_interface_deinit(iface); + return NULL; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, iface->sock_name, sizeof(addr.sun_path)); + + if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(iface->fd, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "Socket exists, but does not " + "allow connections - assuming it was " + "leftover from forced program termination"); + if (unlink(iface->sock_name) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + iface->sock_name); + goto fail; + } + if (bind(iface->fd, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "socket '%s'", iface->sock_name); + } else { + wpa_printf(MSG_INFO, "Socket exists and seems to be " + "in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", iface->sock_name); + goto fail; + } + } + + if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { + perror("chmod"); + goto fail; + } + + eloop_register_read_sock(iface->fd, wpa_priv_receive, iface, NULL); + + return iface; + +fail: + wpa_priv_interface_deinit(iface); + return NULL; +} + + +static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event, + const void *data, size_t data_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &event; + io[0].iov_len = sizeof(event); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &iface->drv_addr; + msg.msg_namelen = sizeof(iface->drv_addr); + + if (sendmsg(iface->fd, &msg, 0) < 0) { + perror("sendmsg(wpas_socket)"); + return -1; + } + + return 0; +} + + +static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event, + union wpa_event_data *data) +{ + size_t buflen = 3 * sizeof(int); + u8 *buf, *pos; + int len; + + if (data) { + buflen += data->assoc_info.req_ies_len + + data->assoc_info.resp_ies_len + + data->assoc_info.beacon_ies_len; + } + + buf = os_malloc(buflen); + if (buf == NULL) + return; + + pos = buf; + + if (data && data->assoc_info.req_ies) { + len = data->assoc_info.req_ies_len; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, data->assoc_info.req_ies, len); + pos += len; + } else { + len = 0; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + } + + if (data && data->assoc_info.resp_ies) { + len = data->assoc_info.resp_ies_len; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, data->assoc_info.resp_ies, len); + pos += len; + } else { + len = 0; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + } + + if (data && data->assoc_info.beacon_ies) { + len = data->assoc_info.beacon_ies_len; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, data->assoc_info.beacon_ies, len); + pos += len; + } else { + len = 0; + os_memcpy(pos, &len, sizeof(int)); + pos += sizeof(int); + } + + wpa_priv_send_event(iface, event, buf, buflen); + + os_free(buf); +} + + +static void wpa_priv_send_interface_status(struct wpa_priv_interface *iface, + union wpa_event_data *data) +{ + int ievent; + size_t len, maxlen; + u8 *buf; + char *ifname; + + if (data == NULL) + return; + + ievent = data->interface_status.ievent; + maxlen = sizeof(data->interface_status.ifname); + ifname = data->interface_status.ifname; + for (len = 0; len < maxlen && ifname[len]; len++) + ; + + buf = os_malloc(sizeof(int) + len); + if (buf == NULL) + return; + + os_memcpy(buf, &ievent, sizeof(int)); + os_memcpy(buf + sizeof(int), ifname, len); + + wpa_priv_send_event(iface, PRIVSEP_EVENT_INTERFACE_STATUS, + buf, sizeof(int) + len); + + os_free(buf); + +} + + +static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface, + union wpa_event_data *data) +{ + size_t len; + u8 *buf, *pos; + + if (data == NULL || data->ft_ies.ies == NULL) + return; + + len = sizeof(int) + ETH_ALEN + data->ft_ies.ies_len; + buf = os_malloc(len); + if (buf == NULL) + return; + + pos = buf; + os_memcpy(pos, &data->ft_ies.ft_action, sizeof(int)); + pos += sizeof(int); + os_memcpy(pos, data->ft_ies.target_ap, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, data->ft_ies.ies, data->ft_ies.ies_len); + + wpa_priv_send_event(iface, PRIVSEP_EVENT_FT_RESPONSE, buf, len); + + os_free(buf); + +} + + +void wpa_supplicant_event(void *ctx, wpa_event_type event, + union wpa_event_data *data) +{ + struct wpa_priv_interface *iface = ctx; + + wpa_printf(MSG_DEBUG, "%s - event=%d", __func__, event); + + if (!iface->wpas_registered) { + wpa_printf(MSG_DEBUG, "Driver event received, but " + "wpa_supplicant not registered"); + return; + } + + switch (event) { + case EVENT_ASSOC: + wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOC, data); + break; + case EVENT_DISASSOC: + wpa_priv_send_event(iface, PRIVSEP_EVENT_DISASSOC, NULL, 0); + break; + case EVENT_ASSOCINFO: + if (data == NULL) + return; + wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOCINFO, data); + break; + case EVENT_MICHAEL_MIC_FAILURE: + if (data == NULL) + return; + wpa_priv_send_event(iface, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE, + &data->michael_mic_failure.unicast, + sizeof(int)); + break; + case EVENT_SCAN_RESULTS: + wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL, + 0); + break; + case EVENT_INTERFACE_STATUS: + wpa_priv_send_interface_status(iface, data); + break; + case EVENT_PMKID_CANDIDATE: + if (data == NULL) + return; + wpa_priv_send_event(iface, PRIVSEP_EVENT_PMKID_CANDIDATE, + &data->pmkid_candidate, + sizeof(struct pmkid_candidate)); + break; + case EVENT_STKSTART: + if (data == NULL) + return; + wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART, + &data->stkstart.peer, ETH_ALEN); + break; + case EVENT_FT_RESPONSE: + wpa_priv_send_ft_response(iface, data); + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO", + event); + break; + } +} + + +void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_priv_interface *iface = ctx; + struct msghdr msg; + struct iovec io[3]; + int event = PRIVSEP_EVENT_RX_EAPOL; + + wpa_printf(MSG_DEBUG, "RX EAPOL from driver"); + io[0].iov_base = &event; + io[0].iov_len = sizeof(event); + io[1].iov_base = (u8 *) src_addr; + io[1].iov_len = ETH_ALEN; + io[2].iov_base = (u8 *) buf; + io[2].iov_len = len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + msg.msg_name = &iface->drv_addr; + msg.msg_namelen = sizeof(iface->drv_addr); + + if (sendmsg(iface->fd, &msg, 0) < 0) + perror("sendmsg(wpas_socket)"); +} + + +static void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx) +{ + wpa_printf(MSG_DEBUG, "wpa_priv termination requested"); + eloop_terminate(); +} + + +static void wpa_priv_fd_workaround(void) +{ +#ifdef __linux__ + int s, i; + /* When started from pcmcia-cs scripts, wpa_supplicant might start with + * fd 0, 1, and 2 closed. This will cause some issues because many + * places in wpa_supplicant are still printing out to stdout. As a + * workaround, make sure that fd's 0, 1, and 2 are not used for other + * sockets. */ + for (i = 0; i < 3; i++) { + s = open("/dev/null", O_RDWR); + if (s > 2) { + close(s); + break; + } + } +#endif /* __linux__ */ +} + + +static void usage(void) +{ + printf("wpa_priv v" VERSION_STR "\n" + "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and " + "contributors\n" + "\n" + "usage:\n" + " wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> " + "[driver:ifname ...]\n"); +} + + +extern int wpa_debug_level; + +int main(int argc, char *argv[]) +{ + int c, i; + int ret = -1; + char *pid_file = NULL; + int daemonize = 0; + char *ctrl_dir = "/var/run/wpa_priv"; + struct wpa_priv_interface *interfaces = NULL, *iface; + + if (os_program_init()) + return -1; + + wpa_priv_fd_workaround(); + + for (;;) { + c = getopt(argc, argv, "Bc:dP:"); + if (c < 0) + break; + switch (c) { + case 'B': + daemonize++; + break; + case 'c': + ctrl_dir = optarg; + break; + case 'd': + wpa_debug_level--; + break; + case 'P': + pid_file = os_rel2abs_path(optarg); + break; + default: + usage(); + goto out; + } + } + + if (optind >= argc) { + usage(); + goto out; + } + + wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir); + + if (eloop_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + goto out; + } + + for (i = optind; i < argc; i++) { + wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]); + iface = wpa_priv_interface_init(ctrl_dir, argv[i]); + if (iface == NULL) + goto out; + iface->next = interfaces; + interfaces = iface; + } + + if (daemonize && os_daemonize(pid_file)) + goto out; + + eloop_register_signal_terminate(wpa_priv_terminate, NULL); + eloop_run(); + + ret = 0; + +out: + iface = interfaces; + while (iface) { + struct wpa_priv_interface *prev = iface; + iface = iface->next; + wpa_priv_interface_deinit(prev); + } + + eloop_destroy(); + + os_daemonize_terminate(pid_file); + os_free(pid_file); + os_program_deinit(); + + return ret; +} diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c new file mode 100644 index 0000000..9295651 --- /dev/null +++ b/wpa_supplicant/wpa_supplicant.c @@ -0,0 +1,2825 @@ +/* + * WPA Supplicant + * Copyright (c) 2003-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 file implements functions for registering and unregistering + * %wpa_supplicant interfaces. In addition, this file contains number of + * functions for managing network connections. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/random.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "eap_peer/eap.h" +#include "eap_server/eap_methods.h" +#include "rsn_supp/wpa.h" +#include "eloop.h" +#include "config.h" +#include "l2_packet/l2_packet.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" +#include "common/version.h" +#include "rsn_supp/preauth.h" +#include "rsn_supp/pmksa_cache.h" +#include "common/wpa_ctrl.h" +#include "mlme.h" +#include "common/ieee802_11_defs.h" +#include "p2p/p2p.h" +#include "blacklist.h" +#include "wpas_glue.h" +#include "wps_supplicant.h" +#include "ibss_rsn.h" +#include "sme.h" +#include "ap.h" +#include "p2p_supplicant.h" +#include "notify.h" +#include "bgscan.h" +#include "bss.h" +#include "scan.h" + +const char *wpa_supplicant_version = +"wpa_supplicant v" VERSION_STR "\n" +"Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> and contributors"; + +const char *wpa_supplicant_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n" +#ifdef EAP_TLS_OPENSSL +"\nThis product includes software developed by the OpenSSL Project\n" +"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n" +#endif /* EAP_TLS_OPENSSL */ +; + +#ifndef CONFIG_NO_STDOUT_DEBUG +/* Long text divided into parts in order to fit in C89 strings size limits. */ +const char *wpa_supplicant_full_license1 = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n"; +const char *wpa_supplicant_full_license2 = +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n"; +const char *wpa_supplicant_full_license3 = +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n"; +const char *wpa_supplicant_full_license4 = +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"; +const char *wpa_supplicant_full_license5 = +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; +#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[]; + +/* Configure default/group WEP keys for static WEP */ +int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + int i, set = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i] == 0) + continue; + + set = 1; + wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL, + i, i == ssid->wep_tx_keyidx, NULL, 0, + ssid->wep_key[i], ssid->wep_key_len[i]); + } + + return set; +} + + +static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + u8 key[32]; + size_t keylen; + enum wpa_alg alg; + u8 seq[6] = { 0 }; + + /* IBSS/WPA-None uses only one key (Group) for both receiving and + * sending unicast and multicast packets. */ + + if (ssid->mode != WPAS_MODE_IBSS) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not " + "IBSS/ad-hoc) for WPA-None", ssid->mode); + return -1; + } + + if (!ssid->psk_set) { + wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for " + "WPA-None"); + return -1; + } + + switch (wpa_s->group_cipher) { + case WPA_CIPHER_CCMP: + os_memcpy(key, ssid->psk, 16); + keylen = 16; + alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_TKIP: + /* WPA-None uses the same Michael MIC key for both TX and RX */ + os_memcpy(key, ssid->psk, 16 + 8); + os_memcpy(key + 16 + 8, ssid->psk + 16, 8); + keylen = 32; + alg = WPA_ALG_TKIP; + break; + default: + wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for " + "WPA-None", wpa_s->group_cipher); + return -1; + } + + /* TODO: should actually remember the previously used seq#, both for TX + * and RX from each STA.. */ + + return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen); +} + + +static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + const u8 *bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.", + MAC2STR(bssid)); + wpa_blacklist_add(wpa_s, bssid); + wpa_sm_notify_disassoc(wpa_s->wpa); + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_s->reassociate = 1; + + /* + * If we timed out, the AP or the local radio may be busy. + * So, wait a second until scanning again. + */ + wpa_supplicant_req_scan(wpa_s, 1, 0); +} + + +/** + * wpa_supplicant_req_auth_timeout - Schedule a timeout for authentication + * @wpa_s: Pointer to wpa_supplicant data + * @sec: Number of seconds after which to time out authentication + * @usec: Number of microseconds after which to time out authentication + * + * This function is used to schedule a timeout for the current authentication + * attempt. + */ +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 && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " + "%d usec", sec, usec); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL); +} + + +/** + * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to cancel authentication timeout scheduled with + * wpa_supplicant_req_auth_timeout() and it is called when authentication has + * been completed. + */ +void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + wpa_blacklist_del(wpa_s, wpa_s->bssid); +} + + +/** + * wpa_supplicant_initiate_eapol - Configure EAPOL state machine + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to configure EAPOL state machine based on the selected + * authentication mode. + */ +void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) +{ +#ifdef IEEE8021X_EAPOL + struct eapol_config eapol_conf; + struct wpa_ssid *ssid = wpa_s->current_ssid; + +#ifdef CONFIG_IBSS_RSN + if (ssid->mode == WPAS_MODE_IBSS && + wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && + wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) { + /* + * RSN IBSS authentication is per-STA and we can disable the + * per-BSSID EAPOL authentication. + */ + eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE); + return; + } +#endif /* CONFIG_IBSS_RSN */ + + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE); + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) + eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); + else + eapol_sm_notify_portControl(wpa_s->eapol, Auto); + + os_memset(&eapol_conf, 0, sizeof(eapol_conf)); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + eapol_conf.accept_802_1x_keys = 1; + eapol_conf.required_keys = 0; + if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) { + eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST; + } + if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) { + eapol_conf.required_keys |= + EAPOL_REQUIRE_KEY_BROADCAST; + } + + if (wpa_s->conf && (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.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_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); +#endif /* IEEE8021X_EAPOL */ +} + + +/** + * wpa_supplicant_set_non_wpa_policy - Set WPA parameters to non-WPA mode + * @wpa_s: Pointer to wpa_supplicant data + * @ssid: Configuration data for the network + * + * This function is used to configure WPA state machine and related parameters + * to a mode where WPA is not enabled. This is called as part of the + * authentication configuration when the selected network does not use WPA. + */ +void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int i; + + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) + wpa_s->key_mgmt = WPA_KEY_MGMT_WPS; + else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else + wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; + wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_s->group_cipher = WPA_CIPHER_NONE; + wpa_s->mgmt_group_cipher = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i] > 5) { + wpa_s->pairwise_cipher = WPA_CIPHER_WEP104; + wpa_s->group_cipher = WPA_CIPHER_WEP104; + break; + } else if (ssid->wep_key_len[i] > 0) { + wpa_s->pairwise_cipher = WPA_CIPHER_WEP40; + wpa_s->group_cipher = WPA_CIPHER_WEP40; + break; + } + } + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, + wpa_s->pairwise_cipher); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); +#ifdef CONFIG_IEEE80211W + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, + wpa_s->mgmt_group_cipher); +#endif /* CONFIG_IEEE80211W */ + + pmksa_cache_clear_current(wpa_s->wpa); +} + + +static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) +{ + bgscan_deinit(wpa_s); + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + wpa_sm_set_scard_ctx(wpa_s->wpa, NULL); + eapol_sm_register_scard_ctx(wpa_s->eapol, NULL); + l2_packet_deinit(wpa_s->l2); + wpa_s->l2 = NULL; + if (wpa_s->l2_br) { + l2_packet_deinit(wpa_s->l2_br); + wpa_s->l2_br = NULL; + } + + if (wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + if (wpa_s->conf != NULL) { + struct wpa_ssid *ssid; + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_free(wpa_s->conf); + wpa_s->conf = NULL; + } + + os_free(wpa_s->confname); + wpa_s->confname = NULL; + + wpa_sm_set_eapol(wpa_s->wpa, NULL); + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + + rsn_preauth_deinit(wpa_s->wpa); + +#ifdef CONFIG_TDLS + wpa_tdls_deinit(wpa_s->wpa); +#endif /* CONFIG_TDLS */ + + pmksa_candidate_free(wpa_s->wpa); + wpa_sm_deinit(wpa_s->wpa); + wpa_s->wpa = NULL; + wpa_blacklist_clear(wpa_s); + + wpa_bss_deinit(wpa_s); + + wpa_supplicant_cancel_scan(wpa_s); + wpa_supplicant_cancel_auth_timeout(wpa_s); + + ieee80211_sta_deinit(wpa_s); + + wpas_wps_deinit(wpa_s); + + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = NULL; + +#ifdef CONFIG_IBSS_RSN + ibss_rsn_deinit(wpa_s->ibss_rsn); + wpa_s->ibss_rsn = NULL; +#endif /* CONFIG_IBSS_RSN */ + + sme_deinit(wpa_s); + +#ifdef CONFIG_AP + wpa_supplicant_ap_deinit(wpa_s); +#endif /* CONFIG_AP */ + +#ifdef CONFIG_P2P + wpas_p2p_deinit(wpa_s); +#endif /* CONFIG_P2P */ + + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; +} + + +/** + * wpa_clear_keys - Clear keys configured for the driver + * @wpa_s: Pointer to wpa_supplicant data + * @addr: Previously used BSSID or %NULL if not available + * + * This function clears the encryption keys that has been previously configured + * for the driver. + */ +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; + } + + /* 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) { + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, + 0); + /* MLME-SETPROTECTION.request(None) */ + wpa_drv_mlme_setprotection( + wpa_s, addr, + MLME_SETPROTECTION_PROTECT_TYPE_NONE, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + } + wpa_s->keys_cleared = 1; +} + + +/** + * wpa_supplicant_state_txt - Get the connection state name as a text string + * @state: State (wpa_state; WPA_*) + * Returns: The state name as a printable text string + */ +const char * wpa_supplicant_state_txt(enum wpa_states state) +{ + switch (state) { + case WPA_DISCONNECTED: + return "DISCONNECTED"; + case WPA_INACTIVE: + return "INACTIVE"; + case WPA_INTERFACE_DISABLED: + return "INTERFACE_DISABLED"; + case WPA_SCANNING: + return "SCANNING"; + case WPA_AUTHENTICATING: + return "AUTHENTICATING"; + case WPA_ASSOCIATING: + return "ASSOCIATING"; + case WPA_ASSOCIATED: + return "ASSOCIATED"; + case WPA_4WAY_HANDSHAKE: + return "4WAY_HANDSHAKE"; + case WPA_GROUP_HANDSHAKE: + return "GROUP_HANDSHAKE"; + case WPA_COMPLETED: + return "COMPLETED"; + default: + return "UNKNOWN"; + } +} + + +#ifdef CONFIG_BGSCAN + +static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->current_ssid == wpa_s->bgscan_ssid) + return; + + bgscan_deinit(wpa_s); + if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) { + if (bgscan_init(wpa_s, wpa_s->current_ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " + "bgscan"); + /* + * Live without bgscan; it is only used as a roaming + * optimization, so the initial connection is not + * affected. + */ + } else + wpa_s->bgscan_ssid = wpa_s->current_ssid; + } else + wpa_s->bgscan_ssid = NULL; +} + + +static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan_ssid != NULL) { + bgscan_deinit(wpa_s); + wpa_s->bgscan_ssid = NULL; + } +} + +#endif /* CONFIG_BGSCAN */ + + +/** + * wpa_supplicant_set_state - Set current connection state + * @wpa_s: Pointer to wpa_supplicant data + * @state: The new connection state + * + * This function is called whenever the connection state changes, e.g., + * association is completed for WPA/WPA2 4-Way Handshake is started. + */ +void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, + enum wpa_states state) +{ + enum wpa_states old_state = wpa_s->wpa_state; + + wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s", + wpa_supplicant_state_txt(wpa_s->wpa_state), + wpa_supplicant_state_txt(state)); + + if (state != WPA_SCANNING) + wpa_supplicant_notify_scanning(wpa_s, 0); + + if (state == WPA_COMPLETED && wpa_s->new_connection) { +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) + struct wpa_ssid *ssid = wpa_s->current_ssid; + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " + MACSTR " completed %s [id=%d id_str=%s]", + MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ? + "(reauth)" : "(auth)", + ssid ? ssid->id : -1, + ssid && ssid->id_str ? ssid->id_str : ""); +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + wpa_s->new_connection = 0; + wpa_s->reassociated_connection = 1; + wpa_drv_set_operstate(wpa_s, 1); +#ifndef IEEE8021X_EAPOL + wpa_drv_set_supp_port(wpa_s, 1); +#endif /* IEEE8021X_EAPOL */ + wpa_s->after_wps = 0; +#ifdef CONFIG_P2P + wpas_p2p_completed(wpa_s); +#endif /* CONFIG_P2P */ + } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || + state == WPA_ASSOCIATED) { + wpa_s->new_connection = 1; + wpa_drv_set_operstate(wpa_s, 0); +#ifndef IEEE8021X_EAPOL + wpa_drv_set_supp_port(wpa_s, 0); +#endif /* IEEE8021X_EAPOL */ + } + wpa_s->wpa_state = state; + +#ifdef CONFIG_BGSCAN + if (state == WPA_COMPLETED) + wpa_supplicant_start_bgscan(wpa_s); + else + wpa_supplicant_stop_bgscan(wpa_s); +#endif /* CONFIG_BGSCAN */ + + if (wpa_s->wpa_state != old_state) { + wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); + + if (wpa_s->wpa_state == WPA_COMPLETED || + old_state == WPA_COMPLETED) + wpas_notify_auth_changed(wpa_s); + } +} + + +void wpa_supplicant_terminate_proc(struct wpa_global *global) +{ + int pending = 0; +#ifdef CONFIG_WPS + struct wpa_supplicant *wpa_s = global->ifaces; + while (wpa_s) { + if (wpas_wps_terminate_pending(wpa_s) == 1) + pending = 1; + wpa_s = wpa_s->next; + } +#endif /* CONFIG_WPS */ + if (pending) + return; + eloop_terminate(); +} + + +static void wpa_supplicant_terminate(int sig, void *signal_ctx) +{ + struct wpa_global *global = signal_ctx; + struct wpa_supplicant *wpa_s; + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING "- signal %d " + "received", sig); + } + wpa_supplicant_terminate_proc(global); +} + + +void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) +{ + enum wpa_states old_state = wpa_s->wpa_state; + + wpa_s->pairwise_cipher = 0; + wpa_s->group_cipher = 0; + wpa_s->mgmt_group_cipher = 0; + wpa_s->key_mgmt = 0; + if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) + wpa_s->wpa_state = WPA_DISCONNECTED; + + if (wpa_s->wpa_state != old_state) + wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); +} + + +/** + * wpa_supplicant_reload_configuration - Reload configuration data + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success or -1 if configuration parsing failed + * + * This function can be used to request that the configuration data is reloaded + * (e.g., after configuration file change). This function is reloading + * configuration only for one interface, so this may need to be called multiple + * times if %wpa_supplicant is controlling multiple interfaces and all + * interfaces need reconfiguration. + */ +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) +{ + struct wpa_config *conf; + struct wpa_ssid *old_ssid; + int reconf_ctrl; + int old_ap_scan; + + if (wpa_s->confname == NULL) + return -1; + conf = wpa_config_read(wpa_s->confname); + if (conf == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration " + "file '%s' - exiting", wpa_s->confname); + return -1; + } + conf->changed_parameters = (unsigned int) -1; + + reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface + || (conf->ctrl_interface && wpa_s->conf->ctrl_interface && + os_strcmp(conf->ctrl_interface, + wpa_s->conf->ctrl_interface) != 0); + + if (reconf_ctrl && wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + + eapol_sm_invalidate_cached_session(wpa_s->eapol); + old_ssid = wpa_s->current_ssid; + wpa_s->current_ssid = NULL; + if (old_ssid != wpa_s->current_ssid) + wpas_notify_network_changed(wpa_s); + + /* + * TODO: should notify EAPOL SM about changes in opensc_engine_path, + * pkcs11_engine_path, pkcs11_module_path. + */ + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + /* + * Clear forced success to clear EAP state for next + * authentication. + */ + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + } + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_sm_set_config(wpa_s->wpa, NULL); + wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth); + rsn_preauth_deinit(wpa_s->wpa); + + old_ap_scan = wpa_s->conf->ap_scan; + wpa_config_free(wpa_s->conf); + wpa_s->conf = conf; + if (old_ap_scan != wpa_s->conf->ap_scan) + wpas_notify_ap_scan_changed(wpa_s); + + if (reconf_ctrl) + wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); + + wpa_supplicant_update_config(wpa_s); + + wpa_supplicant_clear_status(wpa_s); + if (wpa_supplicant_enabled_networks(wpa_s->conf)) { + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); + return 0; +} + + +static void wpa_supplicant_reconfig(int sig, void *signal_ctx) +{ + struct wpa_global *global = signal_ctx; + struct wpa_supplicant *wpa_s; + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring", + sig); + if (wpa_supplicant_reload_configuration(wpa_s) < 0) { + wpa_supplicant_terminate_proc(global); + } + } +} + + +enum wpa_cipher cipher_suite2driver(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_TKIP: + default: + return CIPHER_TKIP; + } +} + + +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) +{ + int ret = wpa_sm_parse_own_wpa_ie(wpa_s->wpa, ie); + if (ret) { + if (ret == -2) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE " + "from association info"); + } + return -1; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set " + "cipher suites"); + if (!(ie->group_cipher & ssid->group_cipher)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group " + "cipher 0x%x (mask 0x%x) - reject", + ie->group_cipher, ssid->group_cipher); + return -1; + } + if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise " + "cipher 0x%x (mask 0x%x) - reject", + ie->pairwise_cipher, ssid->pairwise_cipher); + return -1; + } + if (!(ie->key_mgmt & ssid->key_mgmt)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key " + "management 0x%x (mask 0x%x) - reject", + ie->key_mgmt, ssid->key_mgmt); + return -1; + } + +#ifdef CONFIG_IEEE80211W + if (!(ie->capabilities & WPA_CAPABILITY_MFPC) && + ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP " + "that does not support management frame protection - " + "reject"); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_supplicant_set_suites - Set authentication and encryption parameters + * @wpa_s: Pointer to wpa_supplicant data + * @bss: Scan results for the selected BSS, or %NULL if not available + * @ssid: Configuration data for the selected network + * @wpa_ie: Buffer for the WPA/RSN IE + * @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the + * used buffer length in case the functions returns success. + * Returns: 0 on success or -1 on failure + * + * This function is used to configure authentication and encryption parameters + * based on the network configuration and scan result for the selected BSS (if + * available). + */ +int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + u8 *wpa_ie, size_t *wpa_ie_len) +{ + struct wpa_ie_data ie; + int sel, proto; + const u8 *bss_wpa, *bss_rsn; + + if (bss) { + bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + } else + bss_wpa = bss_rsn = NULL; + + if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) && + wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && + (ie.group_cipher & ssid->group_cipher) && + (ie.pairwise_cipher & ssid->pairwise_cipher) && + (ie.key_mgmt & ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); + proto = WPA_PROTO_RSN; + } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) && + wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 && + (ie.group_cipher & ssid->group_cipher) && + (ie.pairwise_cipher & ssid->pairwise_cipher) && + (ie.key_mgmt & ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); + proto = WPA_PROTO_WPA; + } else if (bss) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); + return -1; + } else { + if (ssid->proto & WPA_PROTO_RSN) + proto = WPA_PROTO_RSN; + else + proto = WPA_PROTO_WPA; + if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) { + os_memset(&ie, 0, sizeof(ie)); + ie.group_cipher = ssid->group_cipher; + ie.pairwise_cipher = ssid->pairwise_cipher; + ie.key_mgmt = ssid->key_mgmt; +#ifdef CONFIG_IEEE80211W + ie.mgmt_group_cipher = + ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ? + WPA_CIPHER_AES_128_CMAC : 0; +#endif /* CONFIG_IEEE80211W */ + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites " + "based on configuration"); + } else + proto = ie.proto; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d " + "pairwise %d key_mgmt %d proto %d", + ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto); +#ifdef CONFIG_IEEE80211W + if (ssid->ieee80211w) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d", + ie.mgmt_group_cipher); + } +#endif /* CONFIG_IEEE80211W */ + + 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)); + + if (bss || !wpa_s->ap_ies_from_associnfo) { + if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, + bss_wpa ? 2 + bss_wpa[1] : 0) || + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, + bss_rsn ? 2 + bss_rsn[1] : 0)) + return -1; + } + + sel = ie.group_cipher & ssid->group_cipher; + if (sel & WPA_CIPHER_CCMP) { + wpa_s->group_cipher = WPA_CIPHER_CCMP; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); + } else if (sel & WPA_CIPHER_TKIP) { + wpa_s->group_cipher = WPA_CIPHER_TKIP; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP"); + } else if (sel & WPA_CIPHER_WEP104) { + wpa_s->group_cipher = WPA_CIPHER_WEP104; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104"); + } else if (sel & WPA_CIPHER_WEP40) { + wpa_s->group_cipher = WPA_CIPHER_WEP40; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40"); + } else { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group " + "cipher"); + return -1; + } + + sel = ie.pairwise_cipher & ssid->pairwise_cipher; + if (sel & WPA_CIPHER_CCMP) { + wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); + } else if (sel & WPA_CIPHER_TKIP) { + wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP"); + } else if (sel & WPA_CIPHER_NONE) { + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE"); + } else { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise " + "cipher"); + return -1; + } + + sel = ie.key_mgmt & ssid->key_mgmt; + if (0) { +#ifdef CONFIG_IEEE80211R + } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); + } else if (sel & WPA_KEY_MGMT_FT_PSK) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT 802.1X with SHA256"); + } else if (sel & WPA_KEY_MGMT_PSK_SHA256) { + wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT PSK with SHA256"); +#endif /* CONFIG_IEEE80211W */ + } else if (sel & WPA_KEY_MGMT_IEEE8021X) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X"); + } else if (sel & WPA_KEY_MGMT_PSK) { + wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK"); + } 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"); + } else { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " + "authenticated key management type"); + return -1; + } + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, + wpa_s->pairwise_cipher); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); + +#ifdef CONFIG_IEEE80211W + sel = ie.mgmt_group_cipher; + if (ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION || + !(ie.capabilities & WPA_CAPABILITY_MFPC)) + sel = 0; + if (sel & WPA_CIPHER_AES_128_CMAC) { + 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 { + wpa_s->mgmt_group_cipher = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); + } + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, + wpa_s->mgmt_group_cipher); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, ssid->ieee80211w); +#endif /* CONFIG_IEEE80211W */ + + if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE"); + return -1; + } + + if (ssid->key_mgmt & + (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256)) + wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN); + else + wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); + + return 0; +} + + +/** + * wpa_supplicant_associate - Request association + * @wpa_s: Pointer to wpa_supplicant data + * @bss: Scan results for the selected BSS, or %NULL if not available + * @ssid: Configuration data for the selected network + * + * This function is used to request %wpa_supplicant to associate with a BSS. + */ +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; + struct wpa_driver_capa capa; + int assoc_failed = 0; + struct wpa_ssid *old_ssid; + +#ifdef CONFIG_IBSS_RSN + ibss_rsn_deinit(wpa_s->ibss_rsn); + wpa_s->ibss_rsn = NULL; +#endif /* CONFIG_IBSS_RSN */ + + if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { +#ifdef CONFIG_AP + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) { + wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP " + "mode"); + return; + } + wpa_supplicant_create_ap(wpa_s, ssid); + wpa_s->current_bss = bss; +#else /* CONFIG_AP */ + wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in " + "the build"); +#endif /* CONFIG_AP */ + return; + } + +#ifdef CONFIG_TDLS + if (bss) + wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1), + bss->ie_len); +#endif /* CONFIG_TDLS */ + + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + ssid->mode == IEEE80211_MODE_INFRA) { + sme_authenticate(wpa_s, bss, ssid); + return; + } + + os_memset(¶ms, 0, sizeof(params)); + wpa_s->reassociate = 0; + if (bss) { +#ifdef CONFIG_IEEE80211R + const u8 *ie, *md = NULL; +#endif /* CONFIG_IEEE80211R */ + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR + " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq); + bssid_changed = !is_zero_ether_addr(wpa_s->bssid); + os_memset(wpa_s->bssid, 0, ETH_ALEN); + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); +#ifdef CONFIG_IEEE80211R + ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); + if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) + md = ie + 2; + wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); + if (md) { + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(wpa_s->wpa, ie); + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPS + } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) && + wpa_s->conf->ap_scan == 2 && + (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + /* Use ap_scan==1 style network selection to find the network + */ + wpa_s->scan_req = 2; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return; +#endif /* CONFIG_WPS */ + } else { + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + } + wpa_supplicant_cancel_scan(wpa_s); + + /* Starting new association, so clear the possibly used WPA IE from the + * previous association. */ + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + +#ifdef IEEE8021X_EAPOL + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if (ssid->leap) { + if (ssid->non_leap == 0) + algs = WPA_AUTH_ALG_LEAP; + else + algs |= WPA_AUTH_ALG_LEAP; + } + } +#endif /* IEEE8021X_EAPOL */ + wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); + if (ssid->auth_alg) { + algs = ssid->auth_alg; + wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " + "0x%x", algs); + } + + if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || + wpa_bss_get_ie(bss, WLAN_EID_RSN)) && + (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_PSK_SHA256))) { + int try_opportunistic; + try_opportunistic = ssid->proactive_key_caching && + (ssid->proto & WPA_PROTO_RSN); + if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, + wpa_s->current_ssid, + try_opportunistic) == 0) + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + wpa_ie_len = sizeof(wpa_ie); + if (wpa_supplicant_set_suites(wpa_s, bss, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " + "key management and encryption suites"); + return; + } + } else if (ssid->key_mgmt & + (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256)) { + wpa_ie_len = sizeof(wpa_ie); + if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " + "key management and encryption suites (no " + "scan results)"); + return; + } +#ifdef CONFIG_WPS + } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + struct wpabuf *wps_ie; + wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); + if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) { + wpa_ie_len = wpabuf_len(wps_ie); + os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len); + } else + wpa_ie_len = 0; + wpabuf_free(wps_ie); + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY)) + params.wps = WPS_MODE_PRIVACY; + else + params.wps = WPS_MODE_OPEN; +#endif /* CONFIG_WPS */ + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_ie_len = 0; + } + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) { + u8 *pos; + size_t len; + int res; + int p2p_group; + p2p_group = wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE; + pos = wpa_ie + wpa_ie_len; + len = sizeof(wpa_ie) - wpa_ie_len; + res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, p2p_group); + if (res >= 0) + wpa_ie_len += res; + } + + wpa_s->cross_connect_disallowed = 0; + if (bss) { + struct wpabuf *p2p; + p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + if (p2p) { + wpa_s->cross_connect_disallowed = + p2p_get_cross_connect_disallowed(p2p); + wpabuf_free(p2p); + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross " + "connection", + wpa_s->cross_connect_disallowed ? + "disallows" : "allows"); + } + } +#endif /* CONFIG_P2P */ + + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); + use_crypt = 1; + cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher); + cipher_group = cipher_suite2driver(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) + use_crypt = 0; + if (wpa_set_wep_keys(wpa_s, ssid)) { + use_crypt = 1; + wep_keys_set = 1; + } + } + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) + use_crypt = 0; + +#ifdef IEEE8021X_EAPOL + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 && + !wep_keys_set) { + use_crypt = 0; + } else { + /* 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; + } + } +#endif /* IEEE8021X_EAPOL */ + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* Set the key before (and later after) association */ + wpa_supplicant_set_wpa_none_key(wpa_s, ssid); + } + + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); + if (bss) { + params.bssid = bss->bssid; + params.ssid = bss->ssid; + params.ssid_len = bss->ssid_len; + params.freq = bss->freq; + } else { + params.ssid = ssid->ssid; + params.ssid_len = ssid->ssid_len; + } + if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 && + params.freq == 0) + params.freq = ssid->frequency; /* Initial channel for IBSS */ + 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.auth_alg = algs; + params.mode = ssid->mode; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) + params.wep_key[i] = ssid->wep_key[i]; + params.wep_key_len[i] = ssid->wep_key_len[i]; + } + 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.passphrase = ssid->passphrase; + if (ssid->psk_set) + params.psk = ssid->psk; + } + + params.drop_unencrypted = use_crypt; + +#ifdef CONFIG_IEEE80211W + params.mgmt_frame_protection = ssid->ieee80211w; + if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION && bss) { + const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + struct wpa_ie_data ie; + if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 && + ie.capabilities & + (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports " + "MFP: require MFP"); + params.mgmt_frame_protection = + MGMT_FRAME_PROTECTION_REQUIRED; + } + } +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) + params.p2p = 1; +#endif /* CONFIG_P2P */ + + if (wpa_s->parent->set_sta_uapsd) + params.uapsd = wpa_s->parent->sta_uapsd; + else + params.uapsd = -1; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + ret = ieee80211_sta_associate(wpa_s, ¶ms); + else + ret = wpa_drv_associate(wpa_s, ¶ms); + if (ret < 0) { + wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " + "failed"); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) { + /* + * The driver is known to mean what is saying, so we + * can stop right here; the association will not + * succeed. + */ + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + return; + } + /* try to continue anyway; new association will be tried again + * after timeout */ + assoc_failed = 1; + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* Set the key after the association just in case association + * cleared the previously configured key. */ + wpa_supplicant_set_wpa_none_key(wpa_s, ssid); + /* No need to timeout authentication since there is no key + * management. */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); +#ifdef CONFIG_IBSS_RSN + } else if (ssid->mode == WPAS_MODE_IBSS && + wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && + wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) { + /* + * RSN IBSS authentication is per-STA and we can disable the + * per-BSSID authentication. + */ + wpa_supplicant_cancel_auth_timeout(wpa_s); +#endif /* CONFIG_IBSS_RSN */ + } else { + /* Timeout for IEEE 802.11 authentication and association */ + int timeout = 60; + + if (assoc_failed) { + /* give IBSS a bit more time */ + timeout = ssid->mode == WPAS_MODE_IBSS ? 10 : 5; + } else if (wpa_s->conf->ap_scan == 1) { + /* give IBSS a bit more time */ + timeout = ssid->mode == WPAS_MODE_IBSS ? 20 : 10; + } + wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); + } + + if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 && + capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) { + /* Set static WEP keys again */ + wpa_set_wep_keys(wpa_s, ssid); + } + + if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) { + /* + * Do not allow EAP session resumption between different + * network configurations. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } + old_ssid = wpa_s->current_ssid; + wpa_s->current_ssid = ssid; + wpa_s->current_bss = bss; + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); + wpa_supplicant_initiate_eapol(wpa_s); + if (old_ssid != wpa_s->current_ssid) + wpas_notify_network_changed(wpa_s); +} + + +static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + struct wpa_ssid *old_ssid; + + wpa_clear_keys(wpa_s, addr); + wpa_supplicant_mark_disassoc(wpa_s); + old_ssid = wpa_s->current_ssid; + wpa_s->current_ssid = NULL; + wpa_s->current_bss = NULL; + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + if (old_ssid != wpa_s->current_ssid) + wpas_notify_network_changed(wpa_s); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); +} + + +/** + * wpa_supplicant_disassociate - Disassociate the current connection + * @wpa_s: Pointer to wpa_supplicant data + * @reason_code: IEEE 802.11 reason code for the disassociate frame + * + * This function is used to request %wpa_supplicant to disassociate with the + * current AP. + */ +void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, + int reason_code) +{ + u8 *addr = NULL; + + if (!is_zero_ether_addr(wpa_s->bssid)) { + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + ieee80211_sta_disassociate(wpa_s, reason_code); + else + wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code); + addr = wpa_s->bssid; + } + + wpa_supplicant_clear_connection(wpa_s, addr); +} + + +/** + * wpa_supplicant_deauthenticate - Deauthenticate the current connection + * @wpa_s: Pointer to wpa_supplicant data + * @reason_code: IEEE 802.11 reason code for the deauthenticate frame + * + * This function is used to request %wpa_supplicant to deauthenticate from the + * current AP. + */ +void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, + int reason_code) +{ + u8 *addr = NULL; + + if (!is_zero_ether_addr(wpa_s->bssid)) { + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + ieee80211_sta_deauthenticate(wpa_s, reason_code); + else + wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, + reason_code); + addr = wpa_s->bssid; + } + + wpa_supplicant_clear_connection(wpa_s, addr); +} + + +/** + * wpa_supplicant_enable_network - Mark a configured network as enabled + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network or %NULL + * + * Enables the specified network or all networks if no network specified. + */ +void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_ssid *other_ssid; + int was_disabled; + + if (ssid == NULL) { + for (other_ssid = wpa_s->conf->ssid; other_ssid; + other_ssid = other_ssid->next) { + if (other_ssid->disabled == 2) + continue; /* do not change persistent P2P group + * data */ + if (other_ssid == wpa_s->current_ssid && + other_ssid->disabled) + wpa_s->reassociate = 1; + + was_disabled = other_ssid->disabled; + + other_ssid->disabled = 0; + + if (was_disabled != other_ssid->disabled) + wpas_notify_network_enabled_changed( + wpa_s, other_ssid); + } + if (wpa_s->reassociate) + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (ssid->disabled && ssid->disabled != 2) { + if (wpa_s->current_ssid == NULL) { + /* + * Try to reassociate since there is no current + * configuration and a new network was made available. + */ + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + + was_disabled = ssid->disabled; + + ssid->disabled = 0; + + if (was_disabled != ssid->disabled) + wpas_notify_network_enabled_changed(wpa_s, ssid); + } +} + + +/** + * wpa_supplicant_disable_network - Mark a configured network as disabled + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network or %NULL + * + * Disables the specified network or all networks if no network specified. + */ +void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_ssid *other_ssid; + int was_disabled; + + if (ssid == NULL) { + for (other_ssid = wpa_s->conf->ssid; other_ssid; + other_ssid = other_ssid->next) { + was_disabled = other_ssid->disabled; + if (was_disabled == 2) + continue; /* do not change persistent P2P group + * data */ + + other_ssid->disabled = 1; + + if (was_disabled != other_ssid->disabled) + wpas_notify_network_enabled_changed( + wpa_s, other_ssid); + } + if (wpa_s->current_ssid) + wpa_supplicant_disassociate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } else if (ssid->disabled != 2) { + if (ssid == wpa_s->current_ssid) + wpa_supplicant_disassociate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + + was_disabled = ssid->disabled; + + ssid->disabled = 1; + + if (was_disabled != ssid->disabled) + wpas_notify_network_enabled_changed(wpa_s, ssid); + } +} + + +/** + * wpa_supplicant_select_network - Attempt association with a network + * @wpa_s: wpa_supplicant structure for a network interface + * @ssid: wpa_ssid structure for a configured network or %NULL for any network + */ +void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + + struct wpa_ssid *other_ssid; + + if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) + wpa_supplicant_disassociate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + + /* + * Mark all other networks disabled or mark all networks enabled if no + * network specified. + */ + for (other_ssid = wpa_s->conf->ssid; other_ssid; + other_ssid = other_ssid->next) { + int was_disabled = other_ssid->disabled; + if (was_disabled == 2) + continue; /* do not change persistent P2P group data */ + + other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0; + + if (was_disabled != other_ssid->disabled) + wpas_notify_network_enabled_changed(wpa_s, other_ssid); + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + if (ssid) + wpas_notify_network_selected(wpa_s, ssid); +} + + +/** + * 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 + * Returns: 0 if succeed or -1 if ap_scan has an invalid value + * + */ +int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan) +{ + + int old_ap_scan; + + if (ap_scan < 0 || ap_scan > 2) + return -1; + + old_ap_scan = wpa_s->conf->ap_scan; + wpa_s->conf->ap_scan = ap_scan; + + if (old_ap_scan != wpa_s->conf->ap_scan) + wpas_notify_ap_scan_changed(wpa_s); + + return 0; +} + + +/** + * wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age + * @wpa_s: wpa_supplicant structure for a network interface + * @expire_age: Expiration age in seconds + * Returns: 0 if succeed or -1 if expire_age has an invalid value + * + */ +int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s, + unsigned int bss_expire_age) +{ + if (bss_expire_age < 10) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u", + bss_expire_age); + return -1; + } + wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec", + bss_expire_age); + wpa_s->conf->bss_expiration_age = bss_expire_age; + + return 0; +} + + +/** + * wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count + * @wpa_s: wpa_supplicant structure for a network interface + * @expire_count: number of scans after which an unseen BSS is reclaimed + * Returns: 0 if succeed or -1 if expire_count has an invalid value + * + */ +int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s, + unsigned int bss_expire_count) +{ + if (bss_expire_count < 1) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u", + bss_expire_count); + return -1; + } + wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u", + bss_expire_count); + wpa_s->conf->bss_expiration_scan_count = bss_expire_count; + + return 0; +} + + +/** + * wpa_supplicant_set_debug_params - Set global debug params + * @global: wpa_global structure + * @debug_level: debug level + * @debug_timestamp: determines if show timestamp in debug data + * @debug_show_keys: determines if show keys in debug data + * Returns: 0 if succeed or -1 if debug_level has wrong value + */ +int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, + int debug_timestamp, int debug_show_keys) +{ + + int old_level, old_timestamp, old_show_keys; + + /* check for allowed debuglevels */ + if (debug_level != MSG_EXCESSIVE && + debug_level != MSG_MSGDUMP && + debug_level != MSG_DEBUG && + debug_level != MSG_INFO && + debug_level != MSG_WARNING && + debug_level != MSG_ERROR) + return -1; + + old_level = wpa_debug_level; + old_timestamp = wpa_debug_timestamp; + old_show_keys = wpa_debug_show_keys; + + wpa_debug_level = debug_level; + wpa_debug_timestamp = debug_timestamp ? 1 : 0; + wpa_debug_show_keys = debug_show_keys ? 1 : 0; + + if (wpa_debug_level != old_level) + wpas_notify_debug_level_changed(global); + if (wpa_debug_timestamp != old_timestamp) + wpas_notify_debug_timestamp_changed(global); + if (wpa_debug_show_keys != old_show_keys) + wpas_notify_debug_show_keys_changed(global); + + return 0; +} + + +/** + * wpa_supplicant_get_ssid - Get a pointer to the current network structure + * @wpa_s: Pointer to wpa_supplicant data + * Returns: A pointer to the current network structure or %NULL on failure + */ +struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *entry; + u8 ssid[MAX_SSID_LEN]; + int res; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + int wired; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { + if (ieee80211_sta_get_ssid(wpa_s, ssid, &ssid_len)) { + wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from " + "MLME"); + return NULL; + } + } else { + res = wpa_drv_get_ssid(wpa_s, ssid); + if (res < 0) { + wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from " + "driver"); + return NULL; + } + ssid_len = res; + } + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); + else if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from " + "driver"); + return NULL; + } + + wired = wpa_s->conf->ap_scan == 0 && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED); + + entry = wpa_s->conf->ssid; + while (entry) { + if (!entry->disabled && + ((ssid_len == entry->ssid_len && + os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) + return entry; +#ifdef CONFIG_WPS + if (!entry->disabled && + (entry->key_mgmt & WPA_KEY_MGMT_WPS) && + (entry->ssid == NULL || entry->ssid_len == 0) && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) + return entry; +#endif /* CONFIG_WPS */ + entry = entry->next; + } + + return NULL; +} + + +static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, + const char *name) +{ + int i; + size_t len; + const char *pos, *driver = name; + + if (wpa_s == NULL) + return -1; + + if (wpa_drivers[0] == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into " + "wpa_supplicant"); + return -1; + } + + if (name == NULL) { + /* default to first driver in the list */ + wpa_s->driver = wpa_drivers[0]; + wpa_s->global_drv_priv = wpa_s->global->drv_priv[0]; + return 0; + } + + do { + pos = os_strchr(driver, ','); + if (pos) + len = pos - driver; + else + len = os_strlen(driver); + + for (i = 0; wpa_drivers[i]; i++) { + if (os_strlen(wpa_drivers[i]->name) == len && + os_strncmp(driver, wpa_drivers[i]->name, len) == + 0) { + wpa_s->driver = wpa_drivers[i]; + wpa_s->global_drv_priv = + wpa_s->global->drv_priv[i]; + return 0; + } + } + + driver = pos + 1; + } while (pos); + + wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name); + return -1; +} + + +/** + * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant + * @ctx: Context pointer (wpa_s); this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @src_addr: Source address of the EAPOL frame + * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header) + * @len: Length of the EAPOL data + * + * This function is called for each received EAPOL frame. Most driver + * interfaces rely on more generic OS mechanism for receiving frames through + * l2_packet, but if such a mechanism is not available, the driver wrapper may + * take care of received EAPOL frames and deliver them to the core supplicant + * code by calling this function. + */ +void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); + wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); + + if (wpa_s->wpa_state < WPA_ASSOCIATED) { + /* + * There is possible race condition between receiving the + * association event and the EAPOL frame since they are coming + * through different paths from the driver. In order to avoid + * issues in trying to process the EAPOL frame before receiving + * association information, lets queue it for processing until + * the association event is received. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing " + "of received EAPOL frame"); + 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_memcpy(wpa_s->pending_eapol_rx_src, src_addr, + ETH_ALEN); + } + return; + } + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len); + return; + } +#endif /* CONFIG_AP */ + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since " + "no key management is configured"); + return; + } + + if (wpa_s->eapol_received == 0 && + (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) || + !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->wpa_state != WPA_COMPLETED) && + (wpa_s->current_ssid == NULL || + wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) { + /* Timeout for completing IEEE 802.1X and WPA authentication */ + wpa_supplicant_req_auth_timeout( + wpa_s, + (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) ? + 70 : 10, 0); + } + wpa_s->eapol_received++; + + if (wpa_s->countermeasures) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped " + "EAPOL packet"); + return; + } + +#ifdef CONFIG_IBSS_RSN + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_IBSS) { + ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len); + return; + } +#endif /* CONFIG_IBSS_RSN */ + + /* Source address of the incoming EAPOL frame could be compared to the + * current BSSID. However, it is possible that a centralized + * Authenticator could be using another MAC address than the BSSID of + * an AP, so just allow any address to be used for now. The replies are + * still sent to the current BSSID (if available), though. */ + + os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN); + if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) && + eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0) + return; + wpa_drv_poll(wpa_s); + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len); + else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + /* + * Set portValid = TRUE here since we are going to skip 4-way + * handshake processing which would normally set portValid. We + * need this to allow the EAPOL state machines to be completed + * without going through EAPOL-Key handshake. + */ + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + } +} + + +/** + * wpa_supplicant_driver_init - Initialize driver interface parameters + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function is called to initialize driver interface parameters. + * wpa_drv_init() must have been called before this function to initialize the + * driver interface. + */ +int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) +{ + static int interface_count = 0; + + if (wpa_s->driver->send_eapol) { + const u8 *addr = wpa_drv_get_mac_addr(wpa_s); + if (addr) + os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); + } else if (!(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { + wpa_s->l2 = l2_packet_init(wpa_s->ifname, + wpa_drv_get_mac_addr(wpa_s), + ETH_P_EAPOL, + wpa_supplicant_rx_eapol, wpa_s, 0); + if (wpa_s->l2 == NULL) + return -1; + } else { + const u8 *addr = wpa_drv_get_mac_addr(wpa_s); + if (addr) + os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); + } + + if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address"); + return -1; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, + MAC2STR(wpa_s->own_addr)); + + if (wpa_s->bridge_ifname[0]) { + wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge " + "interface '%s'", wpa_s->bridge_ifname); + wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname, + wpa_s->own_addr, + ETH_P_EAPOL, + wpa_supplicant_rx_eapol, wpa_s, + 0); + if (wpa_s->l2_br == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet " + "connection for the bridge interface '%s'", + wpa_s->bridge_ifname); + return -1; + } + } + + wpa_clear_keys(wpa_s, NULL); + + /* Make sure that TKIP countermeasures are not left enabled (could + * happen if wpa_supplicant is killed during countermeasures. */ + wpa_drv_set_countermeasures(wpa_s, 0); + + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver"); + wpa_drv_flush_pmkid(wpa_s); + + wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; + if (wpa_supplicant_enabled_networks(wpa_s->conf)) { + wpa_supplicant_req_scan(wpa_s, interface_count, 100000); + interface_count++; + } else + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + + return 0; +} + + +static int wpa_supplicant_daemon(const char *pid_file) +{ + wpa_printf(MSG_DEBUG, "Daemonize.."); + return os_daemonize(pid_file); +} + + +static struct wpa_supplicant * wpa_supplicant_alloc(void) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = os_zalloc(sizeof(*wpa_s)); + if (wpa_s == NULL) + return NULL; + wpa_s->scan_req = 1; + wpa_s->scan_interval = 5; + wpa_s->new_connection = 1; + wpa_s->parent = wpa_s; + + return wpa_s; +} + + +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 " + "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname, + iface->confname ? iface->confname : "N/A", + iface->driver ? iface->driver : "default", + iface->ctrl_interface ? iface->ctrl_interface : "N/A", + iface->bridge_ifname ? iface->bridge_ifname : "N/A"); + + if (iface->confname) { +#ifdef CONFIG_BACKEND_FILE + wpa_s->confname = os_rel2abs_path(iface->confname); + if (wpa_s->confname == NULL) { + wpa_printf(MSG_ERROR, "Failed to get absolute path " + "for configuration file '%s'.", + iface->confname); + return -1; + } + wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'", + iface->confname, wpa_s->confname); +#else /* CONFIG_BACKEND_FILE */ + wpa_s->confname = os_strdup(iface->confname); +#endif /* CONFIG_BACKEND_FILE */ + wpa_s->conf = wpa_config_read(wpa_s->confname); + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, "Failed to read or parse " + "configuration '%s'.", wpa_s->confname); + return -1; + } + + /* + * Override ctrl_interface and driver_param if set on command + * line. + */ + if (iface->ctrl_interface) { + os_free(wpa_s->conf->ctrl_interface); + wpa_s->conf->ctrl_interface = + os_strdup(iface->ctrl_interface); + } + + if (iface->driver_param) { + os_free(wpa_s->conf->driver_param); + wpa_s->conf->driver_param = + os_strdup(iface->driver_param); + } + } else + wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface, + iface->driver_param); + + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, "\nNo configuration found."); + return -1; + } + + if (iface->ifname == NULL) { + wpa_printf(MSG_ERROR, "\nInterface name is required."); + return -1; + } + if (os_strlen(iface->ifname) >= sizeof(wpa_s->ifname)) { + wpa_printf(MSG_ERROR, "\nToo long interface name '%s'.", + iface->ifname); + return -1; + } + os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname)); + + if (iface->bridge_ifname) { + if (os_strlen(iface->bridge_ifname) >= + sizeof(wpa_s->bridge_ifname)) { + wpa_printf(MSG_ERROR, "\nToo long bridge interface " + "name '%s'.", iface->bridge_ifname); + return -1; + } + os_strlcpy(wpa_s->bridge_ifname, iface->bridge_ifname, + sizeof(wpa_s->bridge_ifname)); + } + + /* RSNA Supplicant Key Management - INITIALIZE */ + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + + /* Initialize driver interface and register driver event handler before + * 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) + 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; + + wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, + wpa_s->bridge_ifname[0] ? wpa_s->bridge_ifname : + NULL); + wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth); + + if (wpa_s->conf->dot11RSNAConfigPMKLifetime && + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, + wpa_s->conf->dot11RSNAConfigPMKLifetime)) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " + "dot11RSNAConfigPMKLifetime"); + return -1; + } + + if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold && + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, + wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " + "dot11RSNAConfigPMKReauthThreshold"); + return -1; + } + + if (wpa_s->conf->dot11RSNAConfigSATimeout && + wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, + wpa_s->conf->dot11RSNAConfigSATimeout)) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " + "dot11RSNAConfigSATimeout"); + return -1; + } + + if (wpa_drv_get_capa(wpa_s, &capa) == 0) { + wpa_s->drv_flags = capa.flags; + if (capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { + if (ieee80211_sta_init(wpa_s)) + return -1; + } + wpa_s->max_scan_ssids = capa.max_scan_ssids; + wpa_s->max_remain_on_chan = capa.max_remain_on_chan; + wpa_s->max_stations = capa.max_stations; + } + if (wpa_s->max_remain_on_chan == 0) + wpa_s->max_remain_on_chan = 1000; + + if (wpa_supplicant_driver_init(wpa_s) < 0) + return -1; + +#ifdef CONFIG_TDLS + if (wpa_tdls_init(wpa_s->wpa)) + return -1; +#endif /* CONFIG_TDLS */ + + if (wpa_s->conf->country[0] && wpa_s->conf->country[1] && + wpa_drv_set_country(wpa_s, wpa_s->conf->country)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country"); + return -1; + } + + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); + + if (wpas_wps_init(wpa_s)) + return -1; + + if (wpa_supplicant_init_eapol(wpa_s) < 0) + return -1; + wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); + + wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); + if (wpa_s->ctrl_iface == NULL) { + wpa_printf(MSG_ERROR, + "Failed to initialize control interface '%s'.\n" + "You may have another wpa_supplicant process " + "already running or the file was\n" + "left by an unclean termination of wpa_supplicant " + "in which case you will need\n" + "to manually remove this file before starting " + "wpa_supplicant again.\n", + wpa_s->conf->ctrl_interface); + return -1; + } + +#ifdef CONFIG_P2P + if (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; + + return 0; +} + + +static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, + int notify) +{ + if (wpa_s->drv_priv) { + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + + wpa_drv_set_countermeasures(wpa_s, 0); + wpa_clear_keys(wpa_s, NULL); + } + + wpa_supplicant_cleanup(wpa_s); + + if (notify) + wpas_notify_iface_removed(wpa_s); + + if (wpa_s->drv_priv) + wpa_drv_deinit(wpa_s); +} + + +/** + * wpa_supplicant_add_iface - Add a new network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @iface: Interface configuration options + * Returns: Pointer to the created interface or %NULL on failure + * + * This function is used to add new network interfaces for %wpa_supplicant. + * This can be called before wpa_supplicant_run() to add interfaces before the + * main event loop has been started. In addition, new interfaces can be added + * dynamically while %wpa_supplicant is already running. This could happen, + * e.g., when a hotplug network adapter is inserted. + */ +struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, + struct wpa_interface *iface) +{ + struct wpa_supplicant *wpa_s; + struct wpa_interface t_iface; + struct wpa_ssid *ssid; + + if (global == NULL || iface == NULL) + return NULL; + + wpa_s = wpa_supplicant_alloc(); + if (wpa_s == NULL) + return NULL; + + wpa_s->global = global; + + t_iface = *iface; + if (global->params.override_driver) { + wpa_printf(MSG_DEBUG, "Override interface parameter: driver " + "('%s' -> '%s')", + iface->driver, global->params.override_driver); + t_iface.driver = global->params.override_driver; + } + if (global->params.override_ctrl_interface) { + wpa_printf(MSG_DEBUG, "Override interface parameter: " + "ctrl_interface ('%s' -> '%s')", + iface->ctrl_interface, + global->params.override_ctrl_interface); + t_iface.ctrl_interface = + global->params.override_ctrl_interface; + } + if (wpa_supplicant_init_iface(wpa_s, &t_iface)) { + wpa_printf(MSG_DEBUG, "Failed to add interface %s", + iface->ifname); + wpa_supplicant_deinit_iface(wpa_s, 0); + os_free(wpa_s); + return NULL; + } + + /* Notify the control interfaces about new iface */ + if (wpas_notify_iface_added(wpa_s)) { + wpa_supplicant_deinit_iface(wpa_s, 1); + os_free(wpa_s); + return NULL; + } + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) + wpas_notify_network_added(wpa_s, ssid); + + wpa_s->next = global->ifaces; + global->ifaces = wpa_s; + + wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname); + + return wpa_s; +} + + +/** + * wpa_supplicant_remove_iface - Remove a network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @wpa_s: Pointer to the network interface to be removed + * Returns: 0 if interface was removed, -1 if interface was not found + * + * This function can be used to dynamically remove network interfaces from + * %wpa_supplicant, e.g., when a hotplug network adapter is ejected. In + * addition, this function is used to remove all remaining interfaces when + * %wpa_supplicant is terminated. + */ +int wpa_supplicant_remove_iface(struct wpa_global *global, + struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *prev; + + /* Remove interface from the global list of interfaces */ + prev = global->ifaces; + if (prev == wpa_s) { + global->ifaces = wpa_s->next; + } else { + while (prev && prev->next != wpa_s) + prev = prev->next; + if (prev == NULL) + return -1; + prev->next = wpa_s->next; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname); + + if (global->p2p_group_formation == wpa_s) + global->p2p_group_formation = NULL; + wpa_supplicant_deinit_iface(wpa_s, 1); + os_free(wpa_s); + + return 0; +} + + +/** + * wpa_supplicant_get_eap_mode - Get the current EAP mode + * @wpa_s: Pointer to the network interface + * Returns: Pointer to the eap mode or the string "UNKNOWN" if not found + */ +const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s) +{ + const char *eapol_method; + + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 && + wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + return "NO-EAP"; + } + + eapol_method = eapol_sm_get_method_name(wpa_s->eapol); + if (eapol_method == NULL) + return "UNKNOWN-EAP"; + + return eapol_method; +} + + +/** + * wpa_supplicant_get_iface - Get a new network interface + * @global: Pointer to global data from wpa_supplicant_init() + * @ifname: Interface name + * Returns: Pointer to the interface or %NULL if not found + */ +struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, + const char *ifname) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, ifname) == 0) + return wpa_s; + } + return NULL; +} + + +#ifndef CONFIG_NO_WPA_MSG +static const char * wpa_supplicant_msg_ifname_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL) + return NULL; + return wpa_s->ifname; +} +#endif /* CONFIG_NO_WPA_MSG */ + + +/** + * wpa_supplicant_init - Initialize %wpa_supplicant + * @params: Parameters for %wpa_supplicant + * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure + * + * This function is used to initialize %wpa_supplicant. After successful + * initialization, the returned data pointer can be used to add and remove + * network interfaces, and eventually, to deinitialize %wpa_supplicant. + */ +struct wpa_global * wpa_supplicant_init(struct wpa_params *params) +{ + struct wpa_global *global; + int ret, i; + + if (params == NULL) + return NULL; + +#ifndef CONFIG_NO_WPA_MSG + wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb); +#endif /* CONFIG_NO_WPA_MSG */ + + wpa_debug_open_file(params->wpa_debug_file_path); + if (params->wpa_debug_syslog) + wpa_debug_open_syslog(); + + ret = eap_register_methods(); + if (ret) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + if (ret == -2) + wpa_printf(MSG_ERROR, "Two or more EAP methods used " + "the same EAP type."); + return NULL; + } + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + dl_list_init(&global->p2p_srv_bonjour); + dl_list_init(&global->p2p_srv_upnp); + global->params.daemonize = params->daemonize; + global->params.wait_for_monitor = params->wait_for_monitor; + global->params.dbus_ctrl_interface = params->dbus_ctrl_interface; + if (params->pid_file) + global->params.pid_file = os_strdup(params->pid_file); + if (params->ctrl_interface) + global->params.ctrl_interface = + os_strdup(params->ctrl_interface); + if (params->override_driver) + global->params.override_driver = + os_strdup(params->override_driver); + if (params->override_ctrl_interface) + global->params.override_ctrl_interface = + os_strdup(params->override_ctrl_interface); + wpa_debug_level = global->params.wpa_debug_level = + params->wpa_debug_level; + wpa_debug_show_keys = global->params.wpa_debug_show_keys = + params->wpa_debug_show_keys; + wpa_debug_timestamp = global->params.wpa_debug_timestamp = + params->wpa_debug_timestamp; + + wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR); + + if (eloop_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + wpa_supplicant_deinit(global); + return NULL; + } + + random_init(); + + global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global); + if (global->ctrl_iface == NULL) { + wpa_supplicant_deinit(global); + return NULL; + } + + if (wpas_notify_supplicant_initialized(global)) { + wpa_supplicant_deinit(global); + return NULL; + } + + for (i = 0; wpa_drivers[i]; i++) + global->drv_count++; + if (global->drv_count == 0) { + wpa_printf(MSG_ERROR, "No drivers enabled"); + wpa_supplicant_deinit(global); + return NULL; + } + global->drv_priv = os_zalloc(global->drv_count * sizeof(void *)); + if (global->drv_priv == NULL) { + wpa_supplicant_deinit(global); + return NULL; + } + for (i = 0; wpa_drivers[i]; i++) { + if (!wpa_drivers[i]->global_init) + continue; + global->drv_priv[i] = wpa_drivers[i]->global_init(); + if (global->drv_priv[i] == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize driver " + "'%s'", wpa_drivers[i]->name); + wpa_supplicant_deinit(global); + return NULL; + } + } + + return global; +} + + +/** + * wpa_supplicant_run - Run the %wpa_supplicant main event loop + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: 0 after successful event loop run, -1 on failure + * + * This function starts the main event loop and continues running as long as + * there are any remaining events. In most cases, this function is running as + * long as the %wpa_supplicant process in still in use. + */ +int wpa_supplicant_run(struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s; + + if (global->params.daemonize && + wpa_supplicant_daemon(global->params.pid_file)) + return -1; + + if (global->params.wait_for_monitor) { + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) + if (wpa_s->ctrl_iface) + wpa_supplicant_ctrl_iface_wait( + wpa_s->ctrl_iface); + } + + eloop_register_signal_terminate(wpa_supplicant_terminate, global); + eloop_register_signal_reconfig(wpa_supplicant_reconfig, global); + + eloop_run(); + + return 0; +} + + +/** + * wpa_supplicant_deinit - Deinitialize %wpa_supplicant + * @global: Pointer to global data from wpa_supplicant_init() + * + * This function is called to deinitialize %wpa_supplicant and to free all + * allocated resources. Remaining network interfaces will also be removed. + */ +void wpa_supplicant_deinit(struct wpa_global *global) +{ + int i; + + if (global == NULL) + return; + +#ifdef CONFIG_P2P + wpas_p2p_deinit_global(global); +#endif /* CONFIG_P2P */ + + while (global->ifaces) + wpa_supplicant_remove_iface(global, global->ifaces); + + if (global->ctrl_iface) + wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface); + + wpas_notify_supplicant_deinitialized(global); + + eap_peer_unregister_methods(); +#ifdef CONFIG_AP + eap_server_unregister_methods(); +#endif /* CONFIG_AP */ + + for (i = 0; wpa_drivers[i] && global->drv_priv; i++) { + if (!global->drv_priv[i]) + continue; + wpa_drivers[i]->global_deinit(global->drv_priv[i]); + } + os_free(global->drv_priv); + + random_deinit(); + + eloop_destroy(); + + if (global->params.pid_file) { + os_daemonize_terminate(global->params.pid_file); + os_free(global->params.pid_file); + } + os_free(global->params.ctrl_interface); + os_free(global->params.override_driver); + os_free(global->params.override_ctrl_interface); + + os_free(global); + wpa_debug_close_syslog(); + wpa_debug_close_file(); +} + + +void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) +{ + if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && + wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + char country[3]; + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = '\0'; + if (wpa_drv_set_country(wpa_s, country) < 0) { + wpa_printf(MSG_ERROR, "Failed to set country code " + "'%s'", country); + } + } + +#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; +} + + +void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features) +{ + size_t i; + + if (hw_features == NULL) + return; + + for (i = 0; i < num_hw_features; i++) { + os_free(hw_features[i].channels); + os_free(hw_features[i].rates); + } + + os_free(hw_features); +} + + +static void add_freq(int *freqs, int *num_freqs, int freq) +{ + int i; + + for (i = 0; i < *num_freqs; i++) { + if (freqs[i] == freq) + return; + } + + freqs[*num_freqs] = freq; + (*num_freqs)++; +} + + +static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss, *cbss; + const int max_freqs = 10; + int *freqs; + int num_freqs = 0; + + freqs = os_zalloc(sizeof(int) * (max_freqs + 1)); + if (freqs == NULL) + return NULL; + + cbss = wpa_s->current_bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss == cbss) + continue; + if (bss->ssid_len == cbss->ssid_len && + os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 && + wpa_blacklist_get(wpa_s, bss->bssid) == NULL) { + add_freq(freqs, &num_freqs, bss->freq); + if (num_freqs == max_freqs) + break; + } + } + + if (num_freqs == 0) { + os_free(freqs); + freqs = NULL; + } + + return freqs; +} + + +void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + int timeout; + int count; + int *freqs = NULL; + + /* + * Add the failed BSSID into the blacklist and speed up next scan + * attempt if there could be other APs that could accept association. + * The current blacklist count indicates how many times we have tried + * connecting to this AP and multiple attempts mean that other APs are + * either not available or has already been tried, so that we can start + * increasing the delay here to avoid constant scanning. + */ + count = wpa_blacklist_add(wpa_s, bssid); + if (count == 1 && wpa_s->current_bss) { + /* + * This BSS was not in the blacklist before. If there is + * another BSS available for the same ESS, we should try that + * next. Otherwise, we may as well try this one once more + * before allowing other, likely worse, ESSes to be considered. + */ + freqs = get_bss_freqs_in_ess(wpa_s); + if (freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS " + "has been seen; try it next"); + wpa_blacklist_add(wpa_s, bssid); + /* + * On the next scan, go through only the known channels + * used in this ESS based on previous scans to speed up + * common load balancing use case. + */ + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = freqs; + } + } + + switch (count) { + case 1: + timeout = 100; + break; + case 2: + timeout = 500; + break; + case 3: + timeout = 1000; + break; + default: + timeout = 5000; + } + + /* + * TODO: if more than one possible AP is available in scan results, + * could try the other ones before requesting a new scan. + */ + wpa_supplicant_req_scan(wpa_s, timeout / 1000, + 1000 * (timeout % 1000)); +} diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf new file mode 100644 index 0000000..b015744 --- /dev/null +++ b/wpa_supplicant/wpa_supplicant.conf @@ -0,0 +1,892 @@ +##### Example wpa_supplicant configuration file ############################### +# +# This file describes configuration file format and lists all available option. +# Please also take a look at simpler configuration examples in 'examples' +# subdirectory. +# +# Empty lines and lines starting with # are ignored + +# NOTE! This file may contain password information and should probably be made +# readable only by root user on multiuser systems. + +# Note: All file paths in this configuration file should use full (absolute, +# not relative to working directory) path in order to allow working directory +# to be changed. This can happen if wpa_supplicant is run in the background. + +# Whether to allow wpa_supplicant to update (overwrite) configuration +# +# This option can be used to allow wpa_supplicant to overwrite configuration +# file whenever configuration is changed (e.g., new network block is added with +# wpa_cli or wpa_gui, or a password is changed). This is required for +# wpa_cli/wpa_gui to be able to store the configuration changes permanently. +# Please note that overwriting configuration file will remove the comments from +# it. +#update_config=1 + +# global configuration (shared by all network blocks) +# +# Parameters for the control interface. If this is specified, wpa_supplicant +# will open a control interface that is available for external programs to +# manage wpa_supplicant. The meaning of this string depends on which control +# interface mechanism is used. For all cases, the existance of this parameter +# in configuration is used to determine whether the control interface is +# enabled. +# +# For UNIX domain sockets (default on Linux and BSD): This is a directory that +# will be created for UNIX domain sockets for listening to requests from +# external programs (CLI/GUI, etc.) for status information and configuration. +# The socket file will be named based on the interface name, so multiple +# wpa_supplicant processes can be run at the same time if more than one +# interface is used. +# /var/run/wpa_supplicant is the recommended directory for sockets and by +# default, wpa_cli will use it when trying to connect with wpa_supplicant. +# +# Access control for the control interface can be configured by setting the +# directory to allow only members of a group to use sockets. This way, it is +# possible to run wpa_supplicant as root (since it needs to change network +# configuration and open raw sockets) and still allow GUI/CLI components to be +# run as non-root users. However, since the control interface can be used to +# change the network configuration, this access needs to be protected in many +# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you +# want to allow non-root users to use the control interface, add a new group +# and change this value to match with that group. Add users that should have +# control interface access to this group. If this variable is commented out or +# not included in the configuration file, group will not be changed from the +# value it got by default when the directory or socket was created. +# +# When configuring both the directory and group, use following format: +# DIR=/var/run/wpa_supplicant GROUP=wheel +# DIR=/var/run/wpa_supplicant GROUP=0 +# (group can be either group name or gid) +# +# For UDP connections (default on Windows): The value will be ignored. This +# variable is just used to select that the control interface is to be created. +# The value can be set to, e.g., udp (ctrl_interface=udp) +# +# For Windows Named Pipe: This value can be used to set the security descriptor +# for controlling access to the control interface. Security descriptor can be +# set using Security Descriptor String Format (see http://msdn.microsoft.com/ +# library/default.asp?url=/library/en-us/secauthz/security/ +# security_descriptor_string_format.asp). The descriptor string needs to be +# prefixed with SDDL=. For example, ctrl_interface=SDDL=D: would set an empty +# DACL (which will reject all connections). See README-Windows.txt for more +# information about SDDL string format. +# +ctrl_interface=/var/run/wpa_supplicant + +# IEEE 802.1X/EAPOL version +# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines +# EAPOL version 2. However, there are many APs that do not handle the new +# version number correctly (they seem to drop the frames completely). In order +# 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). +eapol_version=1 + +# AP scanning/selection +# By default, wpa_supplicant requests driver to perform AP scanning and then +# uses the scan results to select a suitable AP. Another alternative is to +# allow the driver to take care of AP scanning and selection and use +# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association +# information from the driver. +# 1: wpa_supplicant initiates scanning and AP selection; if no APs matching to +# the currently enabled networks are found, a new network (IBSS or AP mode +# operation) may be initialized (if configured) (default) +# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association +# parameters (e.g., WPA IE generation); this mode can also be used with +# non-WPA drivers when using IEEE 802.1X mode; do not try to associate with +# APs (i.e., external program needs to control association). This mode must +# also be used when using wired Ethernet drivers. +# 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, +# the network blocks in the configuration file are tried one by one until +# the driver reports successful association; each network block should have +# explicit security policy (i.e., only one option in the lists) for +# key_mgmt, pairwise, group, proto variables +# When using IBSS or AP mode, ap_scan=2 mode can force the new network to be +# created immediately regardless of scan results. ap_scan=1 mode will first try +# to scan for existing networks and only if no matches with the enabled +# networks are found, a new IBSS or AP mode network is created. +ap_scan=1 + +# EAP fast re-authentication +# By default, fast re-authentication is enabled for all EAP methods that +# support it. This variable can be used to disable fast re-authentication. +# Normally, there is no need to disable this. +fast_reauth=1 + +# OpenSSL Engine support +# These options can be used to load OpenSSL engines. +# The two engines that are supported currently are shown below: +# They are both from the opensc project (http://www.opensc.org/) +# By default no engines are loaded. +# make the opensc engine available +#opensc_engine_path=/usr/lib/opensc/engine_opensc.so +# make the pkcs11 engine available +#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so +# configure the path to the pkcs11 module required by the pkcs11 engine +#pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so + +# Dynamic EAP methods +# If EAP methods were built dynamically as shared object files, they need to be +# loaded here before being used in the network blocks. By default, EAP methods +# are included statically in the build, so these lines are not needed +#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_tls.so +#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so + +# Driver interface parameters +# This field can be used to configure arbitrary driver interace parameters. The +# format is specific to the selected driver interface. This field is not used +# in most cases. +#driver_param="field=value" + +# Country code +# The ISO/IEC alpha2 country code for the country in which this device is +# currently operating. +#country=US + +# Maximum lifetime for PMKSA in seconds; default 43200 +#dot11RSNAConfigPMKLifetime=43200 +# Threshold for reauthentication (percentage of PMK lifetime); default 70 +#dot11RSNAConfigPMKReauthThreshold=70 +# Timeout for security association negotiation in seconds; default 60 +#dot11RSNAConfigSATimeout=60 + +# Wi-Fi Protected Setup (WPS) parameters + +# Universally Unique IDentifier (UUID; see RFC 4122) of the device +# If not configured, UUID will be generated based on the local MAC address. +#uuid=12345678-9abc-def0-1234-56789abcdef0 + +# Device Name +# User-friendly description of device; up to 32 octets encoded in UTF-8 +#device_name=Wireless Client + +# Manufacturer +# The manufacturer of the device (up to 64 ASCII characters) +#manufacturer=Company + +# Model Name +# Model of the device (up to 32 ASCII characters) +#model_name=cmodel + +# Model Number +# Additional device description (up to 32 ASCII characters) +#model_number=123 + +# Serial Number +# Serial number of the device (up to 32 characters) +#serial_number=12345 + +# Primary Device Type +# Used format: <categ>-<OUI>-<subcateg> +# categ = Category as an integer value +# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for +# default WPS OUI +# subcateg = OUI-specific Sub Category as an integer value +# Examples: +# 1-0050F204-1 (Computer / PC) +# 1-0050F204-2 (Computer / Server) +# 5-0050F204-1 (Storage / NAS) +# 6-0050F204-1 (Network Infrastructure / AP) +#device_type=1-0050F204-1 + +# OS Version +# 4-octet operating system version number (hex string) +#os_version=01020300 + +# Config Methods +# List of the supported configuration methods +# Available methods: usba ethernet label display ext_nfc_token int_nfc_token +# nfc_interface push_button keypad virtual_display physical_display +# virtual_push_button physical_push_button +# For WSC 1.0: +#config_methods=label display push_button keypad +# For WSC 2.0: +#config_methods=label virtual_display virtual_push_button keypad + +# Credential processing +# 0 = process received credentials internally (default) +# 1 = do not process received credentials; just pass them over ctrl_iface to +# external program(s) +# 2 = process received credentials internally and pass them over ctrl_iface +# to external program(s) +#wps_cred_processing=0 + +# Maximum number of BSS entries to keep in memory +# Default: 200 +# This can be used to limit memory use on the BSS entries (cached scan +# results). A larger value may be needed in environments that have huge number +# of APs when using ap_scan=1 mode. +#bss_max_count=200 + + +# filter_ssids - SSID-based scan result filtering +# 0 = do not filter scan results (default) +# 1 = only include configured SSIDs in scan results/BSS table +#filter_ssids=0 + + +# network block +# +# Each network (usually AP's sharing the same SSID) is configured as a separate +# block in this configuration file. The network blocks are in preference order +# (the first match is used). +# +# network block fields: +# +# disabled: +# 0 = this network can be used (default) +# 1 = this network block is disabled (can be enabled through ctrl_iface, +# e.g., with wpa_cli or wpa_gui) +# +# id_str: Network identifier string for external scripts. This value is passed +# to external action script through wpa_cli as WPA_ID_STR environment +# variable to make it easier to do network specific configuration. +# +# ssid: SSID (mandatory); either as an ASCII string with double quotation or +# as hex string; network name +# +# scan_ssid: +# 0 = do not scan this SSID with specific Probe Request frames (default) +# 1 = scan with SSID-specific Probe Request frames (this can be used to +# find APs that do not accept broadcast SSID or use multiple SSIDs; +# this will add latency to scanning, so enable this only when needed) +# +# bssid: BSSID (optional); if set, this network block is used only when +# associating with the AP using the configured BSSID +# +# priority: priority group (integer) +# By default, all networks will get same priority group (0). If some of the +# networks are more desirable, this field can be used to change the order in +# which wpa_supplicant goes through the networks when selecting a BSS. The +# priority groups will be iterated in decreasing priority (i.e., the larger the +# priority value, the sooner the network is matched against the scan results). +# Within each priority group, networks will be selected based on security +# policy, signal strength, etc. +# Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are not +# using this priority to select the order for scanning. Instead, they try the +# networks in the order that used in the configuration file. +# +# mode: IEEE 802.11 operation mode +# 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: +# proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not +# both), and psk must also be set. +# +# frequency: Channel frequency in megahertz (MHz) for IBSS, e.g., +# 2412 = IEEE 802.11b/g channel 1. This value is used to configure the initial +# channel for IBSS (adhoc) networks. It is ignored in the infrastructure mode. +# In addition, this value is only used by the station that creates the IBSS. If +# an IBSS network with the configured SSID is already present, the frequency of +# the network will be used instead of this configured value. +# +# scan_freq: List of frequencies to scan +# Space-separated list of frequencies in MHz to scan when searching for this +# BSS. If the subset of channels used by the network is known, this option can +# be used to optimize scanning to not occur on channels that the network does +# not use. Example: scan_freq=2412 2437 2462 +# +# freq_list: Array of allowed frequencies +# Space-separated list of frequencies in MHz to allow for selecting the BSS. If +# set, scan results that do not match any of the specified frequencies are not +# considered when selecting a BSS. +# +# proto: list of accepted protocols +# WPA = WPA/IEEE 802.11i/D3.0 +# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN) +# If not set, this defaults to: WPA RSN +# +# key_mgmt: list of accepted authenticated key management protocols +# WPA-PSK = WPA pre-shared key (this requires 'psk' field) +# WPA-EAP = WPA using EAP authentication +# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically +# generated WEP keys +# NONE = WPA is not used; plaintext or static WEP could be used +# WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms +# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms +# If not set, this defaults to: WPA-PSK WPA-EAP +# +# auth_alg: list of allowed IEEE 802.11 authentication algorithms +# OPEN = Open System authentication (required for WPA/WPA2) +# SHARED = Shared Key authentication (requires static WEP keys) +# LEAP = LEAP/Network EAP (only used with LEAP) +# If not set, automatic selection is used (Open System with LEAP enabled if +# LEAP is allowed as one of the EAP methods). +# +# pairwise: list of accepted pairwise (unicast) ciphers for WPA +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# NONE = Use only Group Keys (deprecated, should not be included if APs support +# pairwise keys) +# If not set, this defaults to: CCMP TKIP +# +# group: list of accepted group (broadcast/multicast) ciphers for WPA +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key +# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11] +# If not set, this defaults to: CCMP TKIP WEP104 WEP40 +# +# psk: WPA preshared key; 256-bit pre-shared key +# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., +# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be +# generated using the passphrase and SSID). ASCII passphrase must be between +# 8 and 63 characters (inclusive). +# This field is not needed, if WPA-EAP is used. +# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys +# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant +# startup and reconfiguration time can be optimized by generating the PSK only +# only when the passphrase or SSID has actually changed. +# +# eapol_flags: IEEE 802.1X/EAPOL options (bit field) +# Dynamic WEP key required for non-WPA mode +# bit0 (1): require dynamically generated unicast WEP key +# bit1 (2): require dynamically generated broadcast WEP key +# (3 = require both keys; default) +# Note: When using wired authentication, eapol_flags must be set to 0 for the +# authentication to be completed successfully. +# +# mixed_cell: This option can be used to configure whether so called mixed +# cells, i.e., networks that use both plaintext and encryption in the same +# SSID, are allowed when selecting a BSS form scan results. +# 0 = disabled (default) +# 1 = enabled +# +# proactive_key_caching: +# Enable/disable opportunistic PMKSA caching for WPA2. +# 0 = disabled (default) +# 1 = enabled +# +# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or +# hex without quotation, e.g., 0102030405) +# wep_tx_keyidx: Default WEP key index (TX) (0..3) +# +# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is +# allowed. This is only used with RSN/WPA2. +# 0 = disabled (default) +# 1 = enabled +#peerkey=1 +# +# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to +# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies. +# +# Following fields are only used with internal EAP implementation. +# eap: space-separated list of accepted EAP methods +# MD5 = EAP-MD5 (unsecure and does not generate keying material -> +# cannot be used with WPA; to be used as a Phase 2 method +# with EAP-PEAP or EAP-TTLS) +# MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# OTP = EAP-OTP (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# GTC = EAP-GTC (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# TLS = EAP-TLS (client and server certificate) +# PEAP = EAP-PEAP (with tunnelled EAP authentication) +# TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2 +# authentication) +# If not set, all compiled in methods are allowed. +# +# identity: Identity string for EAP +# This field is also used to configure user NAI for +# EAP-PSK/PAX/SAKE/GPSK. +# anonymous_identity: Anonymous identity string for EAP (to be used as the +# unencrypted identity with EAP types that support different tunnelled +# identity, e.g., EAP-TTLS) +# password: Password string for EAP. This field can include either the +# plaintext password (using ASCII or hex string) or a NtPasswordHash +# (16-byte MD4 hash of password) in hash:<32 hex digits> format. +# NtPasswordHash can only be used when the password is for MSCHAPv2 or +# MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). +# EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit +# PSK) is also configured using this field. For EAP-GPSK, this is a +# variable length PSK. +# ca_cert: File path to CA certificate file (PEM/DER). This file can have one +# or more trusted CA certificates. If ca_cert and ca_path are not +# included, server certificate will not be verified. This is insecure and +# a trusted CA certificate should always be configured when using +# EAP-TLS/TTLS/PEAP. Full path should be used since working directory may +# change when wpa_supplicant is run in the background. +# +# Alternatively, this can be used to only perform matching of the server +# certificate (SHA-256 hash of the DER encoded X.509 certificate). In +# this case, the possible CA certificates in the server certificate chain +# are ignored and only the server certificate is verified. This is +# configured with the following format: +# hash:://server/sha256/cert_hash_in_hex +# For example: "hash://server/sha256/ +# 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a" +# +# On Windows, trusted CA certificates can be loaded from the system +# certificate store by setting this to cert_store://<name>, e.g., +# ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# ca_path: Directory path for CA certificate files (PEM). This path may +# contain multiple CA certificates in OpenSSL format. Common use for this +# is to point to system trusted CA list which is often installed into +# directory like /etc/ssl/certs. If configured, these certificates are +# added to the list of trusted CAs. ca_cert may also be included in that +# case, but it is not required. +# client_cert: File path to client certificate file (PEM/DER) +# Full path should be used since working directory may change when +# wpa_supplicant is run in the background. +# Alternatively, a named configuration blob can be used by setting this +# to blob://<blob name>. +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read from +# the PKCS#12 file in this case. Full path should be used since working +# directory may change when wpa_supplicant is run in the background. +# Windows certificate store can be used by leaving client_cert out and +# configuring private_key in one of the following formats: +# cert://substring_to_match +# hash://certificate_thumbprint_in_hex +# for example: private_key="hash://63093aa9c47f56ae88334c7b65a4" +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# Alternatively, a named configuration blob can be used by setting this +# to blob://<blob name>. +# private_key_passwd: Password for private key file (if left out, this will be +# asked through control interface) +# dh_file: File path to DH/DSA parameters file (in PEM format) +# This is an optional configuration file for setting parameters for an +# ephemeral DH key exchange. In most cases, the default RSA +# authentication does not use this configuration. However, it is possible +# setup RSA to use ephemeral DH key exchange. In addition, ciphers with +# DSA keys always use ephemeral DH keys. This can be used to achieve +# forward secrecy. If the file is in DSA parameters format, it will be +# automatically converted into DH params. +# subject_match: Substring to be matched against the subject of the +# authentication server certificate. If this string is set, the server +# sertificate is only accepted if it contains this string in the subject. +# The subject string is in following format: +# /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com +# altsubject_match: Semicolon separated string of entries to be matched against +# the alternative subject name of the authentication server certificate. +# If this string is set, the server sertificate is only accepted if it +# contains one of the entries in an alternative subject name extension. +# altSubjectName string is in following format: TYPE:VALUE +# Example: EMAIL:server@example.com +# Example: DNS:server.example.com;DNS:server2.example.com +# Following types are supported: EMAIL, DNS, URI +# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters +# (string with field-value pairs, e.g., "peapver=0" or +# "peapver=1 peaplabel=1") +# 'peapver' can be used to force which PEAP version (0 or 1) is used. +# 'peaplabel=1' can be used to force new label, "client PEAP encryption", +# to be used during key derivation when PEAPv1 or newer. Most existing +# PEAPv1 implementation seem to be using the old label, "client EAP +# encryption", and wpa_supplicant is now using that as the default value. +# Some servers, e.g., Radiator, may require peaplabel=1 configuration to +# interoperate with PEAPv1; see eap_testing.txt for more details. +# 'peap_outer_success=0' can be used to terminate PEAP authentication on +# tunneled EAP-Success. This is required with some RADIUS servers that +# implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., +# Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode) +# include_tls_length=1 can be used to force wpa_supplicant to include +# TLS Message Length field in all TLS messages even if they are not +# fragmented. +# sim_min_num_chal=3 can be used to configure EAP-SIM to require three +# challenges (by default, it accepts 2 or 3) +# result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use +# protected result indication. +# 'crypto_binding' option can be used to control PEAPv0 cryptobinding +# behavior: +# * 0 = do not use cryptobinding (default) +# * 1 = use cryptobinding if server supports it +# * 2 = require cryptobinding +# EAP-WSC (WPS) uses following options: pin=<Device Password> or +# pbc=1. +# phase2: Phase2 (inner authentication with TLS tunnel) parameters +# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or +# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) +# Following certificate/private key fields are used in inner Phase2 +# authentication when using EAP-TTLS or EAP-PEAP. +# ca_cert2: File path to CA certificate file. This file can have one or more +# trusted CA certificates. If ca_cert2 and ca_path2 are not included, +# server certificate will not be verified. This is insecure and a trusted +# CA certificate should always be configured. +# ca_path2: Directory path for CA certificate files (PEM) +# client_cert2: File path to client certificate file +# private_key2: File path to client private key file +# private_key2_passwd: Password for private key file +# dh_file2: File path to DH/DSA parameters file (in PEM format) +# subject_match2: Substring to be matched against the subject of the +# authentication server certificate. +# altsubject_match2: Substring to be matched against the alternative subject +# name of the authentication server certificate. +# +# fragment_size: Maximum EAP fragment size in bytes (default 1398). +# This value limits the fragment size for EAP methods that support +# fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set +# small enough to make the EAP messages fit in MTU of the network +# interface used for EAPOL. The default value is suitable for most +# cases. +# +# EAP-FAST variables: +# pac_file: File path for the PAC entries. wpa_supplicant will need to be able +# to create this file and write updates to it when PAC is being +# provisioned or refreshed. Full path to the file should be used since +# working directory may change when wpa_supplicant is run in the +# background. Alternatively, a named configuration blob can be used by +# setting this to blob://<blob name> +# phase1: fast_provisioning option can be used to enable in-line provisioning +# of EAP-FAST credentials (PAC): +# 0 = disabled, +# 1 = allow unauthenticated provisioning, +# 2 = allow authenticated provisioning, +# 3 = allow both unauthenticated and authenticated provisioning +# fast_max_pac_list_len=<num> option can be used to set the maximum +# number of PAC entries to store in a PAC list (default: 10) +# fast_pac_format=binary option can be used to select binary format for +# storing PAC entries in order to save some space (the default +# text format uses about 2.5 times the size of minimal binary +# format) +# +# wpa_supplicant supports number of "EAP workarounds" to work around +# interoperability issues with incorrectly behaving authentication servers. +# These are enabled by default because some of the issues are present in large +# number of authentication servers. Strict EAP conformance mode can be +# configured by disabling workarounds with eap_workaround=0. + +# Example blocks: + +# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers +network={ + ssid="simple" + psk="very secret passphrase" + priority=5 +} + +# Same as previous, but request SSID-specific scanning (for APs that reject +# broadcast SSID) +network={ + ssid="second ssid" + scan_ssid=1 + psk="very secret passphrase" + priority=2 +} + +# Only WPA-PSK is used. Any valid cipher combination is accepted. +network={ + ssid="example" + proto=WPA + key_mgmt=WPA-PSK + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb + priority=2 +} + +# WPA-Personal(PSK) with TKIP and enforcement for frequent PTK rekeying +network={ + ssid="example" + proto=WPA + key_mgmt=WPA-PSK + pairwise=TKIP + group=TKIP + psk="not so secure passphrase" + wpa_ptk_rekey=600 +} + +# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104 +# or WEP40 as the group cipher will not be accepted. +network={ + ssid="example" + proto=RSN + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + priority=1 +} + +# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel +# (e.g., Radiator) +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=1" + phase2="auth=MSCHAPV2" + priority=10 +} + +# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the +# unencrypted use. Real identity is sent only within an encrypted TLS tunnel. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + priority=2 +} + +# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted +# use. Real identity is sent only within an encrypted TLS tunnel. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MSCHAPV2" +} + +# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner +# authentication. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + # Phase1 / outer authentication + anonymous_identity="anonymous@example.com" + ca_cert="/etc/cert/ca.pem" + # Phase 2 / inner authentication + phase2="autheap=TLS" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" + priority=2 +} + +# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and +# group cipher. +network={ + ssid="example" + bssid=00:11:22:33:44:55 + proto=WPA RSN + key_mgmt=WPA-PSK WPA-EAP + pairwise=CCMP + group=CCMP + psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb +} + +# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP +# and all valid ciphers. +network={ + ssid=00010203 + psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +} + + +# EAP-SIM with a GSM SIM or USIM +network={ + ssid="eap-sim-test" + key_mgmt=WPA-EAP + eap=SIM + pin="1234" + pcsc="" +} + + +# EAP-PSK +network={ + ssid="eap-psk-test" + key_mgmt=WPA-EAP + eap=PSK + anonymous_identity="eap_psk_user" + password=06b4be19da289f475aa46a33cb793029 + identity="eap_psk_user@example.com" +} + + +# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using +# EAP-TLS for authentication and key generation; require both unicast and +# broadcast WEP keys. +network={ + ssid="1x-test" + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} + + +# LEAP with dynamic WEP keys +network={ + ssid="leap-example" + key_mgmt=IEEE8021X + eap=LEAP + identity="user" + password="foobar" +} + +# EAP-IKEv2 using shared secrets for both server and peer authentication +network={ + ssid="ikev2-example" + key_mgmt=WPA-EAP + eap=IKEV2 + identity="user" + password="foobar" +} + +# EAP-FAST with WPA (WPA or WPA2) +network={ + ssid="eap-fast-test" + key_mgmt=WPA-EAP + eap=FAST + anonymous_identity="FAST-000102030405" + identity="username" + password="password" + phase1="fast_provisioning=1" + pac_file="/etc/wpa_supplicant.eap-fast-pac" +} + +network={ + ssid="eap-fast-test" + key_mgmt=WPA-EAP + eap=FAST + anonymous_identity="FAST-000102030405" + identity="username" + password="password" + phase1="fast_provisioning=1" + pac_file="blob://eap-fast-pac" +} + +# Plaintext connection (no WPA, no IEEE 802.1X) +network={ + ssid="plaintext-test" + key_mgmt=NONE +} + + +# Shared WEP key connection (no WPA, no IEEE 802.1X) +network={ + ssid="static-wep-test" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_key2="1234567890123" + wep_tx_keyidx=0 + priority=5 +} + + +# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key +# IEEE 802.11 authentication +network={ + ssid="static-wep-test2" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_key2="1234567890123" + wep_tx_keyidx=0 + priority=5 + auth_alg=SHARED +} + + +# IBSS/ad-hoc network with WPA-None/TKIP. +network={ + ssid="test adhoc" + mode=1 + frequency=2412 + proto=WPA + key_mgmt=WPA-NONE + pairwise=NONE + group=TKIP + psk="secret passphrase" +} + + +# Catch all example that allows more or less all configuration modes +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" +} + +# Example of EAP-TLS with smartcard (openssl engine) +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TLS + proto=RSN + pairwise=CCMP TKIP + group=CCMP TKIP + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + + engine=1 + + # The engine configured here must be available. Look at + # OpenSSL engine support in the global section. + # The key available through the engine must be the private key + # matching the client certificate configured above. + + # use the opensc engine + #engine_id="opensc" + #key_id="45" + + # use the pkcs11 engine + engine_id="pkcs11" + key_id="id_45" + + # Optional PIN configuration; this can be left out and PIN will be + # asked through the control interface + pin="1234" +} + +# Example configuration showing how to use an inlined blob as a CA certificate +# data instead of using external file +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="blob://exampleblob" + priority=20 +} + +blob-base64-exampleblob={ +SGVsbG8gV29ybGQhCg== +} + + +# Wildcard match for SSID (plaintext APs only). This example select any +# open AP regardless of its SSID. +network={ + key_mgmt=NONE +} diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h new file mode 100644 index 0000000..8498666 --- /dev/null +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -0,0 +1,636 @@ +/* + * wpa_supplicant - Internal definitions + * Copyright (c) 2003-2010, 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. + */ + +#ifndef WPA_SUPPLICANT_I_H +#define WPA_SUPPLICANT_I_H + +#include "utils/list.h" +#include "common/defs.h" + +extern const char *wpa_supplicant_version; +extern const char *wpa_supplicant_license; +#ifndef CONFIG_NO_STDOUT_DEBUG +extern const char *wpa_supplicant_full_license1; +extern const char *wpa_supplicant_full_license2; +extern const char *wpa_supplicant_full_license3; +extern const char *wpa_supplicant_full_license4; +extern const char *wpa_supplicant_full_license5; +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +struct wpa_sm; +struct wpa_supplicant; +struct ibss_rsn; +struct scan_info; +struct wpa_bss; +struct wpa_scan_results; +struct hostapd_hw_modes; + +/* + * Forward declarations of private structures used within the ctrl_iface + * backends. Other parts of wpa_supplicant do not have access to data stored in + * these structures. + */ +struct ctrl_iface_priv; +struct ctrl_iface_global_priv; +struct wpas_dbus_priv; + +/** + * struct wpa_interface - Parameters for wpa_supplicant_add_iface() + */ +struct wpa_interface { + /** + * confname - Configuration name (file or profile) name + * + * This can also be %NULL when a configuration file is not used. In + * that case, ctrl_interface must be set to allow the interface to be + * configured. + */ + const char *confname; + + /** + * ctrl_interface - Control interface parameter + * + * If a configuration file is not used, this variable can be used to + * set the ctrl_interface parameter that would have otherwise been read + * from the configuration file. If both confname and ctrl_interface are + * set, ctrl_interface is used to override the value from configuration + * file. + */ + const char *ctrl_interface; + + /** + * driver - Driver interface name, or %NULL to use the default driver + */ + const char *driver; + + /** + * driver_param - Driver interface parameters + * + * If a configuration file is not used, this variable can be used to + * set the driver_param parameters that would have otherwise been read + * from the configuration file. If both confname and driver_param are + * set, driver_param is used to override the value from configuration + * file. + */ + const char *driver_param; + + /** + * ifname - Interface name + */ + const char *ifname; + + /** + * bridge_ifname - Optional bridge interface name + * + * If the driver interface (ifname) is included in a Linux bridge + * device, the bridge interface may need to be used for receiving EAPOL + * frames. This can be enabled by setting this variable to enable + * receiving of EAPOL frames from an additional interface. + */ + const char *bridge_ifname; +}; + +/** + * struct wpa_params - Parameters for wpa_supplicant_init() + */ +struct wpa_params { + /** + * daemonize - Run %wpa_supplicant in the background + */ + int daemonize; + + /** + * wait_for_monitor - Wait for a monitor program before starting + */ + int wait_for_monitor; + + /** + * pid_file - Path to a PID (process ID) file + * + * If this and daemonize are set, process ID of the background process + * will be written to the specified file. + */ + char *pid_file; + + /** + * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO) + */ + int wpa_debug_level; + + /** + * wpa_debug_show_keys - Whether keying material is included in debug + * + * This parameter can be used to allow keying material to be included + * in debug messages. This is a security risk and this option should + * not be enabled in normal configuration. If needed during + * development or while troubleshooting, this option can provide more + * details for figuring out what is happening. + */ + int wpa_debug_show_keys; + + /** + * wpa_debug_timestamp - Whether to include timestamp in debug messages + */ + int wpa_debug_timestamp; + + /** + * ctrl_interface - Global ctrl_iface path/parameter + */ + char *ctrl_interface; + + /** + * dbus_ctrl_interface - Enable the DBus control interface + */ + int dbus_ctrl_interface; + + /** + * wpa_debug_file_path - Path of debug file or %NULL to use stdout + */ + const char *wpa_debug_file_path; + + /** + * wpa_debug_syslog - Enable log output through syslog + */ + int wpa_debug_syslog; + + /** + * override_driver - Optional driver parameter override + * + * This parameter can be used to override the driver parameter in + * dynamic interface addition to force a specific driver wrapper to be + * used instead. + */ + char *override_driver; + + /** + * override_ctrl_interface - Optional ctrl_interface override + * + * This parameter can be used to override the ctrl_interface parameter + * in dynamic interface addition to force a control interface to be + * created. + */ + char *override_ctrl_interface; +}; + +struct p2p_srv_bonjour { + struct dl_list list; + struct wpabuf *query; + struct wpabuf *resp; +}; + +struct p2p_srv_upnp { + struct dl_list list; + u8 version; + char *service; +}; + +/** + * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces + * + * This structure is initialized by calling wpa_supplicant_init() when starting + * %wpa_supplicant. + */ +struct wpa_global { + struct wpa_supplicant *ifaces; + struct wpa_params params; + struct ctrl_iface_global_priv *ctrl_iface; + struct wpas_dbus_priv *dbus; + void **drv_priv; + size_t drv_count; + struct os_time suspend_time; + struct p2p_data *p2p; + struct wpa_supplicant *p2p_group_formation; + u8 p2p_dev_addr[ETH_ALEN]; + 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_client_mlme { +#ifdef CONFIG_CLIENT_MLME + enum { + IEEE80211_DISABLED, IEEE80211_AUTHENTICATE, + IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED, + IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED + } state; + u8 prev_bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + u16 aid; + u16 ap_capab, capab; + u8 *extra_ie; /* to be added to the end of AssocReq */ + size_t extra_ie_len; + u8 *extra_probe_ie; /* to be added to the end of ProbeReq */ + size_t extra_probe_ie_len; + enum wpa_key_mgmt key_mgmt; + + /* The last AssocReq/Resp IEs */ + u8 *assocreq_ies, *assocresp_ies; + size_t assocreq_ies_len, assocresp_ies_len; + + int auth_tries, assoc_tries; + + unsigned int ssid_set:1; + unsigned int bssid_set:1; + unsigned int prev_bssid_set:1; + unsigned int authenticated:1; + unsigned int associated:1; + unsigned int probereq_poll:1; + unsigned int use_protection:1; + unsigned int create_ibss:1; + unsigned int mixed_cell:1; + unsigned int wmm_enabled:1; + + struct os_time last_probe; + + unsigned int auth_algs; /* bitfield of allowed auth algs + * (WPA_AUTH_ALG_*) */ + int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ + int auth_transaction; + + struct os_time ibss_join_req; + u8 *probe_resp; /* ProbeResp template for IBSS */ + size_t probe_resp_len; + u32 supp_rates_bits; + + int wmm_last_param_set; + + int sta_scanning; + int scan_hw_mode_idx; + int scan_channel_idx; + enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; + struct os_time last_scan_completed; + int scan_oper_channel; + int scan_oper_freq; + int scan_oper_phymode; + u8 scan_ssid[32]; + size_t scan_ssid_len; + int scan_skip_11b; + int *scan_freqs; + + struct ieee80211_sta_bss *sta_bss_list; +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE]; + + int cts_protect_erp_frames; + + enum hostapd_hw_mode phymode; /* current mode */ + struct hostapd_hw_modes *modes; + size_t num_modes; + unsigned int hw_modes; /* bitfield of allowed hardware modes; + * (1 << HOSTAPD_MODE_*) */ + int num_curr_rates; + int *curr_rates; + int freq; /* The current frequency in MHz */ + int channel; /* The current IEEE 802.11 channel number */ + +#ifdef CONFIG_IEEE80211R + u8 current_md[6]; + u8 *ft_ies; + size_t ft_ies_len; +#endif /* CONFIG_IEEE80211R */ + + void (*public_action_cb)(void *ctx, const u8 *buf, size_t len, + int freq); + void *public_action_cb_ctx; + +#else /* CONFIG_CLIENT_MLME */ + int dummy; /* to keep MSVC happy */ +#endif /* CONFIG_CLIENT_MLME */ +}; + +/** + * struct wpa_supplicant - Internal data for wpa_supplicant interface + * + * This structure contains the internal data for core wpa_supplicant code. This + * should be only used directly from the core code. However, a pointer to this + * data is used from other files as an arbitrary context pointer in calls to + * core functions. + */ +struct wpa_supplicant { + struct wpa_global *global; + struct wpa_supplicant *parent; + struct wpa_supplicant *next; + struct l2_packet_data *l2; + struct l2_packet_data *l2_br; + unsigned char own_addr[ETH_ALEN]; + char ifname[100]; +#ifdef CONFIG_CTRL_IFACE_DBUS + char *dbus_path; +#endif /* CONFIG_CTRL_IFACE_DBUS */ +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW + char *dbus_new_path; +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ + char bridge_ifname[16]; + + char *confname; + struct wpa_config *conf; + int countermeasures; + os_time_t last_michael_mic_error; + u8 bssid[ETH_ALEN]; + u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this + * field contains the targer BSSID. */ + int reassociate; /* reassociation requested */ + int disconnected; /* all connections disabled; i.e., do no reassociate + * before this has been cleared */ + struct wpa_ssid *current_ssid; + struct wpa_bss *current_bss; + int ap_ies_from_associnfo; + unsigned int assoc_freq; + + /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int mgmt_group_cipher; + + void *drv_priv; /* private data used by driver_ops */ + void *global_drv_priv; + + struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID; + * NULL = not yet initialized (start + * with wildcard SSID) + * WILDCARD_SSID_SCAN = wildcard + * SSID was used in the previous scan + */ +#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1) + + void (*scan_res_handler)(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); + struct dl_list bss; /* struct wpa_bss::list */ + struct dl_list bss_id; /* struct wpa_bss::list_id */ + size_t num_bss; + unsigned int bss_update_idx; + unsigned int bss_next_id; + + struct wpa_driver_ops *driver; + int interface_removed; /* whether the network interface has been + * removed */ + struct wpa_sm *wpa; + struct eapol_sm *eapol; + + struct ctrl_iface_priv *ctrl_iface; + + enum wpa_states wpa_state; + int scanning; + int new_connection; + int reassociated_connection; + + int eapol_received; /* number of EAPOL packets received after the + * previous association event */ + + struct scard_data *scard; + + unsigned char last_eapol_src[ETH_ALEN]; + + int keys_cleared; + + struct wpa_blacklist *blacklist; + + int scan_req; /* manual scan request; this forces a scan even if there + * are no enabled networks in the configuration */ + int scan_runs; /* number of scan runs since WPS was started */ + int *next_scan_freqs; + int scan_interval; /* time in sec between scans to find suitable AP */ + + struct wpa_client_mlme mlme; + unsigned int drv_flags; + int max_scan_ssids; + unsigned int max_remain_on_chan; + unsigned int max_stations; + + int pending_mic_error_report; + int pending_mic_error_pairwise; + int mic_errors_seen; /* Michael MIC errors with the current PTK */ + + struct wps_context *wps; + int wps_success; /* WPS success event received */ + struct wps_er *wps_er; + int blacklist_cleared; + + struct wpabuf *pending_eapol_rx; + struct os_time pending_eapol_rx_time; + u8 pending_eapol_rx_src[ETH_ALEN]; + + struct ibss_rsn *ibss_rsn; + + int set_sta_uapsd; + int sta_uapsd; + int set_ap_uapsd; + int ap_uapsd; + +#ifdef CONFIG_SME + struct { + u8 ssid[32]; + size_t ssid_len; + int freq; + u8 assoc_req_ie[200]; + size_t assoc_req_ie_len; + int mfp; + int ft_used; + u8 mobility_domain[2]; + u8 *ft_ies; + size_t ft_ies_len; + u8 prev_bssid[ETH_ALEN]; + int prev_bssid_set; + int auth_alg; + + int sa_query_count; /* number of pending SA Query requests; + * 0 = no SA Query in progress */ + int sa_query_timed_out; + u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * + * sa_query_count octets of pending + * SA Query transaction identifiers */ + struct os_time sa_query_start; + } sme; +#endif /* CONFIG_SME */ + +#ifdef CONFIG_AP + struct hostapd_iface *ap_iface; + void (*ap_configured_cb)(void *ctx, void *data); + void *ap_configured_cb_ctx; + void *ap_configured_cb_data; +#endif /* CONFIG_AP */ + +#ifdef CONFIG_P2P + struct p2p_go_neg_results *go_params; + int create_p2p_iface; + u8 pending_interface_addr[ETH_ALEN]; + char pending_interface_name[100]; + int pending_interface_type; + int p2p_group_idx; + 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; + int pending_action_without_roc; + unsigned int pending_listen_freq; + unsigned int pending_listen_duration; + enum { + NOT_P2P_GROUP_INTERFACE, + P2P_GROUP_INTERFACE_PENDING, + P2P_GROUP_INTERFACE_GO, + P2P_GROUP_INTERFACE_CLIENT + } p2p_group_interface; + struct p2p_group *p2p_group; + int p2p_long_listen; /* remaining time in long Listen state in ms */ + char p2p_pin[10]; + int p2p_wps_method; + u8 p2p_auth_invite[ETH_ALEN]; + int p2p_sd_over_ctrl_iface; + int p2p_in_provisioning; + int pending_invite_ssid_id; + int show_group_started; + u8 go_dev_addr[ETH_ALEN]; + int pending_pd_before_join; + u8 pending_join_iface_addr[ETH_ALEN]; + u8 pending_join_dev_addr[ETH_ALEN]; + int pending_join_wps_method; + int p2p_join_scan_count; + unsigned int roc_waiting_drv_freq; + int action_tx_wait_time; + int force_long_sd; + + /* + * Whether cross connection is disallowed by the AP to which this + * interface is associated (only valid if there is an association). + */ + int cross_connect_disallowed; + + /* + * Whether this P2P group is configured to use cross connection (only + * valid if this is P2P GO interface). The actual cross connect packet + * forwarding may not be configured depending on the uplink status. + */ + int cross_connect_enabled; + + /* Whether cross connection forwarding is in use at the moment. */ + int cross_connect_in_use; + + /* + * Uplink interface name for cross connection + */ + char cross_connect_uplink[100]; + + enum { + P2P_GROUP_REMOVAL_UNKNOWN, + P2P_GROUP_REMOVAL_REQUESTED, + P2P_GROUP_REMOVAL_IDLE_TIMEOUT, + P2P_GROUP_REMOVAL_UNAVAILABLE + } removal_reason; +#endif /* CONFIG_P2P */ + + struct wpa_ssid *bgscan_ssid; + const struct bgscan_ops *bgscan; + void *bgscan_priv; + + int connect_without_scan; + + int after_wps; + unsigned int wps_freq; + int wps_fragment_size; + int auto_reconnect_disabled; + + /* Channel preferences for AP/P2P GO use */ + int best_24_freq; + int best_5_freq; + int best_overall_freq; +}; + + +/* wpa_supplicant.c */ +int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); + +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); + +const char * wpa_supplicant_state_txt(enum wpa_states state); +int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s); +int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + u8 *wpa_ie, size_t *wpa_ie_len); +void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_ssid *ssid); +void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s); +void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr); +void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, + int sec, int usec); +void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, + enum wpa_states state); +struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s); +const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s); +void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); +void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, + int reason_code); +void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, + int reason_code); + +void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +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, + unsigned int expire_age); +int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s, + unsigned int expire_count); +int wpa_supplicant_set_debug_params(struct wpa_global *global, + int debug_level, int debug_timestamp, + int debug_show_keys); + +void wpa_show_license(void); + +struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, + struct wpa_interface *iface); +int wpa_supplicant_remove_iface(struct wpa_global *global, + struct wpa_supplicant *wpa_s); +struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, + const char *ifname); +struct wpa_global * wpa_supplicant_init(struct wpa_params *params); +int wpa_supplicant_run(struct wpa_global *global); +void wpa_supplicant_deinit(struct wpa_global *global); + +int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +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); +enum wpa_cipher cipher_suite2driver(int cipher); +void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s); +void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s); +void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features); +void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid); + +/* events.c */ +void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); +void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid); + +/* eap_register.c */ +int eap_register_methods(void); + +#endif /* WPA_SUPPLICANT_I_H */ diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c new file mode 100644 index 0000000..2662eec --- /dev/null +++ b/wpa_supplicant/wpas_glue.c @@ -0,0 +1,728 @@ +/* + * WPA Supplicant - Glue code to setup EAPOL and RSN modules + * Copyright (c) 2003-2008, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "rsn_supp/wpa.h" +#include "eloop.h" +#include "config.h" +#include "l2_packet/l2_packet.h" +#include "common/wpa_common.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "rsn_supp/pmksa_cache.h" +#include "mlme.h" +#include "sme.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "wpas_glue.h" +#include "wps_supplicant.h" +#include "bss.h" +#include "scan.h" + + +#ifndef CONFIG_NO_CONFIG_BLOBS +#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) +static void wpa_supplicant_set_config_blob(void *ctx, + struct wpa_config_blob *blob) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_config_set_blob(wpa_s->conf, blob); + if (wpa_s->conf->update_config) { + int ret = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to update config after " + "blob set"); + } + } +} + + +static const struct wpa_config_blob * +wpa_supplicant_get_config_blob(void *ctx, const char *name) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_config_get_blob(wpa_s->conf, name); +} +#endif /* defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) */ +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) +static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + struct ieee802_1x_hdr *hdr; + + *msg_len = sizeof(*hdr) + data_len; + hdr = os_malloc(*msg_len); + if (hdr == NULL) + return NULL; + + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = host_to_be16(data_len); + + if (data) + os_memcpy(hdr + 1, data, data_len); + else + os_memset(hdr + 1, 0, data_len); + + if (data_pos) + *data_pos = hdr + 1; + + return (u8 *) hdr; +} + + +/** + * wpa_ether_send - Send Ethernet frame + * @wpa_s: Pointer to wpa_supplicant data + * @dest: Destination MAC address + * @proto: Ethertype in host byte order + * @buf: Frame payload starting from IEEE 802.1X header + * @len: Frame payload length + * Returns: >=0 on success, <0 on failure + */ +static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest, + u16 proto, const u8 *buf, size_t len) +{ + if (wpa_s->l2) { + return l2_packet_send(wpa_s->l2, dest, proto, buf, len); + } + + return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len); +} +#endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */ + + +#ifdef IEEE8021X_EAPOL + +/** + * wpa_supplicant_eapol_send - Send IEEE 802.1X EAPOL packet to Authenticator + * @ctx: Pointer to wpa_supplicant data (wpa_s) + * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*) + * @buf: EAPOL payload (after IEEE 802.1X header) + * @len: EAPOL payload length + * Returns: >=0 on success, <0 on failure + * + * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame + * to the current Authenticator. + */ +static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf, + size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 *msg, *dst, bssid[ETH_ALEN]; + size_t msglen; + int res; + + /* TODO: could add l2_packet_sendmsg that allows fragments to avoid + * extra copy here */ + + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { + /* Current SSID is not using IEEE 802.1X/EAP, so drop possible + * EAPOL frames (mainly, EAPOL-Start) from EAPOL state + * machines. */ + wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X " + "mode (type=%d len=%lu)", type, + (unsigned long) len); + return -1; + } + + if (pmksa_cache_get_current(wpa_s->wpa) && + type == IEEE802_1X_TYPE_EAPOL_START) { + /* Trying to use PMKSA caching - do not send EAPOL-Start frames + * since they will trigger full EAPOL authentication. */ + wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send " + "EAPOL-Start"); + return -1; + } + + if (is_zero_ether_addr(wpa_s->bssid)) { + wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an " + "EAPOL frame"); + if (wpa_drv_get_bssid(wpa_s, bssid) == 0 && + !is_zero_ether_addr(bssid)) { + dst = bssid; + wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR + " from the driver as the EAPOL destination", + MAC2STR(dst)); + } else { + dst = wpa_s->last_eapol_src; + wpa_printf(MSG_DEBUG, "Using the source address of the" + " last received EAPOL frame " MACSTR " as " + "the EAPOL destination", + MAC2STR(dst)); + } + } else { + /* BSSID was already set (from (Re)Assoc event, so use it as + * the EAPOL destination. */ + dst = wpa_s->bssid; + } + + msg = wpa_alloc_eapol(wpa_s, type, buf, len, &msglen, NULL); + if (msg == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "TX EAPOL: dst=" MACSTR, MAC2STR(dst)); + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen); + res = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, msg, msglen); + os_free(msg); + return res; +} + + +/** + * wpa_eapol_set_wep_key - set WEP key for the driver + * @ctx: Pointer to wpa_supplicant data (wpa_s) + * @unicast: 1 = individual unicast key, 0 = broadcast key + * @keyidx: WEP key index (0..3) + * @key: Pointer to key data + * @keylen: Key length in bytes + * Returns: 0 on success or < 0 on error. + */ +static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx, + const u8 *key, size_t keylen) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + int cipher = (keylen == 5) ? WPA_CIPHER_WEP40 : + WPA_CIPHER_WEP104; + if (unicast) + wpa_s->pairwise_cipher = cipher; + else + wpa_s->group_cipher = cipher; + } + return wpa_drv_set_key(wpa_s, WPA_ALG_WEP, + unicast ? wpa_s->bssid : NULL, + keyidx, unicast, NULL, 0, key, keylen); +} + + +static void wpa_supplicant_aborted_cached(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_sm_aborted_cached(wpa_s->wpa); +} + + +static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success, + 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"); + + if (wpas_wps_eapol_cb(wpa_s) > 0) + return; + + if (!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); + } + + if (!success || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + return; + + if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) + return; + + wpa_printf(MSG_DEBUG, "Configure PMK for driver-based RSN 4-way " + "handshake"); + + pmk_len = PMK_LEN; + if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) { +#ifdef CONFIG_IEEE80211R + u8 buf[2 * PMK_LEN]; + wpa_printf(MSG_DEBUG, "RSN: Use FT XXKey as PMK for " + "driver-based 4-way hs and FT"); + res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN); + if (res == 0) { + os_memcpy(pmk, buf + PMK_LEN, PMK_LEN); + os_memset(buf, 0, sizeof(buf)); + } +#else /* CONFIG_IEEE80211R */ + res = -1; +#endif /* CONFIG_IEEE80211R */ + } else { + res = eapol_sm_get_key(eapol, pmk, PMK_LEN); + if (res) { + /* + * EAP-LEAP is an exception from other EAP methods: it + * uses only 16-byte PMK. + */ + res = eapol_sm_get_key(eapol, pmk, 16); + pmk_len = 16; + } + } + + if (res) { + wpa_printf(MSG_DEBUG, "Failed to get PMK from EAPOL state " + "machines"); + return; + } + + wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way " + "handshake", pmk, pmk_len); + + if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk, + pmk_len)) { + wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver"); + } + + wpa_supplicant_cancel_scan(wpa_s); + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + +} + + +static void wpa_supplicant_notify_eapol_done(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete"); + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE); + } else { + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + } +} + +#endif /* IEEE8021X_EAPOL */ + + +#ifndef CONFIG_NO_WPA + +static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s) +{ + int ret = 0; + struct wpa_bss *curr = NULL, *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + const u8 *ie; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) != 0) + continue; + if (ssid == NULL || + ((bss->ssid_len == ssid->ssid_len && + os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) == 0) || + ssid->ssid_len == 0)) { + curr = bss; + break; + } + } + + if (curr) { + ie = wpa_bss_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE); + if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0)) + ret = -1; + + ie = wpa_bss_get_ie(curr, WLAN_EID_RSN); + if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0)) + ret = -1; + } else { + ret = -1; + } + + return ret; +} + + +static int wpa_supplicant_get_beacon_ie(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_get_beacon_ie(wpa_s) == 0) { + return 0; + } + + /* No WPA/RSN IE found in the cached scan results. Try to get updated + * scan results from the driver. */ + if (wpa_supplicant_update_scan_results(wpa_s) < 0) + return -1; + + return wpa_get_beacon_ie(wpa_s); +} + + +static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos); +} + + +static int _wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto, + const u8 *buf, size_t len) +{ + return wpa_ether_send(wpa_s, dest, proto, buf, len); +} + + +static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s) +{ + wpa_supplicant_cancel_auth_timeout(wpa_s); +} + + +static void _wpa_supplicant_set_state(void *wpa_s, enum wpa_states state) +{ + wpa_supplicant_set_state(wpa_s, state); +} + + +/** + * wpa_supplicant_get_state - Get the connection state + * @wpa_s: Pointer to wpa_supplicant data + * Returns: The current connection state (WPA_*) + */ +static enum wpa_states wpa_supplicant_get_state(struct wpa_supplicant *wpa_s) +{ + return wpa_s->wpa_state; +} + + +static enum wpa_states _wpa_supplicant_get_state(void *wpa_s) +{ + return wpa_supplicant_get_state(wpa_s); +} + + +static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) +{ + wpa_supplicant_disassociate(wpa_s, reason_code); + /* Schedule a scan to make sure we continue looking for networks */ + wpa_supplicant_req_scan(wpa_s, 5, 0); +} + + +static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) +{ + wpa_supplicant_deauthenticate(wpa_s, reason_code); + /* Schedule a scan to make sure we continue looking for networks */ + wpa_supplicant_req_scan(wpa_s, 5, 0); +} + + +static void * wpa_supplicant_get_network_ctx(void *wpa_s) +{ + return wpa_supplicant_get_ssid(wpa_s); +} + + +static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { + os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); + return 0; + } + return wpa_drv_get_bssid(wpa_s, bssid); +} + + +static int wpa_supplicant_set_key(void *_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) +{ + struct wpa_supplicant *wpa_s = _wpa_s; + if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) { + /* Clear the MIC error counter when setting a new PTK. */ + wpa_s->mic_errors_seen = 0; + } + return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len, + key, key_len); +} + + +static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr, + int protection_type, + int key_type) +{ + return wpa_drv_mlme_setprotection(wpa_s, addr, protection_type, + key_type); +} + + +static int wpa_supplicant_add_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + return wpa_drv_add_pmkid(wpa_s, bssid, pmkid); +} + + +static int wpa_supplicant_remove_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid); +} + + +#ifdef CONFIG_IEEE80211R +static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md, + const u8 *ies, size_t ies_len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + return ieee80211_sta_update_ft_ies(wpa_s, md, ies, ies_len); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + return sme_update_ft_ies(wpa_s, md, ies, ies_len); + return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len); +} + + +static int wpa_supplicant_send_ft_action(void *ctx, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + return ieee80211_sta_send_ft_action(wpa_s, action, target_ap, + ies, ies_len); + return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len); +} + + +static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_driver_auth_params params; + struct wpa_bss *bss; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + return -1; + + bss = wpa_bss_get_bssid(wpa_s, target_ap); + if (bss == NULL) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.bssid = target_ap; + params.freq = bss->freq; + params.ssid = bss->ssid; + params.ssid_len = bss->ssid_len; + params.auth_alg = WPA_AUTH_ALG_FT; + params.local_state_change = 1; + return wpa_drv_authenticate(wpa_s, ¶ms); +} +#endif /* CONFIG_IEEE80211R */ + +#endif /* CONFIG_NO_WPA */ + + +#ifdef CONFIG_TDLS + +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) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token, + status_code, buf, len); +} + + +static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_drv_tdls_oper(wpa_s, oper, peer); +} + +#endif /* CONFIG_TDLS */ + + +#ifdef IEEE8021X_EAPOL +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void wpa_supplicant_eap_param_needed(void *ctx, const char *field, + const char *txt) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *ssid = wpa_s->current_ssid; + char *buf; + size_t buflen; + int len; + + if (ssid == NULL) + 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, 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 wpa_supplicant_eap_param_needed NULL +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +static void wpa_supplicant_port_cb(void *ctx, int authorized) +{ + struct wpa_supplicant *wpa_s = ctx; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, "AP mode active - skip EAPOL Supplicant " + "port status: %s", + authorized ? "Authorized" : "Unauthorized"); + return; + } +#endif /* CONFIG_AP */ + wpa_printf(MSG_DEBUG, "EAPOL: Supplicant port status: %s", + authorized ? "Authorized" : "Unauthorized"); + wpa_drv_set_supp_port(wpa_s, authorized); +} +#endif /* IEEE8021X_EAPOL */ + + +int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) +{ +#ifdef IEEE8021X_EAPOL + struct eapol_ctx *ctx; + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context."); + return -1; + } + + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->eapol_send_ctx = wpa_s; + ctx->preauth = 0; + ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done; + ctx->eapol_send = wpa_supplicant_eapol_send; + ctx->set_wep_key = wpa_eapol_set_wep_key; + ctx->set_config_blob = wpa_supplicant_set_config_blob; + ctx->get_config_blob = wpa_supplicant_get_config_blob; + ctx->aborted_cached = wpa_supplicant_aborted_cached; + 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->wps = wpa_s->wps; + ctx->eap_param_needed = wpa_supplicant_eap_param_needed; + ctx->port_cb = wpa_supplicant_port_cb; + ctx->cb = wpa_supplicant_eapol_cb; + ctx->cb_ctx = wpa_s; + wpa_s->eapol = eapol_sm_init(ctx); + if (wpa_s->eapol == NULL) { + os_free(ctx); + wpa_printf(MSG_ERROR, "Failed to initialize EAPOL state " + "machines."); + return -1; + } +#endif /* IEEE8021X_EAPOL */ + + return 0; +} + + +int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) +{ +#ifndef CONFIG_NO_WPA + struct wpa_sm_ctx *ctx; + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate WPA context."); + return -1; + } + + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->set_state = _wpa_supplicant_set_state; + ctx->get_state = _wpa_supplicant_get_state; + ctx->deauthenticate = _wpa_supplicant_deauthenticate; + ctx->disassociate = _wpa_supplicant_disassociate; + ctx->set_key = wpa_supplicant_set_key; + ctx->get_network_ctx = wpa_supplicant_get_network_ctx; + ctx->get_bssid = wpa_supplicant_get_bssid; + ctx->ether_send = _wpa_ether_send; + ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie; + ctx->alloc_eapol = _wpa_alloc_eapol; + ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout; + ctx->add_pmkid = wpa_supplicant_add_pmkid; + ctx->remove_pmkid = wpa_supplicant_remove_pmkid; +#ifndef CONFIG_NO_CONFIG_BLOBS + ctx->set_config_blob = wpa_supplicant_set_config_blob; + ctx->get_config_blob = wpa_supplicant_get_config_blob; +#endif /* CONFIG_NO_CONFIG_BLOBS */ + ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection; +#ifdef CONFIG_IEEE80211R + ctx->update_ft_ies = wpa_supplicant_update_ft_ies; + ctx->send_ft_action = wpa_supplicant_send_ft_action; + ctx->mark_authenticated = wpa_supplicant_mark_authenticated; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_TDLS + ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt; + ctx->tdls_oper = wpa_supplicant_tdls_oper; +#endif /* CONFIG_TDLS */ + + wpa_s->wpa = wpa_sm_init(ctx); + if (wpa_s->wpa == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize WPA state " + "machine"); + return -1; + } +#endif /* CONFIG_NO_WPA */ + + return 0; +} + + +void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct rsn_supp_config conf; + if (ssid) { + os_memset(&conf, 0, sizeof(conf)); + conf.network_ctx = ssid; + conf.peerkey_enabled = ssid->peerkey; + conf.allowed_pairwise_cipher = ssid->pairwise_cipher; +#ifdef IEEE8021X_EAPOL + conf.eap_workaround = ssid->eap_workaround; + conf.eap_conf_ctx = &ssid->eap; +#endif /* IEEE8021X_EAPOL */ + conf.ssid = ssid->ssid; + conf.ssid_len = ssid->ssid_len; + conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey; + } + wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); +} diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h new file mode 100644 index 0000000..b571e4d --- /dev/null +++ b/wpa_supplicant/wpas_glue.h @@ -0,0 +1,23 @@ +/* + * WPA Supplicant - Glue code to setup EAPOL and RSN modules + * Copyright (c) 2003-2008, 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. + */ + +#ifndef WPAS_GLUE_H +#define WPAS_GLUE_H + +int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s); +int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s); +void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +#endif /* WPAS_GLUE_H */ diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c new file mode 100644 index 0000000..778ccaf --- /dev/null +++ b/wpa_supplicant/wps_supplicant.c @@ -0,0 +1,1676 @@ +/* + * wpa_supplicant / WPS integration + * Copyright (c) 2008-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "uuid.h" +#include "crypto/dh_group5.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/wpa_common.h" +#include "common/wpa_ctrl.h" +#include "eap_common/eap_wsc_common.h" +#include "eap_peer/eap.h" +#include "rsn_supp/wpa.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "notify.h" +#include "blacklist.h" +#include "bss.h" +#include "scan.h" +#include "ap.h" +#include "p2p/p2p.h" +#include "p2p_supplicant.h" +#include "wps_supplicant.h" + + +#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG +#define WPS_PIN_SCAN_IGNORE_SEL_REG 3 +#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */ + +static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpas_clear_wps(struct wpa_supplicant *wpa_s); + + +int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->wps_success && + wpa_s->current_ssid && + eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { + const u8 *bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + + wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR + " did not succeed - continue trying to find " + "suitable AP", MAC2STR(bssid)); + wpa_blacklist_add(wpa_s, bssid); + + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, + wpa_s->blacklist_cleared ? 5 : 0, 0); + wpa_s->blacklist_cleared = 0; + return 1; + } + + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success) + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL); + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid && + !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + int disabled = wpa_s->current_ssid->disabled; + wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - " + "try to associate with the received credential"); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + if (disabled) { + wpa_printf(MSG_DEBUG, "WPS: Current network is " + "disabled - wait for user to enable"); + return 1; + } + wpa_s->after_wps = 5; + wpa_s->wps_freq = wpa_s->assoc_freq; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return 1; + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) { + wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting " + "for external credential processing"); + wpas_clear_wps(wpa_s); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + return 1; + } + + return 0; +} + + +static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const struct wps_credential *cred) +{ + struct wpa_driver_capa capa; + struct wpa_bss *bss; + const u8 *ie; + struct wpa_ie_data adv; + int wpa2 = 0, ccmp = 0; + + /* + * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in + * case they are configured for mixed mode operation (WPA+WPA2 and + * TKIP+CCMP). Try to use scan results to figure out whether the AP + * actually supports stronger security and select that if the client + * has support for it, too. + */ + + if (wpa_drv_get_capa(wpa_s, &capa)) + return; /* Unknown what driver supports */ + + bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len); + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS " + "table - use credential as-is"); + return; + } + + wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table"); + + ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) { + wpa2 = 1; + if (adv.pairwise_cipher & WPA_CIPHER_CCMP) + ccmp = 1; + } else { + ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 && + adv.pairwise_cipher & WPA_CIPHER_CCMP) + ccmp = 1; + } + + if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) && + (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) { + /* + * TODO: This could be the initial AP configuration and the + * Beacon contents could change shortly. Should request a new + * scan and delay addition of the network until the updated + * scan results are available. + */ + wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA " + "support - use credential as-is"); + return; + } + + if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && + (ssid->pairwise_cipher & WPA_CIPHER_TKIP) && + (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential " + "based on scan results"); + if (wpa_s->conf->ap_scan == 1) + ssid->pairwise_cipher |= WPA_CIPHER_CCMP; + else + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + } + + if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) && + (ssid->proto & WPA_PROTO_WPA) && + (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) { + wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential " + "based on scan results"); + if (wpa_s->conf->ap_scan == 1) + ssid->proto |= WPA_PROTO_RSN; + else + ssid->proto = WPA_PROTO_RSN; + } +} + + +static int wpa_supplicant_wps_cred(void *ctx, + const struct wps_credential *cred) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *ssid = wpa_s->current_ssid; + u8 key_idx = 0; + u16 auth_type; + int registrar = 0; + + if ((wpa_s->conf->wps_cred_processing == 1 || + wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) { + size_t blen = cred->cred_attr_len * 2 + 1; + char *buf = os_malloc(blen); + if (buf) { + wpa_snprintf_hex(buf, blen, + cred->cred_attr, cred->cred_attr_len); + wpa_msg(wpa_s, MSG_INFO, "%s%s", + WPS_EVENT_CRED_RECEIVED, buf); + os_free(buf); + } + + wpas_notify_wps_credential(wpa_s, cred); + } else + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED); + + wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", + cred->cred_attr, cred->cred_attr_len); + + if (wpa_s->conf->wps_cred_processing == 1) + return 0; + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); + wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", + cred->auth_type); + wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); + wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); + wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", + cred->key, cred->key_len); + wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, + MAC2STR(cred->mac_addr)); + + auth_type = cred->auth_type; + if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) { + wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode " + "auth_type into WPA2PSK"); + auth_type = WPS_AUTH_WPA2PSK; + } + + 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 " + "unsupported authentication type 0x%x", + auth_type); + return 0; + } + + if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based " + "on the received credential"); + if (ssid->eap.identity && + ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR, + WSC_ID_REGISTRAR_LEN) == 0) + registrar = 1; + os_free(ssid->eap.identity); + ssid->eap.identity = NULL; + ssid->eap.identity_len = 0; + os_free(ssid->eap.phase1); + ssid->eap.phase1 = NULL; + os_free(ssid->eap.eap_methods); + ssid->eap.eap_methods = NULL; + if (!ssid->p2p_group) + ssid->temporary = 0; + } else { + wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the " + "received credential"); + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + wpas_notify_network_added(wpa_s, ssid); + } + + wpa_config_set_network_defaults(ssid); + + os_free(ssid->ssid); + ssid->ssid = os_malloc(cred->ssid_len); + if (ssid->ssid) { + os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len); + ssid->ssid_len = cred->ssid_len; + } + + 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; + case WPS_ENCR_AES: + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + break; + } + + switch (auth_type) { + case WPS_AUTH_OPEN: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_NONE; + ssid->proto = 0; +#ifdef CONFIG_WPS_REG_DISABLE_OPEN + if (registrar) { + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK + "id=%d - Credentials for an open " + "network disabled by default - use " + "'select_network %d' to enable", + ssid->id, ssid->id); + ssid->disabled = 1; + } +#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; + ssid->proto = WPA_PROTO_WPA; + break; + case WPS_AUTH_WPA: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + ssid->proto = WPA_PROTO_WPA; + break; + case WPS_AUTH_WPA2: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + ssid->proto = WPA_PROTO_RSN; + break; + case WPS_AUTH_WPA2PSK: + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_PSK; + ssid->proto = WPA_PROTO_RSN; + break; + } + + if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) { + if (cred->key_len == 2 * PMK_LEN) { + if (hexstr2bin((const char *) cred->key, ssid->psk, + PMK_LEN)) { + wpa_printf(MSG_ERROR, "WPS: Invalid Network " + "Key"); + return -1; + } + ssid->psk_set = 1; + ssid->export_keys = 1; + } else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) { + os_free(ssid->passphrase); + ssid->passphrase = os_malloc(cred->key_len + 1); + if (ssid->passphrase == NULL) + return -1; + os_memcpy(ssid->passphrase, cred->key, cred->key_len); + ssid->passphrase[cred->key_len] = '\0'; + wpa_config_update_psk(ssid); + ssid->export_keys = 1; + } else { + wpa_printf(MSG_ERROR, "WPS: Invalid Network Key " + "length %lu", + (unsigned long) cred->key_len); + return -1; + } + } + + wpas_wps_security_workaround(wpa_s, ssid, cred); + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration"); + return -1; + } +#endif /* CONFIG_NO_CONFIG_WRITE */ + + return 0; +} + + +#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) +{ + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D + "dev_password_id=%d config_error=%d", + m2d->dev_password_id, m2d->config_error); + wpas_notify_wps_event_m2d(wpa_s, m2d); +#ifdef CONFIG_P2P + if (wpa_s->parent && wpa_s->parent != wpa_s) { + wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D + "dev_password_id=%d config_error=%d", + m2d->dev_password_id, m2d->config_error); + } + if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) { + /* + * 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, + NULL); + } +#endif /* CONFIG_P2P */ +} + + +static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = { + "No Error", /* WPS_EI_NO_ERROR */ + "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */ + "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */ +}; + +static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ + if (fail->error_indication > 0 && + fail->error_indication < NUM_WPS_EI_VALUES) { + wpa_msg(wpa_s, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", + fail->msg, fail->config_error, fail->error_indication, + wps_event_fail_reason[fail->error_indication]); + if (wpa_s->parent && wpa_s->parent != wpa_s) + wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + "msg=%d config_error=%d reason=%d (%s)", + fail->msg, fail->config_error, + fail->error_indication, + wps_event_fail_reason[fail->error_indication]); + } else { + wpa_msg(wpa_s, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d", + fail->msg, fail->config_error); + if (wpa_s->parent && wpa_s->parent != wpa_s) + wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + "msg=%d config_error=%d", + fail->msg, fail->config_error); + } + wpas_clear_wps(wpa_s); + wpas_notify_wps_event_fail(wpa_s, fail); +} + + +static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) +{ + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); + wpa_s->wps_success = 1; + wpas_notify_wps_event_success(wpa_s); +#ifdef CONFIG_P2P + wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0); +#endif /* CONFIG_P2P */ +} + + +static void wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant *wpa_s, + struct wps_event_er_ap *ap) +{ + char uuid_str[100]; + char dev_type[WPS_DEV_TYPE_BUFSIZE]; + + uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str)); + if (ap->pri_dev_type) + wps_dev_type_bin2str(ap->pri_dev_type, dev_type, + sizeof(dev_type)); + else + dev_type[0] = '\0'; + + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_ADD "%s " MACSTR + " pri_dev_type=%s wps_state=%d |%s|%s|%s|%s|%s|%s|", + uuid_str, MAC2STR(ap->mac_addr), dev_type, ap->wps_state, + ap->friendly_name ? ap->friendly_name : "", + ap->manufacturer ? ap->manufacturer : "", + ap->model_description ? ap->model_description : "", + ap->model_name ? ap->model_name : "", + ap->manufacturer_url ? ap->manufacturer_url : "", + ap->model_url ? ap->model_url : ""); +} + + +static void wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant *wpa_s, + struct wps_event_er_ap *ap) +{ + char uuid_str[100]; + uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str)); + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_REMOVE "%s", uuid_str); +} + + +static void wpa_supplicant_wps_event_er_enrollee_add( + struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee) +{ + char uuid_str[100]; + char dev_type[WPS_DEV_TYPE_BUFSIZE]; + + uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str)); + if (enrollee->pri_dev_type) + wps_dev_type_bin2str(enrollee->pri_dev_type, dev_type, + sizeof(dev_type)); + else + dev_type[0] = '\0'; + + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD "%s " MACSTR + " M1=%d config_methods=0x%x dev_passwd_id=%d pri_dev_type=%s " + "|%s|%s|%s|%s|%s|", + uuid_str, MAC2STR(enrollee->mac_addr), enrollee->m1_received, + enrollee->config_methods, enrollee->dev_passwd_id, dev_type, + enrollee->dev_name ? enrollee->dev_name : "", + enrollee->manufacturer ? enrollee->manufacturer : "", + enrollee->model_name ? enrollee->model_name : "", + enrollee->model_number ? enrollee->model_number : "", + enrollee->serial_number ? enrollee->serial_number : ""); +} + + +static void wpa_supplicant_wps_event_er_enrollee_remove( + struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee) +{ + char uuid_str[100]; + uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str)); + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE "%s " MACSTR, + uuid_str, MAC2STR(enrollee->mac_addr)); +} + + +static void wpa_supplicant_wps_event_er_ap_settings( + struct wpa_supplicant *wpa_s, + struct wps_event_er_ap_settings *ap_settings) +{ + char uuid_str[100]; + char key_str[65]; + const struct wps_credential *cred = ap_settings->cred; + + key_str[0] = '\0'; + if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) { + if (cred->key_len >= 8 && cred->key_len <= 64) { + os_memcpy(key_str, cred->key, cred->key_len); + key_str[cred->key_len] = '\0'; + } + } + + uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str)); + /* Use wpa_msg_ctrl to avoid showing the key in debug log */ + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS + "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x " + "key=%s", + uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len), + cred->auth_type, cred->encr_type, key_str); +} + + +static void wpa_supplicant_wps_event_er_set_sel_reg( + struct wpa_supplicant *wpa_s, + struct wps_event_er_set_selected_registrar *ev) +{ + char uuid_str[100]; + + uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str)); + switch (ev->state) { + case WPS_ER_SET_SEL_REG_START: + wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG + "uuid=%s state=START sel_reg=%d dev_passwd_id=%u " + "sel_reg_config_methods=0x%x", + uuid_str, ev->sel_reg, ev->dev_passwd_id, + ev->sel_reg_config_methods); + break; + case WPS_ER_SET_SEL_REG_DONE: + wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG + "uuid=%s state=DONE", uuid_str); + break; + case WPS_ER_SET_SEL_REG_FAILED: + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG + "uuid=%s state=FAILED", uuid_str); + break; + } +} + + +static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, + union wps_event_data *data) +{ + struct wpa_supplicant *wpa_s = ctx; + switch (event) { + case WPS_EV_M2D: + wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d); + break; + case WPS_EV_FAIL: + wpa_supplicant_wps_event_fail(wpa_s, &data->fail); + break; + case WPS_EV_SUCCESS: + wpa_supplicant_wps_event_success(wpa_s); + break; + case WPS_EV_PWD_AUTH_FAIL: +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee) + wpa_supplicant_ap_pwd_auth_fail(wpa_s); +#endif /* CONFIG_AP */ + break; + case WPS_EV_PBC_OVERLAP: + break; + case WPS_EV_PBC_TIMEOUT: + break; + case WPS_EV_ER_AP_ADD: + wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap); + break; + case WPS_EV_ER_AP_REMOVE: + wpa_supplicant_wps_event_er_ap_remove(wpa_s, &data->ap); + break; + case WPS_EV_ER_ENROLLEE_ADD: + wpa_supplicant_wps_event_er_enrollee_add(wpa_s, + &data->enrollee); + break; + case WPS_EV_ER_ENROLLEE_REMOVE: + wpa_supplicant_wps_event_er_enrollee_remove(wpa_s, + &data->enrollee); + break; + case WPS_EV_ER_AP_SETTINGS: + wpa_supplicant_wps_event_er_ap_settings(wpa_s, + &data->ap_settings); + break; + case WPS_EV_ER_SET_SELECTED_REGISTRAR: + wpa_supplicant_wps_event_er_set_sel_reg(wpa_s, + &data->set_sel_reg); + break; + } +} + + +enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid) +{ + if (eap_is_wps_pbc_enrollee(&ssid->eap) || + eap_is_wps_pin_enrollee(&ssid->eap)) + return WPS_REQ_ENROLLEE; + else + return WPS_REQ_REGISTRAR; +} + + +static void wpas_clear_wps(struct wpa_supplicant *wpa_s) +{ + int id; + struct wpa_ssid *ssid, *remove_ssid = NULL; + + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + + /* Remove any existing WPS network from configuration */ + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + if (ssid == wpa_s->current_ssid) { + wpa_s->current_ssid = NULL; + if (ssid != NULL) + wpas_notify_network_changed(wpa_s); + } + id = ssid->id; + remove_ssid = ssid; + } else + id = -1; + ssid = ssid->next; + if (id >= 0) { + wpas_notify_network_removed(wpa_s, remove_ssid); + wpa_config_remove_network(wpa_s->conf, id); + } + } +} + + +static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed " + "out"); + wpas_clear_wps(wpa_s); +} + + +static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, + int registrar, const u8 *bssid) +{ + struct wpa_ssid *ssid; + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return NULL; + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 || + wpa_config_set(ssid, "eap", "WSC", 0) < 0 || + wpa_config_set(ssid, "identity", registrar ? + "\"" WSC_ID_REGISTRAR "\"" : + "\"" WSC_ID_ENROLLEE "\"", 0) < 0) { + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return NULL; + } + + if (bssid) { +#ifndef CONFIG_P2P + struct wpa_bss *bss; + int count = 0; +#endif /* CONFIG_P2P */ + + os_memcpy(ssid->bssid, bssid, ETH_ALEN); + ssid->bssid_set = 1; + + /* + * Note: With P2P, the SSID may change at the time the WPS + * provisioning is started, so better not filter the AP based + * on the current SSID in the scan results. + */ +#ifndef CONFIG_P2P + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0) + continue; + + os_free(ssid->ssid); + ssid->ssid = os_malloc(bss->ssid_len); + if (ssid->ssid == NULL) + break; + os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len); + ssid->ssid_len = bss->ssid_len; + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from " + "scan results", + ssid->ssid, ssid->ssid_len); + count++; + } + + if (count > 1) { + wpa_printf(MSG_DEBUG, "WPS: More than one SSID found " + "for the AP; use wildcard"); + os_free(ssid->ssid); + ssid->ssid = NULL; + ssid->ssid_len = 0; + } +#endif /* CONFIG_P2P */ + } + + return ssid; +} + + +static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, + struct wpa_ssid *selected) +{ + struct wpa_ssid *ssid; + + /* Mark all other networks disabled and trigger reassociation */ + ssid = wpa_s->conf->ssid; + while (ssid) { + int was_disabled = ssid->disabled; + ssid->disabled = ssid != selected; + if (was_disabled != ssid->disabled) + wpas_notify_network_enabled_changed(wpa_s, ssid); + ssid = ssid->next; + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_s->scan_runs = 0; + wpa_s->wps_success = 0; + wpa_s->blacklist_cleared = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, + int p2p_group) +{ + struct wpa_ssid *ssid; + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 0, bssid); + if (ssid == NULL) + return -1; + ssid->temporary = 1; + ssid->p2p_group = p2p_group; +#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); + if (ssid->ssid) { + ssid->ssid_len = wpa_s->go_params->ssid_len; + os_memcpy(ssid->ssid, wpa_s->go_params->ssid, + ssid->ssid_len); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " + "SSID", ssid->ssid, ssid->ssid_len); + } + } +#endif /* CONFIG_P2P */ + wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0); + 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); + wpas_wps_reassoc(wpa_s, ssid); + 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) +{ + struct wpa_ssid *ssid; + char val[128]; + unsigned int rpin = 0; + + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 0, bssid); + if (ssid == NULL) + return -1; + ssid->temporary = 1; + ssid->p2p_group = p2p_group; +#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); + if (ssid->ssid) { + ssid->ssid_len = wpa_s->go_params->ssid_len; + os_memcpy(ssid->ssid, wpa_s->go_params->ssid, + ssid->ssid_len); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " + "SSID", ssid->ssid, ssid->ssid_len); + } + } +#endif /* CONFIG_P2P */ + if (pin) + os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"", + pin, dev_pw_id); + else { + rpin = wps_generate_pin(); + os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"", + rpin, dev_pw_id); + } + wpa_config_set(ssid, "phase1", val, 0); + 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); + wpas_wps_reassoc(wpa_s, ssid); + return rpin; +} + + +/* Cancel the wps pbc/pin requests */ +int wpas_wps_cancel(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode"); + return wpa_supplicant_ap_wps_cancel(wpa_s); + } +#endif /* CONFIG_AP */ + + if (wpa_s->wpa_state == WPA_SCANNING) { + wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan"); + wpa_supplicant_cancel_scan(wpa_s); + wpas_clear_wps(wpa_s); + } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) { + wpa_printf(MSG_DEBUG, "WPS: Cancel operation - " + "deauthenticate"); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + wpas_clear_wps(wpa_s); + } + + return 0; +} + + +#ifdef CONFIG_WPS_OOB +int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, + char *path, char *method, char *name) +{ + struct wps_context *wps = wpa_s->wps; + struct oob_device_data *oob_dev; + + oob_dev = wps_get_oob_device(device_type); + if (oob_dev == NULL) + return -1; + oob_dev->device_path = path; + oob_dev->device_name = name; + wps->oob_conf.oob_method = wps_get_oob_method(method); + + if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E) { + /* + * Use pre-configured DH keys in order to be able to write the + * key hash into the OOB file. + */ + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + wps->dh_pubkey = NULL; + dh5_free(wps->dh_ctx); + wps->dh_ctx = dh5_init(&wps->dh_privkey, &wps->dh_pubkey); + wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192); + if (wps->dh_ctx == NULL || wps->dh_pubkey == NULL) { + wpa_printf(MSG_ERROR, "WPS: Failed to initialize " + "Diffie-Hellman handshake"); + return -1; + } + } + + if (wps->oob_conf.oob_method == OOB_METHOD_CRED) + wpas_clear_wps(wpa_s); + + if (wps_process_oob(wps, oob_dev, 0) < 0) + return -1; + + if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || + wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && + wpas_wps_start_pin(wpa_s, NULL, + wpabuf_head(wps->oob_conf.dev_password), 0, + DEV_PW_DEFAULT) < 0) + return -1; + + return 0; +} +#endif /* CONFIG_WPS_OOB */ + + +int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin, struct wps_new_ap_settings *settings) +{ + struct wpa_ssid *ssid; + char val[200]; + char *pos, *end; + int res; + + if (!pin) + return -1; + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 1, bssid); + if (ssid == NULL) + return -1; + ssid->temporary = 1; + pos = val; + end = pos + sizeof(val); + res = os_snprintf(pos, end - pos, "\"pin=%s", pin); + if (res < 0 || res >= end - pos) + return -1; + pos += res; + if (settings) { + res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s " + "new_encr=%s new_key=%s", + settings->ssid_hex, settings->auth, + settings->encr, settings->key_hex); + if (res < 0 || res >= end - pos) + return -1; + pos += res; + } + res = os_snprintf(pos, end - pos, "\""); + if (res < 0 || res >= end - pos) + return -1; + wpa_config_set(ssid, "phase1", val, 0); + 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); + wpas_wps_reassoc(wpa_s, ssid); + return 0; +} + + +static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, + size_t psk_len) +{ + wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for " + "STA " MACSTR, MAC2STR(mac_addr)); + wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); + + /* TODO */ + + return 0; +} + + +static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, + const struct wps_device_data *dev) +{ + char uuid[40], txt[400]; + int len; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid); + len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR + " [%s|%s|%s|%s|%s|%s]", + uuid, MAC2STR(dev->mac_addr), dev->device_name, + dev->manufacturer, dev->model_name, + dev->model_number, dev->serial_number, + wps_dev_type_bin2str(dev->pri_dev_type, devtype, + sizeof(devtype))); + if (len > 0 && len < (int) sizeof(txt)) + wpa_printf(MSG_INFO, "%s", txt); +} + + +static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods) +{ +#ifdef CONFIG_WPS_ER + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->wps_er == NULL) + return; + wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d " + "dev_password_id=%u sel_reg_config_methods=0x%x", + sel_reg, dev_passwd_id, sel_reg_config_methods); + wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id, + sel_reg_config_methods); +#endif /* CONFIG_WPS_ER */ +} + + +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) { + wpa_printf(MSG_INFO, "WPS: Converting display to " + "virtual_display for WPS 2.0 compliance"); + config_methods |= WPS_CONFIG_VIRT_DISPLAY; + } + if ((config_methods & + (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { + wpa_printf(MSG_INFO, "WPS: Converting push_button to " + "virtual_push_button for WPS 2.0 compliance"); + config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + } +#endif /* CONFIG_WPS2 */ + + return config_methods; +} + + +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); + if (is_nil_uuid(wpa_s->conf->uuid)) { + struct wpa_supplicant *first; + first = wpa_s->global->ifaces; + while (first && first->next) + first = first->next; + if (first && first != wpa_s) { + 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); + } 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); + } + } 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); + } +} + + +int wpas_wps_init(struct wpa_supplicant *wpa_s) +{ + struct wps_context *wps; + struct wps_registrar_config rcfg; + + wps = os_zalloc(sizeof(*wps)); + if (wps == NULL) + return -1; + + wps->cred_cb = wpa_supplicant_wps_cred; + wps->event_cb = wpa_supplicant_wps_event; + wps->cb_ctx = wpa_s; + + wps->dev.device_name = wpa_s->conf->device_name; + wps->dev.manufacturer = wpa_s->conf->manufacturer; + wps->dev.model_name = wpa_s->conf->model_name; + wps->dev.model_number = wpa_s->conf->model_number; + wps->dev.serial_number = wpa_s->conf->serial_number; + wps->config_methods = + wps_config_methods_str2bin(wpa_s->conf->config_methods); + if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) == + (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) { + wpa_printf(MSG_ERROR, "WPS: Both Label and Display config " + "methods are not allowed at the same time"); + os_free(wps); + return -1; + } + wps->config_methods = wps_fix_config_methods(wps->config_methods); + os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + + wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types; + os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type, + WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types); + + wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); + wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */ + os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN); + wpas_wps_set_uuid(wpa_s, wps); + + wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; + wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; + + os_memset(&rcfg, 0, sizeof(rcfg)); + rcfg.new_psk_cb = wpas_wps_new_psk_cb; + rcfg.pin_needed_cb = wpas_wps_pin_needed_cb; + rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb; + rcfg.cb_ctx = wpa_s; + + wps->registrar = wps_registrar_init(wps, &rcfg); + if (wps->registrar == NULL) { + wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar"); + os_free(wps); + return -1; + } + + wpa_s->wps = wps; + + return 0; +} + + +void wpas_wps_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + + if (wpa_s->wps == NULL) + return; + +#ifdef CONFIG_WPS_ER + wps_er_deinit(wpa_s->wps_er, NULL, NULL); + wpa_s->wps_er = NULL; +#endif /* CONFIG_WPS_ER */ + + wps_registrar_deinit(wpa_s->wps->registrar); + wpabuf_free(wpa_s->wps->dh_pubkey); + wpabuf_free(wpa_s->wps->dh_privkey); + wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash); + wpabuf_free(wpa_s->wps->oob_conf.dev_password); + os_free(wpa_s->wps->network_key); + os_free(wpa_s->wps); + wpa_s->wps = NULL; +} + + +int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_scan_res *bss) +{ + struct wpabuf *wps_ie; + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) + return -1; + + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (eap_is_wps_pbc_enrollee(&ssid->eap)) { + if (!wps_ie) { + wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); + return 0; + } + + if (!wps_is_selected_pbc_registrar(wps_ie)) { + wpa_printf(MSG_DEBUG, " skip - WPS AP " + "without active PBC Registrar"); + wpabuf_free(wps_ie); + return 0; + } + + /* TODO: overlap detection */ + wpa_printf(MSG_DEBUG, " selected based on WPS IE " + "(Active PBC)"); + wpabuf_free(wps_ie); + return 1; + } + + if (eap_is_wps_pin_enrollee(&ssid->eap)) { + if (!wps_ie) { + wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); + return 0; + } + + /* + * Start with WPS APs that advertise our address as an + * authorized MAC (v2.0) or active PIN Registrar (v1.0) and + * allow any WPS AP after couple of scans since some APs do not + * set Selected Registrar attribute properly when using + * external Registrar. + */ + if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) { + if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) { + wpa_printf(MSG_DEBUG, " skip - WPS AP " + "without active PIN Registrar"); + wpabuf_free(wps_ie); + return 0; + } + wpa_printf(MSG_DEBUG, " selected based on WPS IE"); + } else { + wpa_printf(MSG_DEBUG, " selected based on WPS IE " + "(Authorized MAC or Active PIN)"); + } + wpabuf_free(wps_ie); + return 1; + } + + if (wps_ie) { + wpa_printf(MSG_DEBUG, " selected based on WPS IE"); + wpabuf_free(wps_ie); + return 1; + } + + return -1; +} + + +int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + struct wpabuf *wps_ie = NULL; + int ret = 0; + + if (eap_is_wps_pbc_enrollee(&ssid->eap)) { + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) { + /* allow wildcard SSID for WPS PBC */ + ret = 1; + } + } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { + wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (wps_ie && + (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) || + wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) { + /* allow wildcard SSID for WPS PIN */ + ret = 1; + } + } + + if (!ret && ssid->bssid_set && + os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) { + /* allow wildcard SSID due to hardcoded BSSID match */ + ret = 1; + } + +#ifdef CONFIG_WPS_STRICT + if (wps_ie) { + if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len > + 0, bss->bssid) < 0) + ret = 0; + if (bss->beacon_ie_len) { + struct wpabuf *bcn_wps; + bcn_wps = wpa_scan_get_vendor_ie_multi_beacon( + bss, WPS_IE_VENDOR_TYPE); + if (bcn_wps == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE " + "missing from AP Beacon"); + ret = 0; + } else { + if (wps_validate_beacon(wps_ie) < 0) + ret = 0; + wpabuf_free(bcn_wps); + } + } + } +#endif /* CONFIG_WPS_STRICT */ + + wpabuf_free(wps_ie); + + return ret; +} + + +int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, struct wpa_ssid *ssid) +{ + const u8 *sel_uuid, *uuid; + struct wpabuf *wps_ie; + int ret = 0; + struct wpa_bss *bss; + + if (!eap_is_wps_pbc_enrollee(&ssid->eap)) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is " + "present in scan results; selected BSSID " MACSTR, + MAC2STR(selected->bssid)); + + /* Make sure that only one AP is in active PBC mode */ + wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE); + if (wps_ie) { + sel_uuid = wps_get_uuid_e(wps_ie); + wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS", + sel_uuid, UUID_LEN); + } else { + wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include " + "WPS IE?!"); + sel_uuid = NULL; + } + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + struct wpabuf *ie; + if (bss == selected) + continue; + ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (!ie) + continue; + if (!wps_is_selected_pbc_registrar(ie)) { + wpabuf_free(ie); + continue; + } + wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " + MACSTR, MAC2STR(bss->bssid)); + uuid = wps_get_uuid_e(ie); + wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS", + uuid, UUID_LEN); + if (sel_uuid == NULL || uuid == NULL || + os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) { + ret = 1; /* PBC overlap */ + wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: " + MACSTR " and " MACSTR, + MAC2STR(selected->bssid), + MAC2STR(bss->bssid)); + wpabuf_free(ie); + break; + } + + /* TODO: verify that this is reasonable dual-band situation */ + + wpabuf_free(ie); + } + + wpabuf_free(wps_ie); + + return ret; +} + + +void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + unsigned int pbc = 0, auth = 0, pin = 0, wps = 0; + + if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED) + return; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + struct wpabuf *ie; + ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (!ie) + continue; + if (wps_is_selected_pbc_registrar(ie)) + pbc++; + else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0)) + auth++; + else if (wps_is_selected_pin_registrar(ie)) + pin++; + else + wps++; + wpabuf_free(ie); + } + + if (pbc) + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC); + else if (auth) + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH); + else if (pin) + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN); + else if (wps) + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE); +} + + +int wpas_wps_searching(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled) + return 1; + } + + return 0; +} + + +int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end) +{ + struct wpabuf *wps_ie; + int ret; + + wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA); + if (wps_ie == NULL) + return 0; + + ret = wps_attr_text(wps_ie, buf, end); + wpabuf_free(wps_ie); + return ret; +} + + +int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter) +{ +#ifdef CONFIG_WPS_ER + if (wpa_s->wps_er) { + wps_er_refresh(wpa_s->wps_er); + return 0; + } + wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter); + if (wpa_s->wps_er == NULL) + return -1; + return 0; +#else /* CONFIG_WPS_ER */ + return 0; +#endif /* CONFIG_WPS_ER */ +} + + +int wpas_wps_er_stop(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WPS_ER + wps_er_deinit(wpa_s->wps_er, NULL, NULL); + wpa_s->wps_er = NULL; +#endif /* CONFIG_WPS_ER */ + return 0; +} + + +#ifdef CONFIG_WPS_ER +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr, + const char *uuid, const char *pin) +{ + u8 u[UUID_LEN]; + int any = 0; + + if (os_strcmp(uuid, "any") == 0) + any = 1; + else if (uuid_str2bin(uuid, u)) + return -1; + return wps_registrar_add_pin(wpa_s->wps->registrar, addr, + any ? NULL : u, + (const u8 *) pin, os_strlen(pin), 300); +} + + +int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid) +{ + u8 u[UUID_LEN]; + + if (uuid_str2bin(uuid, u)) + return -1; + return wps_er_pbc(wpa_s->wps_er, u); +} + + +int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, + const char *pin) +{ + u8 u[UUID_LEN]; + + if (uuid_str2bin(uuid, u)) + return -1; + return wps_er_learn(wpa_s->wps_er, u, (const u8 *) pin, + os_strlen(pin)); +} + + +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, + int id) +{ + u8 u[UUID_LEN]; + struct wpa_ssid *ssid; + struct wps_credential cred; + + if (uuid_str2bin(uuid, u)) + return -1; + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->ssid == NULL) + return -1; + + os_memset(&cred, 0, sizeof(cred)); + if (ssid->ssid_len > 32) + return -1; + os_memcpy(cred.ssid, ssid->ssid, ssid->ssid_len); + cred.ssid_len = ssid->ssid_len; + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + cred.auth_type = (ssid->proto & WPA_PROTO_RSN) ? + WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK; + if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) + cred.encr_type = WPS_ENCR_AES; + else + cred.encr_type = WPS_ENCR_TKIP; + if (ssid->passphrase) { + cred.key_len = os_strlen(ssid->passphrase); + if (cred.key_len >= 64) + return -1; + os_memcpy(cred.key, ssid->passphrase, cred.key_len); + } else if (ssid->psk_set) { + cred.key_len = 32; + os_memcpy(cred.key, ssid->psk, 32); + } else + return -1; + } else { + cred.auth_type = WPS_AUTH_OPEN; + cred.encr_type = WPS_ENCR_NONE; + } + return wps_er_set_config(wpa_s->wps_er, u, &cred); +} + + +int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, + const char *pin, struct wps_new_ap_settings *settings) +{ + u8 u[UUID_LEN]; + struct wps_credential cred; + size_t len; + + if (uuid_str2bin(uuid, u)) + return -1; + if (settings->ssid_hex == NULL || settings->auth == NULL || + settings->encr == NULL || settings->key_hex == NULL) + return -1; + + os_memset(&cred, 0, sizeof(cred)); + len = os_strlen(settings->ssid_hex); + if ((len & 1) || len > 2 * sizeof(cred.ssid) || + hexstr2bin(settings->ssid_hex, cred.ssid, len / 2)) + return -1; + cred.ssid_len = len / 2; + + len = os_strlen(settings->key_hex); + if ((len & 1) || len > 2 * sizeof(cred.key) || + hexstr2bin(settings->key_hex, cred.key, len / 2)) + return -1; + cred.key_len = len / 2; + + if (os_strcmp(settings->auth, "OPEN") == 0) + cred.auth_type = WPS_AUTH_OPEN; + else if (os_strcmp(settings->auth, "WPAPSK") == 0) + cred.auth_type = WPS_AUTH_WPAPSK; + else if (os_strcmp(settings->auth, "WPA2PSK") == 0) + cred.auth_type = WPS_AUTH_WPA2PSK; + else + return -1; + + if (os_strcmp(settings->encr, "NONE") == 0) + cred.encr_type = WPS_ENCR_NONE; + else if (os_strcmp(settings->encr, "WEP") == 0) + cred.encr_type = WPS_ENCR_WEP; + else if (os_strcmp(settings->encr, "TKIP") == 0) + cred.encr_type = WPS_ENCR_TKIP; + else if (os_strcmp(settings->encr, "CCMP") == 0) + cred.encr_type = WPS_ENCR_AES; + else + return -1; + + return wps_er_config(wpa_s->wps_er, u, (const u8 *) pin, + os_strlen(pin), &cred); +} + + +static int callbacks_pending = 0; + +static void wpas_wps_terminate_cb(void *ctx) +{ + wpa_printf(MSG_DEBUG, "WPS ER: Terminated"); + if (--callbacks_pending <= 0) + eloop_terminate(); +} +#endif /* CONFIG_WPS_ER */ + + +int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WPS_ER + if (wpa_s->wps_er) { + callbacks_pending++; + wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s); + wpa_s->wps_er = NULL; + return 1; + } +#endif /* CONFIG_WPS_ER */ + return 0; +} + + +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; + + if (wps == NULL) + return; + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) { + wps->config_methods = wps_config_methods_str2bin( + wpa_s->conf->config_methods); + if ((wps->config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) == + (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) { + wpa_printf(MSG_ERROR, "WPS: Both Label and Display " + "config methods are not allowed at the " + "same time"); + wps->config_methods &= ~WPS_CONFIG_LABEL; + } + } + wps->config_methods = wps_fix_config_methods(wps->config_methods); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) + os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) { + wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types; + os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type, + wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION) + wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID) + wpas_wps_set_uuid(wpa_s, wps); + + if (wpa_s->conf->changed_parameters & + (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) { + /* Update pointers to make sure they refer current values */ + wps->dev.device_name = wpa_s->conf->device_name; + wps->dev.manufacturer = wpa_s->conf->manufacturer; + wps->dev.model_name = wpa_s->conf->model_name; + wps->dev.model_number = wpa_s->conf->model_number; + wps->dev.serial_number = wpa_s->conf->serial_number; + } +} diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h new file mode 100644 index 0000000..b38c091 --- /dev/null +++ b/wpa_supplicant/wps_supplicant.h @@ -0,0 +1,125 @@ +/* + * wpa_supplicant / WPS integration + * Copyright (c) 2008-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. + */ + +#ifndef WPS_SUPPLICANT_H +#define WPS_SUPPLICANT_H + +struct wpa_scan_res; + +#ifdef CONFIG_WPS + +#include "wps/wps.h" +#include "wps/wps_defs.h" + +struct wpa_bss; + +struct wps_new_ap_settings { + const char *ssid_hex; + const char *auth; + const char *encr; + const char *key_hex; +}; + +int wpas_wps_init(struct wpa_supplicant *wpa_s); +void wpas_wps_deinit(struct wpa_supplicant *wpa_s); +int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s); +enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid); +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, + int p2p_group); +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin, int p2p_group, u16 dev_pw_id); +int wpas_wps_cancel(struct wpa_supplicant *wpa_s); +int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, + char *path, char *method, char *name); +int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin, struct wps_new_ap_settings *settings); +int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_scan_res *bss); +int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_scan_res *bss); +int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, struct wpa_ssid *ssid); +void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s); +int wpas_wps_searching(struct wpa_supplicant *wpa_s); +int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos, + char *end); +int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter); +int wpas_wps_er_stop(struct wpa_supplicant *wpa_s); +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr, + const char *uuid, const char *pin); +int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid); +int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, + const char *pin); +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, + int id); +int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, + const char *pin, struct wps_new_ap_settings *settings); +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); + +#else /* CONFIG_WPS */ + +static inline int wpas_wps_init(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void wpas_wps_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid) +{ + return 0; +} + +static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + return -1; +} + +static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_scan_res *bss) +{ + return 0; +} + +static inline int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid) +{ + return 0; +} + +static inline void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s) +{ +} + +static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +#endif /* CONFIG_WPS */ + +#endif /* WPS_SUPPLICANT_H */ |