diff options
Diffstat (limited to 'contrib')
153 files changed, 31534 insertions, 5319 deletions
diff --git a/contrib/wpa_supplicant/ChangeLog b/contrib/wpa_supplicant/ChangeLog index 09d5b61..4ed9e26 100644 --- a/contrib/wpa_supplicant/ChangeLog +++ b/contrib/wpa_supplicant/ChangeLog @@ -1,32 +1,294 @@ ChangeLog for wpa_supplicant -2005-06-10 - v0.3.9 - * 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 - * 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 - * fixed a possible double free in EAP-TTLS fast-reauthentication when - identity or password is entered through control interface - * added -P<pid file> argument for wpa_supplicant to write the current - process id into a file - * driver_madwifi: fixed association in plaintext mode - * driver_madwifi: added preliminary support for compiling against 'BSD' - branch of madwifi CVS tree - * added EAP workaround for PEAPv1 session resumption: allow outer, +2006-02-08 - v0.4.8 + * 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 support for EAP-FAST key derivation using other ciphers than + RC4-128-SHA for authentication and AES128-SHA for provisioning + * fixed EAP-SIM and EAP-AKA pseudonym and fast re-authentication to + decrypt AT_ENCR_DATA attributes correctly + * 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 + * fixed cygwin build + * 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) + * fixed TLS library deinitialization after RSN pre-authentication not + to disable TLS library for normal authentication + * 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) + * do not try to use USIM APDUs when initializing PC/SC for SIM card + access for a network that has not enabled EAP-AKA + +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-02-13 - v0.3.8 +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 diff --git a/contrib/wpa_supplicant/Makefile b/contrib/wpa_supplicant/Makefile index fa91243..d8fd3ed 100644 --- a/contrib/wpa_supplicant/Makefile +++ b/contrib/wpa_supplicant/Makefile @@ -7,7 +7,7 @@ CFLAGS = -MMD -O2 -Wall -g endif # Include directories for CVS version -CFLAGS += -I../driver/modules -I../utils -I../hostapd +CFLAGS += -I. -I../utils -I../hostapd ALL=wpa_supplicant wpa_passphrase wpa_cli @@ -37,7 +37,7 @@ install: all OBJS = config.o \ eloop.o common.o md5.o \ - rc4.o sha1.o aes_wrap.o + rc4.o sha1.o OBJS_p = wpa_passphrase.o sha1.o md5.o OBJS_c = wpa_cli.o wpa_ctrl.o @@ -47,9 +47,18 @@ ifdef CONFIG_EAPOL_TEST CFLAGS += -Werror -DEAPOL_TEST endif +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.o base64.o +CFLAGS += -DCONFIG_BACKEND_FILE +endif + ifdef CONFIG_DRIVER_HOSTAP CFLAGS += -DCONFIG_DRIVER_HOSTAP -OBJS += driver_hostap.o +OBJS_d += driver_hostap.o CONFIG_WIRELESS_EXTENSION=y endif @@ -60,73 +69,88 @@ endif ifdef CONFIG_DRIVER_PRISM54 CFLAGS += -DCONFIG_DRIVER_PRISM54 -OBJS += driver_prism54.o +OBJS_d += driver_prism54.o CONFIG_WIRELESS_EXTENSION=y endif ifdef CONFIG_DRIVER_HERMES CFLAGS += -DCONFIG_DRIVER_HERMES -OBJS += driver_hermes.o +OBJS_d += driver_hermes.o CONFIG_WIRELESS_EXTENSION=y endif ifdef CONFIG_DRIVER_MADWIFI CFLAGS += -DCONFIG_DRIVER_MADWIFI -OBJS += driver_madwifi.o +OBJS_d += driver_madwifi.o CONFIG_WIRELESS_EXTENSION=y endif ifdef CONFIG_DRIVER_ATMEL CFLAGS += -DCONFIG_DRIVER_ATMEL -OBJS += driver_atmel.o +OBJS_d += driver_atmel.o CONFIG_WIRELESS_EXTENSION=y endif ifdef CONFIG_DRIVER_NDISWRAPPER CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER -OBJS += driver_ndiswrapper.o +OBJS_d += driver_ndiswrapper.o CONFIG_WIRELESS_EXTENSION=y endif ifdef CONFIG_DRIVER_BROADCOM CFLAGS += -DCONFIG_DRIVER_BROADCOM -OBJS += driver_broadcom.o +OBJS_d += driver_broadcom.o endif ifdef CONFIG_DRIVER_IPW CFLAGS += -DCONFIG_DRIVER_IPW -OBJS += driver_ipw.o +OBJS_d += driver_ipw.o CONFIG_WIRELESS_EXTENSION=y endif ifdef CONFIG_DRIVER_BSD CFLAGS += -DCONFIG_DRIVER_BSD -OBJS += driver_bsd.o +OBJS_d += driver_bsd.o CONFIG_DNET_PCAP=y +CONFIG_L2_FREEBSD=y endif ifdef CONFIG_DRIVER_NDIS CFLAGS += -DCONFIG_DRIVER_NDIS -OBJS += driver_ndis.o driver_ndis_.o +OBJS_d += driver_ndis.o driver_ndis_.o CONFIG_DNET_PCAP=y CONFIG_WINPCAP=y endif +ifdef CONFIG_DRIVER_WIRED +CFLAGS += -DCONFIG_DRIVER_WIRED +OBJS_d += driver_wired.o +endif + ifdef CONFIG_DRIVER_TEST CFLAGS += -DCONFIG_DRIVER_TEST -OBJS += driver_test.o +OBJS_d += driver_test.o endif ifdef CONFIG_DNET_PCAP CFLAGS += -DUSE_DNET_PCAP ifdef CONFIG_WINPCAP +OBJS += l2_packet_pcap.o CFLAGS += -DCONFIG_WINPCAP LIBS += -lwpcap -lpacket LIBS_w += -lwpcap else +ifdef CONFIG_L2_FREEBSD +OBJS += l2_packet_freebsd.o +LIBS += -lpcap +else +OBJS += l2_packet_pcap.o LIBS += -ldnet -lpcap endif endif +else +OBJS += l2_packet_linux.o +endif ifdef CONFIG_EAP_TLS # EAP-TLS @@ -209,8 +233,9 @@ endif ifdef CONFIG_EAP_PSK # EAP-PSK CFLAGS += -DEAP_PSK -OBJS += eap_psk.o +OBJS += eap_psk.o eap_psk_common.o CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y endif ifdef CONFIG_EAP_AKA @@ -223,6 +248,7 @@ endif ifdef CONFIG_EAP_SIM_COMMON OBJS += eap_sim_common.o +NEED_AES=y endif ifdef CONFIG_EAP_TLV @@ -238,6 +264,13 @@ OBJS += eap_fast.o TLS_FUNCS=y endif +ifdef CONFIG_EAP_PAX +# EAP-PAX +CFLAGS += -DEAP_PAX +OBJS += eap_pax.o eap_pax_common.o +CONFIG_IEEE8021X_EAPOL=y +endif + ifdef CONFIG_IEEE8021X_EAPOL # IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) CFLAGS += -DIEEE8021X_EAPOL @@ -252,12 +285,38 @@ OBJS += pcsc_funcs.o LIBS += -lpcsclite -lpthread endif +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + ifdef TLS_FUNCS -# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS) +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) CFLAGS += -DEAP_TLS_FUNCS -OBJS += eap_tls_common.o tls_openssl.o +OBJS += eap_tls_common.o +ifeq ($(CONFIG_TLS), openssl) +OBJS += tls_openssl.o LIBS += -lssl -lcrypto LIBS_p += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += tls_gnutls.o +LIBS += -lgnutls -lgcrypt -lgpg-error +LIBS_p += -lgcrypt +endif +ifeq ($(CONFIG_TLS), schannel) +OBJS += tls_schannel.o +# Using OpenSSL for crypto at the moment; to be replaced +LIBS += -lcrypto +LIBS_p += -lcrypto +endif +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifndef CONFIG_L2_FREEBSD +LIBS += -ldl +endif +endif +endif +NEED_CRYPTO=y else OBJS += tls_none.o endif @@ -266,16 +325,49 @@ ifdef CONFIG_PKCS12 CFLAGS += -DPKCS12_FUNCS endif +ifdef CONFIG_SMARTCARD +CFLAGS += -DCONFIG_SMARTCARD +endif + ifdef MS_FUNCS +OBJS += ms_funcs.o +NEED_CRYPTO=y +endif + +ifdef NEED_CRYPTO ifndef TLS_FUNCS +ifeq ($(CONFIG_TLS), openssl) LIBS += -lcrypto +LIBS_p += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +LIBS += -lgcrypt +LIBS_p += -lgcrypt +endif +ifeq ($(CONFIG_TLS), schannel) +# Using OpenSSL for crypto at the moment; to be replaced +LIBS += -lcrypto +LIBS_p += -lcrypto +endif +endif +ifeq ($(CONFIG_TLS), openssl) +OBJS += crypto.o +OBJS_p += crypto.o +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += crypto_gnutls.o +OBJS_p += crypto_gnutls.o +endif +ifeq ($(CONFIG_TLS), schannel) +# Using OpenSSL for crypto at the moment; to be replaced +OBJS += crypto.o +OBJS_p += crypto.o endif -OBJS += ms_funcs.o crypto.o endif ifdef CONFIG_WIRELESS_EXTENSION CFLAGS += -DCONFIG_WIRELESS_EXTENSION -OBJS += driver_wext.o +OBJS_d += driver_wext.o endif ifdef CONFIG_CTRL_IFACE @@ -283,10 +375,6 @@ CFLAGS += -DCONFIG_CTRL_IFACE OBJS += ctrl_iface.o endif -ifdef CONFIG_XSUPPLICANT_IFACE -CFLAGS += -DCONFIG_XSUPPLICANT_IFACE -endif - ifdef CONFIG_READLINE CFLAGS += -DCONFIG_READLINE LIBS_c += -lncurses -lreadline @@ -294,13 +382,34 @@ endif ifdef CONFIG_NATIVE_WINDOWS CFLAGS += -DCONFIG_NATIVE_WINDOWS -DCONFIG_CTRL_IFACE_UDP -LIBS += -lws2_32 -lgdi32 +LIBS += -lws2_32 -lgdi32 -lcrypt32 LIBS_c += -lws2_32 endif +ifdef CONFIG_NO_STDOUT_DEBUG +CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +CFLAGS += -DCONFIG_IPV6 +endif + +ifndef CONFIG_NO_WPA +OBJS += wpa.o preauth.o +NEED_AES=y +else +CFLAGS += -DCONFIG_NO_WPA +endif + +ifdef NEED_AES +OBJS += aes_wrap.o +endif + +OBJS += wpa_supplicant.o events.o OBJS_t := $(OBJS) eapol_test.o radius.o radius_client.o -OBJS_t2 := $(OBJS) preauth_test.o l2_packet.o -OBJS += wpa_supplicant.o wpa.o l2_packet.o drivers.o +OBJS_t2 := $(OBJS) preauth_test.o +OBJS += main.o drivers.o $(OBJS_d) wpa_supplicant: .config $(OBJS) $(CC) -o wpa_supplicant $(OBJS) $(LIBS) @@ -342,30 +451,38 @@ 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 + TEST_SRC_MS_FUNCS = ms_funcs.c crypto.c sha1.c md5.c test-ms_funcs: $(TEST_SRC_MS_FUNCS) $(CC) -o test-ms_funcs -Wall -Werror $(TEST_SRC_MS_FUNCS) \ - -DTEST_MAIN_MS_FUNCS -lcrypto -I../hostapd + -DTEST_MAIN_MS_FUNCS -lcrypto -I../hostapd -I. ./test-ms_funcs rm test-ms_funcs TEST_SRC_SHA1 = sha1.c test-sha1: $(TEST_SRC_SHA1) $(CC) -o test-sha1 -Wall -Werror $(TEST_SRC_SHA1) \ - -DTEST_MAIN -I../hostad + -DTEST_MAIN -I../hostad -I. ./test-sha1 rm test-sha1 TEST_SRC_AES_WRAP = aes_wrap.c test-aes_wrap: $(TEST_SRC_AES_WRAP) $(CC) -o test-aes_wrap -Wall -Werror $(TEST_SRC_AES_WRAP) \ - -DTEST_MAIN -I../hostad + -DTEST_MAIN -I../hostad -I. ./test-aes_wrap rm test-aes_wrap @@ -373,7 +490,7 @@ TEST_SRC_EAP_SIM_COMMON = eap_sim_common.c sha1.c md5.c \ aes_wrap.c common.c test-eap_sim_common: $(TEST_SRC_EAP_SIM_COMMON) $(CC) -o test-eap_sim_common -Wall -Werror $(TEST_SRC_EAP_SIM_COMMON) \ - -DTEST_MAIN_EAP_SIM_COMMON -I../hostapd + -DTEST_MAIN_EAP_SIM_COMMON -I../hostapd -I. ./test-eap_sim_common rm test-eap_sim_common @@ -382,4 +499,25 @@ tests: test-ms_funcs test-sha1 test-aes_wrap test-eap_sim_common clean: rm -f core *~ *.o *.d $(ALL) $(WINALL) +%.eps: %.fig + fig2dev -L eps $*.fig $*.eps + +%.png: %.fig + fig2dev -L png -m 3 $*.fig | pngtopnm | pnmscale 0.4 | pnmtopng \ + > $*.png + +docs-pics: doc/wpa_supplicant.png doc/wpa_supplicant.eps + +docs: docs-pics + doxygen doc/doxygen.full + $(MAKE) -C doc/latex + cp doc/latex/refman.pdf wpa_supplicant-devel.pdf + +docs-fast: docs-pics + doxygen doc/doxygen.fast + +clean-docs: + rm -rf doc/latex doc/html + rm -f doc/wpa_supplicant.{eps,png} wpa_supplicant-devel.pdf + -include $(OBJS:%.o=%.d) diff --git a/contrib/wpa_supplicant/README b/contrib/wpa_supplicant/README index bab25d5..831756b 100644 --- a/contrib/wpa_supplicant/README +++ b/contrib/wpa_supplicant/README @@ -1,7 +1,7 @@ WPA Supplicant ============== -Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> and +Copyright (c) 2003-2006, Jouni Malinen <jkmaline@cc.hut.fi> and contributors All Rights Reserved. @@ -89,6 +89,7 @@ Supported WPA/IEEE 802.11i features: * EAP-SIM * EAP-AKA * EAP-PSK + * EAP-PAX * LEAP (note: requires special support from the driver for IEEE 802.11 authentication) (following methods are supported, but since they do not generate keying @@ -97,8 +98,6 @@ Supported WPA/IEEE 802.11i features: * EAP-MSCHAPv2 * EAP-GTC * EAP-OTP - Alternatively, an external program, e.g., Xsupplicant, can be used for EAP - authentication. - key management for CCMP, TKIP, WEP104, WEP40 - RSN/WPA2 (IEEE 802.11i) * pre-authentication @@ -112,6 +111,7 @@ 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: Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x) @@ -164,8 +164,10 @@ Current hardware/software requirements: 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. + 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/). @@ -173,7 +175,8 @@ Current hardware/software requirements: 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 +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 @@ -221,8 +224,7 @@ networks that require some kind of security. Task group I (Security) of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked to address the flaws of the base standard and has in practice completed its work in May 2004. The IEEE 802.11i amendment to the IEEE -802.11 standard was approved in June 2004 and this amendment is likely -to be published in July 2004. +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 @@ -277,14 +279,6 @@ robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) to replace TKIP and optimizations for handoff (reduced number of messages in initial key handshake, pre-authentication, and PMKSA caching). -Some wireless LAN vendors are already providing support for CCMP in -their WPA products. There is no "official" interoperability -certification for CCMP and/or mixed modes using both TKIP and CCMP, so -some interoperability issues can be expected even though many -combinations seem to be working with equipment from different vendors. -Certification for WPA2 is likely to start during the second half of -2004. - wpa_supplicant @@ -307,9 +301,9 @@ Following steps are used when associating with an AP using WPA: - 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 or external Xsupplicant - completes EAP authentication with the authentication server (proxied - by the Authenticator in the AP) +- 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 @@ -352,6 +346,7 @@ CONFIG_EAP_OTP=y CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_EAP_PSK=y +CONFIG_EAP_PAX=y CONFIG_EAP_LEAP=y Following option can be used to include GSM SIM/USIM interface for GSM/UMTS @@ -366,13 +361,12 @@ interface with libpcap/libdnet. CONFIG_DNET_PCAP=y Following options can be added to .config to select which driver -interfaces are included. Prism54.org driver is not yet complete and -Hermes driver interface needs to be downloaded from Agere (see above). -Most Linux driver need to include CONFIG_WIRELESS_EXTENSION. +interfaces are included. Hermes driver interface needs to be downloaded +from Agere (see above). CONFIG_WIRELESS_EXTENSION will be used +automatically if any of the selected drivers need it. CONFIG_WIRELESS_EXTENSION=y CONFIG_DRIVER_HOSTAP=y -CONFIG_DRIVER_PRISM54=y CONFIG_DRIVER_HERMES=y CONFIG_DRIVER_MADWIFI=y CONFIG_DRIVER_ATMEL=y @@ -387,7 +381,6 @@ Following example includes all features and driver interfaces that are included in the wpa_supplicant package: CONFIG_DRIVER_HOSTAP=y -CONFIG_DRIVER_PRISM54=y CONFIG_DRIVER_HERMES=y CONFIG_DRIVER_MADWIFI=y CONFIG_DRIVER_ATMEL=y @@ -409,6 +402,7 @@ CONFIG_EAP_OTP=y CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_EAP_PSK=y +CONFIG_EAP_PAX=y CONFIG_EAP_LEAP=y CONFIG_PCSC=y @@ -463,8 +457,6 @@ options: -d = increase debugging verbosity (-dd even more) -K = include keys (passwords, etc.) in debug output -t = include timestamp in debug messages - -e = use external IEEE 802.1X Supplicant (e.g., xsupplicant) - (this disables the internal Supplicant) -h = show this help text -L = show license (GPL and BSD) -q = decrease debugging verbosity (-qq even less) @@ -475,8 +467,6 @@ options: drivers: hostap = Host AP driver (Intersil Prism2/2.5/3) [default] (this can also be used with Linuxant DriverLoader) - prism54 = Prism54.org driver (Intersil Prism GT/Duette/Indigo) - not yet fully implemented hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II) madwifi = MADWIFI 802.11 support (Atheros, etc.) atmel = ATMEL AT76C5XXx (USB, PCMCIA) @@ -484,6 +474,7 @@ drivers: ndiswrapper = Linux ndiswrapper broadcom = Broadcom wl.o driver ipw = Intel ipw2100/2200 driver + wired = wpa_supplicant wired Ethernet driver bsd = BSD 802.11 support (Atheros, etc.) ndis = Windows NDIS driver @@ -647,6 +638,21 @@ network={ } +6) Authentication for wired Ethernet. This can be used with 'wired' interface + (-Dwired 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 ------------ @@ -681,7 +687,7 @@ 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 configuring some +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 @@ -757,11 +763,83 @@ wpa_cli commands 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 +fi + + Integrating with pcmcia-cs/cardmgr scripts ------------------------------------------ @@ -804,55 +882,38 @@ started--and will then negotiate keys with the AP. -Optional integration with Xsupplicant -------------------------------------- - -wpa_supplicant has an integrated IEEE 802.1X Supplicant that supports -most commonly used EAP methods. In addition, wpa_supplicant has an -experimental interface for integrating it with Xsupplicant -(http://www.open1x.org/) for the WPA with EAP authentication. - -When using WPA-EAP, both wpa_supplicant and Xsupplicant must be -configured with the network security policy. See Xsupplicant documents -for information about its configuration. Please also note, that a new -command line option -W (enable WPA) must be used when starting -xsupplicant. - -Example configuration for xsupplicant: - -network_list = all -default_netname = jkm - -jkm -{ - type = wireless - allow_types = eap_peap - identity = <BEGIN_ID>jkm<END_ID> - eap-peap { - random_file = /dev/urandom - root_cert = /home/jkm/CA.pem - chunk_size = 1398 - allow_types = eap_mschapv2 - eap-mschapv2 { - username = <BEGIN_UNAME>jkm<END_UNAME> - password = <BEGIN_PASS>jkm<END_PASS> - } - } -} +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): -Example configuration for wpa_supplicant: +# Start wpa_supplicant in the background +wpa_supplicant -g/var/run/wpa_supplicant-global -B -network={ - ssid="jkm" - key_mgmt=WPA-EAP -} +# 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 -Both wpa_supplicant and xsupplicant need to be started. Please remember -to add '-W' option for xsupplicant in order to provide keying material -for wpa_supplicant and '-e' option for wpa_supplicant to disable internal -IEEE 802.1X implementation. +# At this point, the new network interface should start trying to associate +# with the WPA-PSK network using SSID test. -wpa_supplicant -iwlan0 -cwpa_supplicant.conf -e -xsupplicant -iwlan0 -cxsupplicant.conf -W +# Remove network interface +wpa_cli -g/var/run/wpa_supplicant-global interface_remove wlan0 diff --git a/contrib/wpa_supplicant/aes.c b/contrib/wpa_supplicant/aes.c index eabebd0..ce94778 100644 --- a/contrib/wpa_supplicant/aes.c +++ b/contrib/wpa_supplicant/aes.c @@ -1,8 +1,15 @@ /* + * AES (Rijndael) cipher + * * Modifications to public domain implementation: * - support only 128-bit keys * - cleanup - * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * - use C pre-processor to make it easier to change S table access + * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at + * cost of reduced throughput (quite small difference on Pentium 4, + * 10-25% when using -O1 or -O2 optimization) + * + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -14,7 +21,7 @@ * See README and COPYING for more details. */ -/** +/* * rijndael-alg-fst.c * * @version 3.0 (December 2000) @@ -40,7 +47,8 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define FULL_UNROLL +/* #define FULL_UNROLL */ +#define AES_SMALL_TABLES /* @@ -123,6 +131,7 @@ static const u32 Te0[256] = { 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; +#ifndef AES_SMALL_TABLES static const u32 Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, @@ -388,6 +397,7 @@ static const u32 Te4[256] = { 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; +#endif /* AES_SMALL_TABLES */ static const u32 Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, @@ -454,6 +464,7 @@ static const u32 Td0[256] = { 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; +#ifndef AES_SMALL_TABLES static const u32 Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, @@ -724,6 +735,116 @@ static const u32 rcon[] = { 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; +#else /* AES_SMALL_TABLES */ +static const u8 Td4s[256] = { + 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, +}; +static const u8 rcons[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 + /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; +#endif /* AES_SMALL_TABLES */ + + +#ifndef AES_SMALL_TABLES + +#define RCON(i) rcon[(i)] + +#define TE0(i) Te0[((i) >> 24) & 0xff] +#define TE1(i) Te1[((i) >> 16) & 0xff] +#define TE2(i) Te2[((i) >> 8) & 0xff] +#define TE3(i) Te3[(i) & 0xff] +#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) +#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff) +#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000) +#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) +#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) +#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) +#define TE4(i) (Te4[(i)] & 0x000000ff) + +#define TD0(i) Td0[((i) >> 24) & 0xff] +#define TD1(i) Td1[((i) >> 16) & 0xff] +#define TD2(i) Td2[((i) >> 8) & 0xff] +#define TD3(i) Td3[(i) & 0xff] +#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000) +#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff) +#define TD0_(i) Td0[(i) & 0xff] +#define TD1_(i) Td1[(i) & 0xff] +#define TD2_(i) Td2[(i) & 0xff] +#define TD3_(i) Td3[(i) & 0xff] + +#else /* AES_SMALL_TABLES */ + +#define RCON(i) (rcons[(i)] << 24) + +static inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + +#define TE0(i) Te0[((i) >> 24) & 0xff] +#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) +#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) +#define TE3(i) rotr(Te0[(i) & 0xff], 24) +#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) +#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) +#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) +#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) +#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) +#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) +#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) + +#define TD0(i) Td0[((i) >> 24) & 0xff] +#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) +#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) +#define TD3(i) rotr(Td0[(i) & 0xff], 24) +#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) +#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) +#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) +#define TD44(i) (Td4s[(i) & 0xff]) +#define TD0_(i) Td0[(i) & 0xff] +#define TD1_(i) rotr(Td0[(i) & 0xff], 8) +#define TD2_(i) rotr(Td0[(i) & 0xff], 16) +#define TD3_(i) rotr(Td0[(i) & 0xff], 24) + +#endif /* AES_SMALL_TABLES */ #define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) @@ -755,11 +876,8 @@ void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) for (i = 0; i < 10; i++) { temp = rk[3]; rk[4] = rk[0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp ) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; + TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ + RCON(i); rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; @@ -790,33 +908,19 @@ void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) * first and the last: */ for (i = 1; i < Nr; i++) { rk += 4; - rk[0] = - Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[0] ) & 0xff] & 0xff]; - rk[1] = - Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[1] ) & 0xff] & 0xff]; - rk[2] = - Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[2] ) & 0xff] & 0xff]; - rk[3] = - Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + for (j = 0; j < 4; j++) { + rk[j] = TD0_(TE4((rk[j] >> 24) )) ^ + TD1_(TE4((rk[j] >> 16) & 0xff)) ^ + TD2_(TE4((rk[j] >> 8) & 0xff)) ^ + TD3_(TE4((rk[j] ) & 0xff)); + } } } void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; - int Nr = 10; + const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ @@ -829,153 +933,61 @@ void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) s1 = GETU32(pt + 4) ^ rk[1]; s2 = GETU32(pt + 8) ^ rk[2]; s3 = GETU32(pt + 12) ^ rk[3]; + +#define ROUND(i,d,s) \ +d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \ +d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \ +d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \ +d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] + #ifdef FULL_UNROLL - /* round 1: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; - /* round 2: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; - /* round 3: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; - /* round 4: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; - /* round 5: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; - /* round 6: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; - /* round 7: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; - /* round 8: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; - /* round 9: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; - rk += Nr << 2; + + ROUND(1,t,s); + ROUND(2,s,t); + ROUND(3,t,s); + ROUND(4,s,t); + ROUND(5,t,s); + ROUND(6,s,t); + ROUND(7,t,s); + ROUND(8,s,t); + ROUND(9,t,s); + + rk += Nr << 2; + #else /* !FULL_UNROLL */ - /* - * Nr - 1 full rounds: - */ - r = Nr >> 1; - for (;;) { - t0 = - Te0[(s0 >> 24) ] ^ - Te1[(s1 >> 16) & 0xff] ^ - Te2[(s2 >> 8) & 0xff] ^ - Te3[(s3 ) & 0xff] ^ - rk[4]; - t1 = - Te0[(s1 >> 24) ] ^ - Te1[(s2 >> 16) & 0xff] ^ - Te2[(s3 >> 8) & 0xff] ^ - Te3[(s0 ) & 0xff] ^ - rk[5]; - t2 = - Te0[(s2 >> 24) ] ^ - Te1[(s3 >> 16) & 0xff] ^ - Te2[(s0 >> 8) & 0xff] ^ - Te3[(s1 ) & 0xff] ^ - rk[6]; - t3 = - Te0[(s3 >> 24) ] ^ - Te1[(s0 >> 16) & 0xff] ^ - Te2[(s1 >> 8) & 0xff] ^ - Te3[(s2 ) & 0xff] ^ - rk[7]; - rk += 8; - if (--r == 0) { - break; - } + /* Nr - 1 full rounds: */ + r = Nr >> 1; + for (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } - s0 = - Te0[(t0 >> 24) ] ^ - Te1[(t1 >> 16) & 0xff] ^ - Te2[(t2 >> 8) & 0xff] ^ - Te3[(t3 ) & 0xff] ^ - rk[0]; - s1 = - Te0[(t1 >> 24) ] ^ - Te1[(t2 >> 16) & 0xff] ^ - Te2[(t3 >> 8) & 0xff] ^ - Te3[(t0 ) & 0xff] ^ - rk[1]; - s2 = - Te0[(t2 >> 24) ] ^ - Te1[(t3 >> 16) & 0xff] ^ - Te2[(t0 >> 8) & 0xff] ^ - Te3[(t1 ) & 0xff] ^ - rk[2]; - s3 = - Te0[(t3 >> 24) ] ^ - Te1[(t0 >> 16) & 0xff] ^ - Te2[(t1 >> 8) & 0xff] ^ - Te3[(t2 ) & 0xff] ^ - rk[3]; - } #endif /* ?FULL_UNROLL */ - /* + +#undef ROUND + + /* * apply last round and * map cipher state to byte array block: */ - s0 = - (Te4[(t0 >> 24) ] & 0xff000000) ^ - (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[0]; + s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; PUTU32(ct , s0); - s1 = - (Te4[(t1 >> 24) ] & 0xff000000) ^ - (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[1]; + s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; PUTU32(ct + 4, s1); - s2 = - (Te4[(t2 >> 24) ] & 0xff000000) ^ - (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[2]; + s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; PUTU32(ct + 8, s2); - s3 = - (Te4[(t3 >> 24) ] & 0xff000000) ^ - (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[3]; + s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; PUTU32(ct + 12, s3); } void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; - int Nr = 10; + const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ @@ -984,149 +996,110 @@ void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) * map byte array block to cipher state * and add initial round key: */ - s0 = GETU32(ct ) ^ rk[0]; - s1 = GETU32(ct + 4) ^ rk[1]; - s2 = GETU32(ct + 8) ^ rk[2]; - s3 = GETU32(ct + 12) ^ rk[3]; + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; + +#define ROUND(i,d,s) \ +d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \ +d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \ +d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \ +d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] + #ifdef FULL_UNROLL - /* round 1: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; - /* round 2: */ - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; - /* round 3: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; - /* round 4: */ - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; - /* round 5: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; - /* round 6: */ - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; - /* round 7: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; - /* round 8: */ - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; - /* round 9: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + + ROUND(1,t,s); + ROUND(2,s,t); + ROUND(3,t,s); + ROUND(4,s,t); + ROUND(5,t,s); + ROUND(6,s,t); + ROUND(7,t,s); + ROUND(8,s,t); + ROUND(9,t,s); + rk += Nr << 2; + #else /* !FULL_UNROLL */ - /* - * Nr - 1 full rounds: - */ - r = Nr >> 1; - for (;;) { - t0 = - Td0[(s0 >> 24) ] ^ - Td1[(s3 >> 16) & 0xff] ^ - Td2[(s2 >> 8) & 0xff] ^ - Td3[(s1 ) & 0xff] ^ - rk[4]; - t1 = - Td0[(s1 >> 24) ] ^ - Td1[(s0 >> 16) & 0xff] ^ - Td2[(s3 >> 8) & 0xff] ^ - Td3[(s2 ) & 0xff] ^ - rk[5]; - t2 = - Td0[(s2 >> 24) ] ^ - Td1[(s1 >> 16) & 0xff] ^ - Td2[(s0 >> 8) & 0xff] ^ - Td3[(s3 ) & 0xff] ^ - rk[6]; - t3 = - Td0[(s3 >> 24) ] ^ - Td1[(s2 >> 16) & 0xff] ^ - Td2[(s1 >> 8) & 0xff] ^ - Td3[(s0 ) & 0xff] ^ - rk[7]; - rk += 8; - if (--r == 0) { - break; - } + /* Nr - 1 full rounds: */ + r = Nr >> 1; + for (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } - s0 = - Td0[(t0 >> 24) ] ^ - Td1[(t3 >> 16) & 0xff] ^ - Td2[(t2 >> 8) & 0xff] ^ - Td3[(t1 ) & 0xff] ^ - rk[0]; - s1 = - Td0[(t1 >> 24) ] ^ - Td1[(t0 >> 16) & 0xff] ^ - Td2[(t3 >> 8) & 0xff] ^ - Td3[(t2 ) & 0xff] ^ - rk[1]; - s2 = - Td0[(t2 >> 24) ] ^ - Td1[(t1 >> 16) & 0xff] ^ - Td2[(t0 >> 8) & 0xff] ^ - Td3[(t3 ) & 0xff] ^ - rk[2]; - s3 = - Td0[(t3 >> 24) ] ^ - Td1[(t2 >> 16) & 0xff] ^ - Td2[(t1 >> 8) & 0xff] ^ - Td3[(t0 ) & 0xff] ^ - rk[3]; - } #endif /* ?FULL_UNROLL */ - /* + +#undef ROUND + + /* * apply last round and * map cipher state to byte array block: */ - s0 = - (Td4[(t0 >> 24) ] & 0xff000000) ^ - (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[0]; + s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; PUTU32(pt , s0); - s1 = - (Td4[(t1 >> 24) ] & 0xff000000) ^ - (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[1]; + s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; PUTU32(pt + 4, s1); - s2 = - (Td4[(t2 >> 24) ] & 0xff000000) ^ - (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[2]; + s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; PUTU32(pt + 8, s2); - s3 = - (Td4[(t3 >> 24) ] & 0xff000000) ^ - (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[3]; + s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; PUTU32(pt + 12, s3); } + + + +/* Generic wrapper functions for AES functions */ + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + if (len != 16) + return NULL; + rk = malloc(4 * 44); + if (rk == NULL) + return NULL; + rijndaelKeySetupEnc(rk, key); + return rk; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + rijndaelEncrypt(ctx, plain, crypt); +} + + +void aes_encrypt_deinit(void *ctx) +{ + free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + if (len != 16) + return NULL; + rk = malloc(4 * 44); + if (rk == NULL) + return NULL; + rijndaelKeySetupDec(rk, key); + return rk; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + rijndaelDecrypt(ctx, crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + free(ctx); +} diff --git a/contrib/wpa_supplicant/aes_wrap.c b/contrib/wpa_supplicant/aes_wrap.c index dbcc136..a5925ca 100644 --- a/contrib/wpa_supplicant/aes_wrap.c +++ b/contrib/wpa_supplicant/aes_wrap.c @@ -1,10 +1,13 @@ /* - * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * One-Key CBC MAC (OMAC1) hash with AES-128 - * AES-128 CTR mode encryption - * AES-128 EAX mode encryption/decryption - * AES-128 CBC - * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * AES-based functions + * + * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * - One-Key CBC MAC (OMAC1) hash with AES-128 + * - AES-128 CTR mode encryption + * - AES-128 EAX mode encryption/decryption + * - AES-128 CBC + * + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -21,43 +24,26 @@ #include <string.h> #include "common.h" #include "aes_wrap.h" +#include "crypto.h" -#ifdef EAP_TLS_FUNCS - -#include <openssl/aes.h> - -#else /* EAP_TLS_FUNCS */ - +#ifndef EAP_TLS_FUNCS #include "aes.c" - -struct aes_key_st { - u32 rk[44]; -}; -typedef struct aes_key_st AES_KEY; - -#define AES_set_encrypt_key(userKey, bits, key) \ - rijndaelKeySetupEnc((key)->rk, (userKey)) -#define AES_set_decrypt_key(userKey, bits, key) \ - rijndaelKeySetupDec((key)->rk, (userKey)) -#define AES_encrypt(in, out, key) \ - rijndaelEncrypt((key)->rk, in, out) -#define AES_decrypt(in, out, key) \ - rijndaelDecrypt((key)->rk, in, out) - #endif /* EAP_TLS_FUNCS */ -/* - * @kek: key encryption key (KEK) - * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes - * @plain: plaintext key to be wrapped, n * 64 bit - * @cipher: wrapped key, (n + 1) * 64 bit +/** + * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: Key encryption key (KEK) + * @n: Length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @plain: Plaintext key to be wrapped, n * 64 bit + * @cipher: Wrapped key, (n + 1) * 64 bit + * Returns: 0 on success, -1 on failure */ -void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher) +int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) { u8 *a, *r, b[16]; int i, j; - AES_KEY key; + void *ctx; a = cipher; r = cipher + 8; @@ -66,7 +52,9 @@ void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher) memset(a, 0xa6, 8); memcpy(r, plain, 8 * n); - AES_set_encrypt_key(kek, 128, &key); + ctx = aes_encrypt_init(kek, 16); + if (ctx == NULL) + return -1; /* 2) Calculate intermediate values. * For j = 0 to 5 @@ -80,40 +68,47 @@ void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher) for (i = 1; i <= n; i++) { memcpy(b, a, 8); memcpy(b + 8, r, 8); - AES_encrypt(b, b, &key); + aes_encrypt(ctx, b, b); memcpy(a, b, 8); a[7] ^= n * j + i; memcpy(r, b + 8, 8); r += 8; } } + aes_encrypt_deinit(ctx); /* 3) Output the results. * * These are already in @cipher due to the location of temporary * variables. */ + + return 0; } -/* - * @kek: key encryption key (KEK) - * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes - * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit - * @plain: plaintext key, n * 64 bit +/** + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: Key encryption key (KEK) + * @n: Length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bit + * @plain: Plaintext key, n * 64 bit + * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) */ -int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain) +int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) { u8 a[8], *r, b[16]; int i, j; - AES_KEY key; + void *ctx; /* 1) Initialize variables. */ memcpy(a, cipher, 8); r = plain; memcpy(r, cipher + 8, 8 * n); - AES_set_decrypt_key(kek, 128, &key); + ctx = aes_decrypt_init(kek, 16); + if (ctx == NULL) + return -1; /* 2) Compute intermediate values. * For j = 5 to 0 @@ -129,12 +124,13 @@ int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain) b[7] ^= n * j + i; memcpy(b + 8, r, 8); - AES_decrypt(b, b, &key); + aes_decrypt(ctx, b, b); memcpy(a, b, 8); memcpy(r, b + 8, 8); r -= 8; } } + aes_decrypt_deinit(ctx); /* 3) Output results. * @@ -165,27 +161,37 @@ static void gf_mulx(u8 *pad) } -void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +/** + * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 + * @key: Key for the hash operation + * @data: Data buffer for which a MAC is determined + * @data: Length of data buffer in bytes + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + */ +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) { - AES_KEY akey; + void *ctx; u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE]; const u8 *pos = data; int i; size_t left = data_len; - AES_set_encrypt_key(key, 128, &akey); + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; memset(cbc, 0, BLOCK_SIZE); while (left >= BLOCK_SIZE) { for (i = 0; i < BLOCK_SIZE; i++) cbc[i] ^= *pos++; if (left > BLOCK_SIZE) - AES_encrypt(cbc, cbc, &akey); + aes_encrypt(ctx, cbc, cbc); left -= BLOCK_SIZE; } memset(pad, 0, BLOCK_SIZE); - AES_encrypt(pad, pad, &akey); + aes_encrypt(ctx, pad, pad); gf_mulx(pad); if (left || data_len == 0) { @@ -197,32 +203,55 @@ void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) for (i = 0; i < BLOCK_SIZE; i++) pad[i] ^= cbc[i]; - AES_encrypt(pad, mac, &akey); + aes_encrypt(ctx, pad, mac); + aes_encrypt_deinit(ctx); + return 0; } -void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) +/** + * aes_128_encrypt_block - Perform one AES 128-bit block operation + * @key: Key for AES + * @in: Input data (16 bytes) + * @out: Output of the AES block operation (16 bytes) + * Returns: 0 on success, -1 on failure + */ +int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) { - AES_KEY akey; - AES_set_encrypt_key(key, 128, &akey); - AES_encrypt(in, out, &akey); + void *ctx; + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + aes_encrypt(ctx, in, out); + aes_encrypt_deinit(ctx); + return 0; } -void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len) +/** + * aes_128_ctr_encrypt - AES-128 CTR mode encryption + * @key: Key for encryption (16 bytes) + * @nonce: Nonce for counter mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * Returns: 0 on success, -1 on failure + */ +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) { - AES_KEY akey; + void *ctx; size_t len, left = data_len; int i; u8 *pos = data; u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE]; - AES_set_encrypt_key(key, 128, &akey); + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; memcpy(counter, nonce, BLOCK_SIZE); while (left > 0) { - AES_encrypt(counter, buf, &akey); + aes_encrypt(ctx, counter, buf); len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE; for (i = 0; i < len; i++) @@ -236,9 +265,23 @@ void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, break; } } + aes_encrypt_deinit(ctx); + return 0; } +/** + * aes_128_eax_encrypt - AES-128 EAX mode encryption + * @key: Key for encryption (16 bytes) + * @nonce: Nonce for counter mode + * @nonce_len: Nonce length in bytes + * @hdr: Header data to be authenticity protected + * @hdr_len: Length of the header data bytes + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * @tag: 16-byte tag value + * Returns: 0 on success, -1 on failure + */ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, u8 *tag) @@ -284,6 +327,18 @@ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, } +/** + * aes_128_eax_decrypt - AES-128 EAX mode decryption + * @key: Key for decryption (16 bytes) + * @nonce: Nonce for counter mode + * @nonce_len: Nonce length in bytes + * @hdr: Header data to be authenticity protected + * @hdr_len: Length of the header data bytes + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * @tag: 16-byte tag value + * Returns: 0 on success, -1 on failure, -2 if tag does not match + */ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, const u8 *tag) @@ -332,48 +387,70 @@ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, } -void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len) +/** + * aes_128_cbc_encrypt - AES-128 CBC encryption + * @key: Encryption key + * @iv: Encryption IV for CBC mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - AES_KEY akey; + void *ctx; u8 cbc[BLOCK_SIZE]; u8 *pos = data; int i, j, blocks; - AES_set_encrypt_key(key, 128, &akey); + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; memcpy(cbc, iv, BLOCK_SIZE); blocks = data_len / BLOCK_SIZE; for (i = 0; i < blocks; i++) { for (j = 0; j < BLOCK_SIZE; j++) cbc[j] ^= pos[j]; - AES_encrypt(cbc, cbc, &akey); + aes_encrypt(ctx, cbc, cbc); memcpy(pos, cbc, BLOCK_SIZE); pos += BLOCK_SIZE; } + aes_encrypt_deinit(ctx); + return 0; } -void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len) +/** + * aes_128_cbc_decrypt - AES-128 CBC decryption + * @key: Decryption key + * @iv: Decryption IV for CBC mode (16 bytes) + * @data: Data to decrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - AES_KEY akey; + void *ctx; u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE]; u8 *pos = data; int i, j, blocks; - AES_set_decrypt_key(key, 128, &akey); + ctx = aes_decrypt_init(key, 16); + if (ctx == NULL) + return -1; memcpy(cbc, iv, BLOCK_SIZE); blocks = data_len / BLOCK_SIZE; for (i = 0; i < blocks; i++) { memcpy(tmp, pos, BLOCK_SIZE); - AES_decrypt(pos, pos, &akey); + aes_decrypt(ctx, pos, pos); for (j = 0; j < BLOCK_SIZE; j++) pos[j] ^= cbc[j]; memcpy(cbc, tmp, BLOCK_SIZE); pos += BLOCK_SIZE; } + aes_decrypt_deinit(ctx); + return 0; } @@ -388,25 +465,28 @@ static void test_aes_perf(void) const int num_iters = 10; int i; unsigned int start, end; - AES_KEY akey; u8 key[16], pt[16], ct[16]; + void *ctx; printf("keySetupEnc:"); for (i = 0; i < num_iters; i++) { rdtscll(start); - AES_set_encrypt_key(key, 128, &akey); + ctx = aes_encrypt_init(key, 16); rdtscll(end); + aes_encrypt_deinit(ctx); printf(" %d", end - start); } printf("\n"); printf("Encrypt:"); + ctx = aes_encrypt_init(key, 16); for (i = 0; i < num_iters; i++) { rdtscll(start); - AES_encrypt(pt, ct, &akey); + aes_encrypt(ctx, pt, ct); rdtscll(end); printf(" %d", end - start); } + aes_encrypt_deinit(ctx); printf("\n"); } #endif /* __i386__ */ @@ -599,7 +679,10 @@ int main(int argc, char *argv[]) int ret = 0, i; struct omac1_test_vector *tv; - aes_wrap(kek, 2, plain, result); + if (aes_wrap(kek, 2, plain, result)) { + printf("AES-WRAP-128-128 reported failure\n"); + ret++; + } if (memcmp(result, crypt, 24) != 0) { printf("AES-WRAP-128-128 failed\n"); ret++; diff --git a/contrib/wpa_supplicant/aes_wrap.h b/contrib/wpa_supplicant/aes_wrap.h index 70e83ea..cb1a539 100644 --- a/contrib/wpa_supplicant/aes_wrap.h +++ b/contrib/wpa_supplicant/aes_wrap.h @@ -1,21 +1,42 @@ +/* + * AES-based functions + * + * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * - One-Key CBC MAC (OMAC1) hash with AES-128 + * - AES-128 CTR mode encryption + * - AES-128 EAX mode encryption/decryption + * - AES-128 CBC + * + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 AES_WRAP_H #define AES_WRAP_H -void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher); -int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain); -void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); -void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); -void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len); +int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); +int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); +int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len); int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, u8 *tag); int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, const u8 *tag); -void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len); -void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len); +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); #endif /* AES_WRAP_H */ diff --git a/contrib/wpa_supplicant/base64.c b/contrib/wpa_supplicant/base64.c new file mode 100644 index 0000000..2717e30 --- /dev/null +++ b/contrib/wpa_supplicant/base64.c @@ -0,0 +1,198 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <string.h> + +#include "base64.h" + +static const unsigned char base64_table[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len) +{ + unsigned char *out, *pos; + const unsigned char *end, *in; + size_t olen; + int line_len; + + olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ + olen += olen / 72; /* line feeds */ + olen++; /* nul termination */ + out = malloc(olen); + if (out == NULL) + return NULL; + + end = src + len; + in = src; + pos = out; + line_len = 0; + while (end - in >= 3) { + *pos++ = base64_table[in[0] >> 2]; + *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = base64_table[in[2] & 0x3f]; + in += 3; + line_len += 4; + if (line_len >= 72) { + *pos++ = '\n'; + line_len = 0; + } + } + + if (end - in) { + *pos++ = base64_table[in[0] >> 2]; + if (end - in == 1) { + *pos++ = base64_table[(in[0] & 0x03) << 4]; + *pos++ = '='; + } else { + *pos++ = base64_table[((in[0] & 0x03) << 4) | + (in[1] >> 4)]; + *pos++ = base64_table[(in[1] & 0x0f) << 2]; + } + *pos++ = '='; + line_len += 4; + } + + if (line_len) + *pos++ = '\n'; + + *pos = '\0'; + if (out_len) + *out_len = pos - out; + return out; +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + unsigned char dtable[256], *out, *pos, in[4], block[4], tmp; + size_t i, count, olen; + + memset(dtable, 0x80, 256); + for (i = 0; i < sizeof(base64_table); i++) + dtable[base64_table[i]] = i; + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) { + if (dtable[src[i]] != 0x80) + count++; + } + + if (count % 4) + return NULL; + + olen = count / 4 * 3; + pos = out = malloc(count); + if (out == NULL) + return NULL; + + count = 0; + for (i = 0; i < len; i++) { + tmp = dtable[src[i]]; + if (tmp == 0x80) + continue; + + in[count] = src[i]; + block[count] = tmp; + count++; + if (count == 4) { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + count = 0; + } + } + + if (pos > out) { + if (in[2] == '=') + pos -= 2; + else if (in[3] == '=') + pos--; + } + + *out_len = pos - out; + return out; +} + + +#ifdef TEST_MAIN +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + FILE *f; + size_t len, elen; + unsigned char *buf, *e; + + if (argc != 4) { + printf("Usage: base64 <encode|decode> <in file> <out file>\n"); + return -1; + } + + f = fopen(argv[2], "r"); + if (f == NULL) + return -1; + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + buf = malloc(len); + if (buf == NULL) { + fclose(f); + return -1; + } + fread(buf, 1, len, f); + fclose(f); + + if (strcmp(argv[1], "encode") == 0) + e = base64_encode(buf, len, &elen); + else + e = base64_decode(buf, len, &elen); + if (e == NULL) + return -2; + f = fopen(argv[3], "w"); + if (f == NULL) + return -3; + fwrite(e, 1, elen, f); + fclose(f); + free(e); + + return 0; +} +#endif /* TEST_MAIN */ diff --git a/contrib/wpa_supplicant/base64.h b/contrib/wpa_supplicant/base64.h new file mode 100644 index 0000000..7043328 --- /dev/null +++ b/contrib/wpa_supplicant/base64.h @@ -0,0 +1,23 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.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 BASE64_H +#define BASE64_h + +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len); +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len); + +#endif /* BASE64_H */ diff --git a/contrib/wpa_supplicant/common.c b/contrib/wpa_supplicant/common.c index 071ffe8..4b756d8 100644 --- a/contrib/wpa_supplicant/common.c +++ b/contrib/wpa_supplicant/common.c @@ -1,6 +1,5 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / common helper functions, etc. + * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -22,6 +21,10 @@ #include <ctype.h> #include <time.h> #include <sys/time.h> +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock2.h> +#include <wincrypt.h> +#endif /* CONFIG_NATIVE_WINDOWS */ #include "common.h" @@ -34,12 +37,17 @@ int wpa_debug_timestamp = 0; int hostapd_get_rand(u8 *buf, size_t len) { #ifdef CONFIG_NATIVE_WINDOWS - int i; - /* FIX: use more secure pseudo random number generator */ - for (i = 0; i < len; i++) { - buf[i] = rand(); - } - return 0; + HCRYPTPROV prov; + BOOL ret; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return -1; + + ret = CryptGenRandom(prov, len, buf); + CryptReleaseContext(prov, 0); + + return ret ? 0 : -1; #else /* CONFIG_NATIVE_WINDOWS */ FILE *f; size_t rc; @@ -93,6 +101,12 @@ static int hex2byte(const char *hex) } +/** + * hwaddr_aton - Convert ASCII string to MAC address + * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ int hwaddr_aton(const char *txt, u8 *addr) { int i; @@ -115,6 +129,14 @@ int hwaddr_aton(const char *txt, u8 *addr) } +/** + * hexstr2bin - Convert ASCII hex string into binary data + * @hex: ASCII hex string (e.g., "01ab") + * @buf: Buffer for the binary data + * @len: Length of the text to convert in bytes (of buf); hex will be double + * this size + * Returns: 0 on success, -1 on failure (invalid hex string) + */ int hexstr2bin(const char *hex, u8 *buf, size_t len) { int i, a; @@ -171,6 +193,15 @@ char * rel2abs_path(const char *rel_path) } +/** + * inc_byte_array - Increment arbitrary length byte array by one + * @counter: Pointer to byte array + * @len: Length of the counter in bytes + * + * This function increments the last byte of the counter by one and continues + * rolling over to more significant bytes if the byte was incremented from + * 0xff to 0x00. + */ void inc_byte_array(u8 *counter, size_t len) { int pos = len - 1; @@ -201,7 +232,9 @@ void fprint_char(FILE *f, char c) } -static void wpa_debug_print_timestamp(void) +#ifndef CONFIG_NO_STDOUT_DEBUG + +void wpa_debug_print_timestamp(void) { struct timeval tv; char buf[16]; @@ -218,6 +251,17 @@ static void wpa_debug_print_timestamp(void) } +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ void wpa_printf(int level, char *fmt, ...) { va_list ap; @@ -240,7 +284,9 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, return; wpa_debug_print_timestamp(); printf("%s - hexdump(len=%lu):", title, (unsigned long) len); - if (show) { + if (buf == NULL) { + printf(" [NULL]"); + } else if (show) { for (i = 0; i < len; i++) printf(" %02x", buf[i]); } else { @@ -276,6 +322,11 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, title, (unsigned long) len); return; } + if (buf == NULL) { + printf("%s - hexdump_ascii(len=%lu): [NULL]\n", + title, (unsigned long) len); + return; + } printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); while (len) { llen = len > line_len ? line_len : len; @@ -312,6 +363,8 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); } +#endif /* CONFIG_NO_STDOUT_DEBUG */ + #ifdef CONFIG_NATIVE_WINDOWS diff --git a/contrib/wpa_supplicant/config.c b/contrib/wpa_supplicant/config.c index 26307cf..2bba4ab 100644 --- a/contrib/wpa_supplicant/config.c +++ b/contrib/wpa_supplicant/config.c @@ -1,5 +1,5 @@ /* - * WPA Supplicant / Configuration file parser + * WPA Supplicant / Configuration parser and common functions * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -18,63 +18,40 @@ #include "common.h" #include "wpa.h" -#include "config.h" #include "sha1.h" #include "wpa_supplicant.h" #include "eapol_sm.h" #include "eap.h" +#include "l2_packet.h" #include "config.h" +/* + * 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; - int (*parser)(struct parse_data *data, int line, const char *value); - void *param1, *param2, *param3, *param4; - struct wpa_ssid *ssid; - int key_data; -}; + /* Parser function for this variable */ + int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid, + int line, const char *value); -static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line) -{ - char *pos, *end, *sstart; - - while (fgets(s, size, stream)) { - (*line)++; - s[size - 1] = '\0'; - pos = s; - - while (*pos == ' ' || *pos == '\t' || *pos == '\r') - pos++; - if (*pos == '#' || *pos == '\n' || *pos == '\0' || - *pos == '\r') - continue; - - /* Remove # comments unless they are within a double quoted - * string. Remove trailing white space. */ - sstart = strchr(pos, '"'); - if (sstart) - sstart = strchr(sstart + 1, '"'); - if (!sstart) - sstart = pos; - end = strchr(sstart, '#'); - if (end) - *end-- = '\0'; - else - end = pos + strlen(pos) - 1; - while (end > pos && - (*end == '\n' || *end == ' ' || *end == '\t' || - *end == '\r')) { - *end-- = '\0'; - } - if (*pos == '\0') - continue; + /* 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); - return pos; - } + /* Variable specific parameters for the parser. */ + void *param1, *param2, *param3, *param4; - return NULL; -} + /* 0 = this variable can be included in debug output + * 1 = this variable contains key/private data and it must not be + * included in debug output unless explicitly requested + */ + int key_data; +}; static char * wpa_config_parse_string(const char *value, size_t *len) @@ -90,7 +67,7 @@ static char * wpa_config_parse_string(const char *value, size_t *len) return strdup(value); } else { u8 *str; - int hlen = strlen(value); + size_t hlen = strlen(value); if (hlen % 1) return NULL; *len = hlen / 2; @@ -106,14 +83,15 @@ static char * wpa_config_parse_string(const char *value, size_t *len) } -static int wpa_config_parse_str(struct parse_data *data, +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; - dst = (char **) (((u8 *) data->ssid) + (long) data->param1); - dst_len = (size_t *) (((u8 *) data->ssid) + (long) data->param2); + dst = (char **) (((u8 *) ssid) + (long) data->param1); + dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2); free(*dst); *dst = wpa_config_parse_string(value, &res_len); @@ -155,12 +133,91 @@ static int wpa_config_parse_str(struct parse_data *data, } -static int wpa_config_parse_int(struct parse_data *data, +static int is_hex(const u8 *data, size_t len) +{ + int 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) +{ + int i; + char *buf, *pos, *end; + + pos = buf = malloc(len + 3); + if (buf == NULL) + return NULL; + end = buf + len + 3; + pos += snprintf(pos, end - pos, "\""); + for (i = 0; i < len; i++) + pos += snprintf(pos, end - pos, "%c", value[i]); + pos += snprintf(pos, end - pos, "\""); + + return buf; +} + + +static char * wpa_config_write_string_hex(const u8 *value, size_t len) +{ + int i; + char *buf, *pos, *end; + + pos = buf = malloc(2 * len + 1); + if (buf == NULL) + return NULL; + memset(buf, 0, 2 * len + 1); + end = buf + 2 * len + 1; + for (i = 0; i < len; i++) + pos += snprintf(pos, end - pos, "%02x", value[i]); + + 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 = strlen(*src); + + return wpa_config_write_string((const u8 *) *src, len); +} + + +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 *) data->ssid) + (long) data->param1); + dst = (int *) (((u8 *) ssid) + (long) data->param1); *dst = atoi(value); wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); @@ -184,26 +241,60 @@ static int wpa_config_parse_int(struct parse_data *data, } -static int wpa_config_parse_bssid(struct parse_data *data, int line, +static char * wpa_config_write_int(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int *src; + char *value; + + src = (int *) (((u8 *) ssid) + (long) data->param1); + + value = malloc(20); + if (value == NULL) + return NULL; + snprintf(value, 20, "%d", *src); + return value; +} + + +static int wpa_config_parse_bssid(const struct parse_data *data, + struct wpa_ssid *ssid, int line, const char *value) { - if (hwaddr_aton(value, data->ssid->bssid)) { + if (hwaddr_aton(value, ssid->bssid)) { wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.", line, value); return -1; } - data->ssid->bssid_set = 1; - wpa_hexdump(MSG_MSGDUMP, "BSSID", data->ssid->bssid, ETH_ALEN); + ssid->bssid_set = 1; + wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN); return 0; } -static int wpa_config_parse_psk(struct parse_data *data, int line, +static char * wpa_config_write_bssid(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *value; + + if (!ssid->bssid_set) + return NULL; + + value = malloc(20); + if (value == NULL) + return NULL; + snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid)); + return value; +} + + +static int wpa_config_parse_psk(const struct parse_data *data, + struct wpa_ssid *ssid, int line, const char *value) { if (*value == '"') { char *pos; - int len; + size_t len; value++; pos = strrchr(value, '"'); @@ -212,29 +303,45 @@ static int wpa_config_parse_psk(struct parse_data *data, int line, len = strlen(value); if (len < 8 || len > 63) { wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase " - "length %d (expected: 8..63) '%s'.", - line, len, value); + "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); - data->ssid->passphrase = strdup(value); - return data->ssid->passphrase == NULL ? -1 : 0; + ssid->passphrase = strdup(value); + return ssid->passphrase == NULL ? -1 : 0; } - if (hexstr2bin(value, data->ssid->psk, PMK_LEN) || + 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; } - data->ssid->psk_set = 1; - wpa_hexdump_key(MSG_MSGDUMP, "PSK", data->ssid->psk, PMK_LEN); + ssid->psk_set = 1; + wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN); return 0; } -static int wpa_config_parse_proto(struct parse_data *data, int line, +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, + strlen(ssid->passphrase)); + + if (ssid->psk_set) + return wpa_config_write_string_hex(ssid->psk, PMK_LEN); + + return NULL; +} + + +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; @@ -279,12 +386,39 @@ static int wpa_config_parse_proto(struct parse_data *data, int line, } wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val); - data->ssid->proto = val; + ssid->proto = val; return errors ? -1 : 0; } -static int wpa_config_parse_key_mgmt(struct parse_data *data, int line, +static char * wpa_config_write_proto(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int first = 1; + char *buf, *pos, *end; + + pos = buf = malloc(10); + if (buf == NULL) + return NULL; + memset(buf, 0, 10); + end = buf + 10; + + if (ssid->proto & WPA_PROTO_WPA) { + pos += snprintf(pos, end - pos, "%sWPA", first ? "" : " "); + first = 0; + } + + if (ssid->proto & WPA_PROTO_RSN) { + pos += snprintf(pos, end - pos, "%sRSN", first ? "" : " "); + first = 0; + } + + return buf; +} + + +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; @@ -334,11 +468,54 @@ static int wpa_config_parse_key_mgmt(struct parse_data *data, int line, } wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val); - data->ssid->key_mgmt = val; + ssid->key_mgmt = val; return errors ? -1 : 0; } +static char * wpa_config_write_key_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int first = 1; + char *buf, *pos, *end; + + pos = buf = malloc(50); + if (buf == NULL) + return NULL; + memset(buf, 0, 50); + end = buf + 50; + + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + pos += snprintf(pos, end - pos, "%sWPA-PSK", first ? "" : " "); + first = 0; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + pos += snprintf(pos, end - pos, "%sWPA-EAP", first ? "" : " "); + first = 0; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + pos += snprintf(pos, end - pos, "%sIEEE8021X", + first ? "" : " "); + first = 0; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) { + pos += snprintf(pos, end - pos, "%sNONE", first ? "" : " "); + first = 0; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) { + pos += snprintf(pos, end - pos, "%sWPA-NONE", + first ? "" : " "); + first = 0; + } + + return buf; +} + + static int wpa_config_parse_cipher(int line, const char *value) { int val = 0, last; @@ -391,7 +568,48 @@ static int wpa_config_parse_cipher(int line, const char *value) } -static int wpa_config_parse_pairwise(struct parse_data *data, int line, +static char * wpa_config_write_cipher(int cipher) +{ + int first = 1; + char *buf, *pos, *end; + + pos = buf = malloc(50); + if (buf == NULL) + return NULL; + memset(buf, 0, 50); + end = buf + 50; + + if (cipher & WPA_CIPHER_CCMP) { + pos += snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); + first = 0; + } + + if (cipher & WPA_CIPHER_TKIP) { + pos += snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); + first = 0; + } + + if (cipher & WPA_CIPHER_WEP104) { + pos += snprintf(pos, end - pos, "%sWEP104", first ? "" : " "); + first = 0; + } + + if (cipher & WPA_CIPHER_WEP40) { + pos += snprintf(pos, end - pos, "%sWEP40", first ? "" : " "); + first = 0; + } + + if (cipher & WPA_CIPHER_NONE) { + pos += snprintf(pos, end - pos, "%sNONE", first ? "" : " "); + first = 0; + } + + return buf; +} + + +static int wpa_config_parse_pairwise(const struct parse_data *data, + struct wpa_ssid *ssid, int line, const char *value) { int val; @@ -405,12 +623,20 @@ static int wpa_config_parse_pairwise(struct parse_data *data, int line, } wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val); - data->ssid->pairwise_cipher = val; + ssid->pairwise_cipher = val; return 0; } -static int wpa_config_parse_group(struct parse_data *data, int line, +static char * wpa_config_write_pairwise(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_cipher(ssid->pairwise_cipher); +} + + +static int wpa_config_parse_group(const struct parse_data *data, + struct wpa_ssid *ssid, int line, const char *value) { int val; @@ -425,12 +651,20 @@ static int wpa_config_parse_group(struct parse_data *data, int line, } wpa_printf(MSG_MSGDUMP, "group: 0x%x", val); - data->ssid->group_cipher = val; + ssid->group_cipher = val; return 0; } -static int wpa_config_parse_auth_alg(struct parse_data *data, int line, +static char * wpa_config_write_group(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_cipher(ssid->group_cipher); +} + + +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; @@ -476,12 +710,44 @@ static int wpa_config_parse_auth_alg(struct parse_data *data, int line, } wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val); - data->ssid->auth_alg = val; + ssid->auth_alg = val; return errors ? -1 : 0; } -static int wpa_config_parse_eap(struct parse_data *data, int line, +static char * wpa_config_write_auth_alg(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int first = 1; + char *buf, *pos, *end; + + pos = buf = malloc(30); + if (buf == NULL) + return NULL; + memset(buf, 0, 30); + end = buf + 30; + + if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) { + pos += snprintf(pos, end - pos, "%sOPEN", first ? "" : " "); + first = 0; + } + + if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) { + pos += snprintf(pos, end - pos, "%sSHARED", first ? "" : " "); + first = 0; + } + + if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) { + pos += snprintf(pos, end - pos, "%sLEAP", first ? "" : " "); + first = 0; + } + + return buf; +} + + +static int wpa_config_parse_eap(const struct parse_data *data, + struct wpa_ssid *ssid, int line, const char *value) { int last, errors = 0; @@ -520,9 +786,9 @@ static int wpa_config_parse_eap(struct parse_data *data, int line, "See README for more information."); errors++; } else if (methods[num_methods] == EAP_TYPE_LEAP) - data->ssid->leap++; + ssid->leap++; else - data->ssid->non_leap++; + ssid->non_leap++; num_methods++; if (last) break; @@ -540,11 +806,41 @@ static int wpa_config_parse_eap(struct parse_data *data, int line, num_methods++; wpa_hexdump(MSG_MSGDUMP, "eap methods", methods, num_methods); - data->ssid->eap_methods = methods; + ssid->eap_methods = methods; return errors ? -1 : 0; } +static char * wpa_config_write_eap(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + int first = 1; + char *buf, *pos, *end; + const u8 *eap_methods = ssid->eap_methods; + const char *name; + + if (eap_methods == NULL) + return NULL; + + pos = buf = malloc(100); + if (buf == NULL) + return NULL; + memset(buf, 0, 100); + end = buf + 100; + + while (*eap_methods != EAP_TYPE_NONE) { + name = eap_get_name(*eap_methods); + if (name) + pos += snprintf(pos, end - pos, "%s%s", + first ? "" : " ", name); + first = 0; + eap_methods++; + } + + return buf; +} + + static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, const char *value, int idx) { @@ -570,54 +866,140 @@ static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, } -static int wpa_config_parse_wep_key0(struct parse_data *data, int line, +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(data->ssid->wep_key[0], - &data->ssid->wep_key_len[0], line, + 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(struct parse_data *data, int line, +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(data->ssid->wep_key[1], - &data->ssid->wep_key_len[1], line, + 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(struct parse_data *data, int line, +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(data->ssid->wep_key[2], - &data->ssid->wep_key_len[2], line, + 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(struct parse_data *data, int line, +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(data->ssid->wep_key[3], - &data->ssid->wep_key_len[3], line, + return wpa_config_parse_wep_key(ssid->wep_key[3], + &ssid->wep_key_len[3], line, value, 3); } +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); +} + + +/* Helper macros for network block parser */ + +/* OFFSET: Get offset of a variable within the wpa_ssid structure */ #define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v) -#define STR(f) .name = #f, .parser = wpa_config_parse_str, .param1 = OFFSET(f) + +/* STR: Define a string variable for an ASCII string; f = field name */ +#define STR(f) .name = #f, .parser = wpa_config_parse_str, \ + .writer = wpa_config_write_str, .param1 = OFFSET(f) + +/* 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), .param2 = OFFSET(f ## _len) + +/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length + * explicitly specified. */ #define STR_RANGE(f, min, max) STR_LEN(f), .param3 = (void *) (min), \ .param4 = (void *) (max) + + +/* INT: Define an integer variable */ #define INT(f) .name = #f, .parser = wpa_config_parse_int, \ + .writer = wpa_config_write_int, \ .param1 = OFFSET(f), .param2 = (void *) 0 + +/* INT: Define an integer variable with allowed value range */ #define INT_RANGE(f, min, max) INT(f), .param3 = (void *) (min), \ .param4 = (void *) (max) -#define FUNC(f) .name = #f, .parser = wpa_config_parse_ ## f -static struct parse_data ssid_fields[] = { +/* FUNC: Define a configuration variable that uses a custom function for + * parsing and writing the value. */ +#define FUNC(f) .name = #f, .parser = wpa_config_parse_ ## f, \ + .writer = wpa_config_write_ ## f + +/* + * 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 insider 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 in struct wpa_ssid + * definitions 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) }, @@ -632,24 +1014,30 @@ static struct parse_data ssid_fields[] = { { STR_LEN(anonymous_identity) }, { STR_RANGE(eappsk, EAP_PSK_LEN, EAP_PSK_LEN), .key_data = 1 }, { STR_LEN(nai) }, - { STR_LEN(server_nai) }, { STR_LEN(password), .key_data = 1 }, { STR(ca_cert) }, + { STR(ca_path) }, { STR(client_cert) }, { STR(private_key) }, { STR(private_key_passwd), .key_data = 1 }, { 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), .key_data = 1 }, { STR(dh_file2) }, { STR(subject_match2) }, + { STR(altsubject_match2) }, { STR(phase1) }, { STR(phase2) }, { STR(pcsc) }, { STR(pin), .key_data = 1 }, + { STR(engine_id) }, + { STR(key_id) }, + { INT(engine) }, { INT(eapol_flags) }, { FUNC(wep_key0), .key_data = 1 }, { FUNC(wep_key1), .key_data = 1 }, @@ -660,6 +1048,8 @@ static struct parse_data ssid_fields[] = { { INT(eap_workaround) }, { STR(pac_file) }, { INT_RANGE(mode, 0, 1) }, + { INT_RANGE(proactive_key_caching, 0, 1) }, + { INT_RANGE(disabled, 0, 1) }, }; #undef OFFSET @@ -672,119 +1062,18 @@ static struct parse_data ssid_fields[] = { #define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0])) -static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) -{ - struct wpa_ssid *ssid; - int errors = 0, i, end = 0; - char buf[256], *pos, *pos2; - - wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", - *line); - ssid = (struct wpa_ssid *) malloc(sizeof(*ssid)); - if (ssid == NULL) - return NULL; - memset(ssid, 0, sizeof(*ssid)); - ssid->id = id; - - ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN; - ssid->pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP; - ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | - WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40; - ssid->key_mgmt = WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X; - ssid->eapol_flags = EAPOL_FLAG_REQUIRE_KEY_UNICAST | - EAPOL_FLAG_REQUIRE_KEY_BROADCAST; - ssid->eap_workaround = (unsigned int) -1; - - while ((pos = wpa_config_get_line(buf, sizeof(buf), f, line))) { - if (strcmp(pos, "}") == 0) { - end = 1; - break; - } - - pos2 = strchr(pos, '='); - if (pos2 == NULL) { - wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line " - "'%s'.", *line, pos); - errors++; - continue; - } - - *pos2++ = '\0'; - if (*pos2 == '"') { - if (strchr(pos2 + 1, '"') == NULL) { - wpa_printf(MSG_ERROR, "Line %d: invalid " - "quotation '%s'.", *line, pos2); - errors++; - continue; - } - } - - for (i = 0; i < NUM_SSID_FIELDS; i++) { - struct parse_data *field = &ssid_fields[i]; - if (strcmp(pos, field->name) != 0) - continue; - - field->ssid = ssid; - if (field->parser(field, *line, pos2)) { - wpa_printf(MSG_ERROR, "Line %d: failed to " - "parse %s '%s'.", *line, pos, pos2); - errors++; - } - break; - } - if (i == NUM_SSID_FIELDS) { - wpa_printf(MSG_ERROR, "Line %d: unknown network field " - "'%s'.", *line, pos); - errors++; - } - } - - if (!end) { - wpa_printf(MSG_ERROR, "Line %d: network block was not " - "terminated properly.", *line); - errors++; - } - - if (ssid->passphrase) { - if (ssid->psk_set) { - wpa_printf(MSG_ERROR, "Line %d: both PSK and " - "passphrase configured.", *line); - errors++; - } - 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; - } - - if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !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)) { - /* 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; - } - - if (errors) { - free(ssid); - ssid = NULL; - } - - return ssid; -} - - -static int wpa_config_add_prio_network(struct wpa_config *config, - struct wpa_ssid *ssid) +/** + * wpa_config_add_prio_network - Add a network to priority lists + * @config: Configuration data from wpa_config_read() + * 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; @@ -821,181 +1110,125 @@ static int wpa_config_add_prio_network(struct wpa_config *config, } -struct wpa_config * wpa_config_read(const char *config_file) +/** + * 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. + */ +static int wpa_config_update_prio_list(struct wpa_config *config) { - 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, prio; - - config = malloc(sizeof(*config)); - if (config == NULL) - return NULL; - memset(config, 0, sizeof(*config)); - config->eapol_version = 1; - config->ap_scan = 1; - config->fast_reauth = 1; - wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", - config_file); - f = fopen(config_file, "r"); - if (f == NULL) { - free(config); - return NULL; - } + struct wpa_ssid *ssid; + int ret = 0; - while ((pos = wpa_config_get_line(buf, sizeof(buf), f, &line))) { - if (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; - } -#ifdef CONFIG_CTRL_IFACE - } else if (strncmp(pos, "ctrl_interface=", 15) == 0) { - free(config->ctrl_interface); - config->ctrl_interface = strdup(pos + 15); - wpa_printf(MSG_DEBUG, "ctrl_interface='%s'", - config->ctrl_interface); -#ifndef CONFIG_CTRL_IFACE_UDP - } else if (strncmp(pos, "ctrl_interface_group=", 21) == 0) { - struct group *grp; - char *endp; - const char *group = pos + 21; - - grp = getgrnam(group); - if (grp) { - config->ctrl_interface_gid = grp->gr_gid; - config->ctrl_interface_gid_set = 1; - wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" - " (from group name '%s')", - (int) config->ctrl_interface_gid, - group); - continue; - } + free(config->pssid); + config->pssid = NULL; + config->num_prio = 0; - /* Group name not found - try to parse this as gid */ - config->ctrl_interface_gid = strtol(group, &endp, 10); - if (*group == '\0' || *endp != '\0') { - wpa_printf(MSG_DEBUG, "Line %d: Invalid group " - "'%s'", line, group); - errors++; - continue; - } - wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", - (int) config->ctrl_interface_gid); -#endif /* CONFIG_CTRL_IFACE_UDP */ -#endif /* CONFIG_CTRL_IFACE */ - } else if (strncmp(pos, "eapol_version=", 14) == 0) { - config->eapol_version = atoi(pos + 14); - if (config->eapol_version < 1 || - config->eapol_version > 2) { - wpa_printf(MSG_ERROR, "Line %d: Invalid EAPOL " - "version (%d): '%s'.", - line, config->eapol_version, pos); - errors++; - continue; - } - wpa_printf(MSG_DEBUG, "eapol_version=%d", - config->eapol_version); - } else if (strncmp(pos, "ap_scan=", 8) == 0) { - config->ap_scan = atoi(pos + 8); - wpa_printf(MSG_DEBUG, "ap_scan=%d", config->ap_scan); - } else if (strncmp(pos, "fast_reauth=", 12) == 0) { - config->fast_reauth = atoi(pos + 12); - wpa_printf(MSG_DEBUG, "fast_reauth=%d", - config->fast_reauth); - } else { - wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " - "line '%s'.", line, pos); - errors++; - continue; - } + ssid = config->ssid; + while (ssid) { + ssid->pnext = NULL; + if (wpa_config_add_prio_network(config, ssid) < 0) + ret = -1; + ssid = ssid->next; } - fclose(f); + return ret; +} - config->ssid = head; - 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; - } - } - if (errors) { - wpa_config_free(config); - config = NULL; - head = NULL; - } - return config; +/** + * wpa_config_free_ssid - Free network/ssid configuration data + * @ssid: Configuration data for the network + * + * This function frees all resources allocated for the netowkr configuration + * data. + */ +void wpa_config_free_ssid(struct wpa_ssid *ssid) +{ + free(ssid->ssid); + free(ssid->passphrase); + free(ssid->eap_methods); + free(ssid->identity); + free(ssid->anonymous_identity); + free(ssid->eappsk); + free(ssid->nai); + free(ssid->password); + free(ssid->ca_cert); + free(ssid->ca_path); + free(ssid->client_cert); + free(ssid->private_key); + free(ssid->private_key_passwd); + free(ssid->dh_file); + free(ssid->subject_match); + free(ssid->altsubject_match); + free(ssid->ca_cert2); + free(ssid->ca_path2); + free(ssid->client_cert2); + free(ssid->private_key2); + free(ssid->private_key2_passwd); + free(ssid->dh_file2); + free(ssid->subject_match2); + free(ssid->altsubject_match2); + free(ssid->phase1); + free(ssid->phase2); + free(ssid->pcsc); + free(ssid->pin); + free(ssid->engine_id); + free(ssid->key_id); + free(ssid->otp); + free(ssid->pending_req_otp); + free(ssid->pac_file); + free(ssid->new_password); + 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) { + struct wpa_config_blob *blob, *prevblob; struct wpa_ssid *ssid, *prev = NULL; ssid = config->ssid; while (ssid) { prev = ssid; ssid = ssid->next; - free(prev->ssid); - free(prev->passphrase); - free(prev->eap_methods); - free(prev->identity); - free(prev->anonymous_identity); - free(prev->eappsk); - free(prev->nai); - free(prev->server_nai); - free(prev->password); - free(prev->ca_cert); - free(prev->client_cert); - free(prev->private_key); - free(prev->private_key_passwd); - free(prev->dh_file); - free(prev->subject_match); - free(prev->ca_cert2); - free(prev->client_cert2); - free(prev->private_key2); - free(prev->private_key2_passwd); - free(prev->dh_file2); - free(prev->subject_match2); - free(prev->phase1); - free(prev->phase2); - free(prev->pcsc); - free(prev->pin); - free(prev->otp); - free(prev->pending_req_otp); - free(prev->pac_file); - free(prev); + wpa_config_free_ssid(prev); } + + blob = config->blobs; + prevblob = NULL; + while (blob) { + prevblob = blob; + blob = blob->next; + wpa_config_free_blob(prevblob); + } + free(config->ctrl_interface); + free(config->opensc_engine_path); + free(config->pkcs11_engine_path); + free(config->pkcs11_module_path); + free(config->driver_param); free(config->pssid); free(config); } +/** + * wpa_config_allowed_eap_method - Check whether EAP method is allowed + * @ssid: Pointer to a configuration data + * @method: EAP type + * Returns: 1 = allowed EAP method, 0 = not allowed + */ int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method) { u8 *pos; @@ -1013,39 +1246,306 @@ int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method) } -const char * wpa_cipher_txt(int cipher) +/** + * 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) { - switch (cipher) { - case WPA_CIPHER_NONE: - return "NONE"; - case WPA_CIPHER_WEP40: - return "WEP-40"; - case WPA_CIPHER_WEP104: - return "WEP-104"; - case WPA_CIPHER_TKIP: - return "TKIP"; - case WPA_CIPHER_CCMP: - return "CCMP"; - default: - return "UNKNOWN"; + struct wpa_ssid *ssid; + + ssid = config->ssid; + while (ssid) { + if (id == ssid->id) + break; + ssid = ssid->next; } + + return ssid; } -const char * wpa_key_mgmt_txt(int key_mgmt, int proto) +/** + * 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) { - switch (key_mgmt) { - case WPA_KEY_MGMT_IEEE8021X: - return proto == WPA_PROTO_RSN ? - "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; - case WPA_KEY_MGMT_PSK: - return proto == WPA_PROTO_RSN ? - "WPA2-PSK" : "WPA-PSK"; - case WPA_KEY_MGMT_NONE: - return "NONE"; - case WPA_KEY_MGMT_IEEE8021X_NO_WPA: - return "IEEE 802.1X (no WPA)"; - default: - return "UNKNOWN"; + 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 = malloc(sizeof(*ssid)); + if (ssid == NULL) + return NULL; + memset(ssid, 0, sizeof(*ssid)); + 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 a 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; + ssid->eapol_flags = DEFAULT_EAPOL_FLAGS; + ssid->eap_workaround = DEFAULT_EAP_WORKAROUND; +} + + +/** + * wpa_config_set - Set a variable in network configuration + * @ssid: Pointer to a 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) +{ + int i, 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 (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 - Get a variable in network configuration + * @ssid: Pointer to a 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) +{ + int 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 (strcmp(var, field->name) == 0) + return field->writer(field, ssid); + } + + return NULL; +} + + +/** + * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID + * @ssid: Pointer to a 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) +{ + 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; +} + + +/** + * 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 (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) { + free(blob->name); + free(blob->data); + 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 (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; +} + + +/** + * 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 = malloc(sizeof(*config)); + if (config == NULL) + return NULL; + memset(config, 0, sizeof(*config)); + config->eapol_version = DEFAULT_EAPOL_VERSION; + config->ap_scan = DEFAULT_AP_SCAN; + config->fast_reauth = DEFAULT_FAST_REAUTH; + + if (ctrl_interface) + config->ctrl_interface = strdup(ctrl_interface); + if (driver_param) + config->driver_param = strdup(driver_param); + + return config; } diff --git a/contrib/wpa_supplicant/config.h b/contrib/wpa_supplicant/config.h index 13deb3e..bd479b6 100644 --- a/contrib/wpa_supplicant/config.h +++ b/contrib/wpa_supplicant/config.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / Configuration file structures + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -7,27 +21,297 @@ #endif /* CONFIG_CTRL_IFACE_UDP */ #endif /* CONFIG_CTRL_IFACE */ +#define DEFAULT_EAPOL_VERSION 1 +#define DEFAULT_AP_SCAN 1 +#define DEFAULT_FAST_REAUTH 1 + #include "config_ssid.h" +/** + * struct wpa_config_blob - Named configuration blob + * + * This data structure is used to provide storage for binary objects to store + * abstract information like certificates and private keys inlined with the + * configuration data. + */ +struct wpa_config_blob { + /** + * name - Blob name + */ + char *name; + + /** + * data - Pointer to binary data + */ + u8 *data; + + /** + * len - Length of binary data + */ + size_t len; + + /** + * next - Pointer to next blob in the configuration + */ + struct wpa_config_blob *next; +}; + + +/** + * 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 { - struct wpa_ssid *ssid; /* global network list */ - struct wpa_ssid **pssid; /* per priority network lists (in priority - * order) */ - int num_prio; /* number of different priorities */ + /** + * 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; - char *ctrl_interface; /* directory for UNIX domain sockets */ + + /** + * ctrl_interface - Directory for UNIX domain sockets + * + * This variable is used to configure where the UNIX domain sockets + * for the control interface are created. If UDP-based ctrl_iface is + * used, this variable can be set to any string (i.e., %NULL is not + * allowed). + */ + char *ctrl_interface; + #ifdef CONFIG_CTRL_IFACE #ifndef CONFIG_CTRL_IFACE_UDP + /** + * ctrl_interface_gid - Group identity for the UNIX domain sockets + * + * 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. + */ gid_t ctrl_interface_gid; #endif /* CONFIG_CTRL_IFACE_UDP */ + /** + * ctrl_interface_gid_set - Whether ctrl_interface_gid is used + * + * If this variable is zero, ctrl_interface_gid value is not used and + * group will not be changed from the value it got by default + * when the directory or socket was created. + */ int ctrl_interface_gid_set; #endif /* CONFIG_CTRL_IFACE */ + + /** + * 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; }; -struct wpa_config * wpa_config_read(const char *config_file); +/* Protypes for common functions from config.c */ + void wpa_config_free(struct wpa_config *ssid); +void wpa_config_free_ssid(struct wpa_ssid *ssid); +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(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); + +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); + + +/* 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/contrib/wpa_supplicant/config_file.c b/contrib/wpa_supplicant/config_file.c new file mode 100644 index 0000000..b203893 --- /dev/null +++ b/contrib/wpa_supplicant/config_file.c @@ -0,0 +1,695 @@ +/* + * WPA Supplicant / Configuration backend: text file + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "wpa.h" +#include "wpa_supplicant.h" +#include "config.h" +#include "base64.h" + + +static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line) +{ + char *pos, *end, *sstart; + + while (fgets(s, size, stream)) { + (*line)++; + s[size - 1] = '\0'; + pos = s; + + while (*pos == ' ' || *pos == '\t' || *pos == '\r') + pos++; + if (*pos == '#' || *pos == '\n' || *pos == '\0' || + *pos == '\r') + continue; + + /* Remove # comments unless they are within a double quoted + * string. Remove trailing white space. */ + sstart = strchr(pos, '"'); + if (sstart) + sstart = strrchr(sstart + 1, '"'); + if (!sstart) + sstart = pos; + end = strchr(sstart, '#'); + if (end) + *end-- = '\0'; + else + end = pos + strlen(pos) - 1; + while (end > pos && + (*end == '\n' || *end == ' ' || *end == '\t' || + *end == '\r')) { + *end-- = '\0'; + } + if (*pos == '\0') + continue; + + return pos; + } + + return NULL; +} + + +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 = (struct wpa_ssid *) malloc(sizeof(*ssid)); + if (ssid == NULL) + return NULL; + memset(ssid, 0, sizeof(*ssid)); + ssid->id = id; + + wpa_config_set_network_defaults(ssid); + + while ((pos = wpa_config_get_line(buf, sizeof(buf), f, line))) { + if (strcmp(pos, "}") == 0) { + end = 1; + break; + } + + pos2 = strchr(pos, '='); + if (pos2 == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line " + "'%s'.", *line, pos); + errors++; + continue; + } + + *pos2++ = '\0'; + if (*pos2 == '"') { + if (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++; + } + + 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) && !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)) { + /* 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; + } + + if (errors) { + free(ssid); + ssid = NULL; + } + + return ssid; +} + + +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 ((pos = wpa_config_get_line(buf, sizeof(buf), f, line))) { + if (strcmp(pos, "}") == 0) { + end = 1; + break; + } + + len = strlen(pos); + nencoded = realloc(encoded, encoded_len + len); + if (nencoded == NULL) { + wpa_printf(MSG_ERROR, "Line %d: not enough memory for " + "blob", *line); + free(encoded); + return NULL; + } + encoded = nencoded; + memcpy(encoded + encoded_len, pos, len); + encoded_len += len; + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: blob was not terminated " + "properly", *line); + free(encoded); + return NULL; + } + + blob = malloc(sizeof(*blob)); + if (blob == NULL) { + free(encoded); + return NULL; + } + memset(blob, 0, sizeof(*blob)); + blob->name = strdup(name); + blob->data = base64_decode(encoded, encoded_len, &blob->len); + free(encoded); + + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + return NULL; + } + + return blob; +} + + +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, prio; + + 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) { + free(config); + return NULL; + } + + while ((pos = wpa_config_get_line(buf, sizeof(buf), f, &line))) { + if (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; + } + } else if (strncmp(pos, "blob-base64-", 12) == 0) { + char *name = pos + 12, *name_end; + struct wpa_config_blob *blob; + + name_end = strchr(name, '='); + if (name_end == NULL) { + wpa_printf(MSG_ERROR, "Line %d: no blob name " + "terminator", line); + errors++; + continue; + } + *name_end = '\0'; + + blob = wpa_config_read_blob(f, &line, name); + if (blob == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to read" + " blob %s", line, name); + errors++; + continue; + } + wpa_config_set_blob(config, blob); +#ifdef CONFIG_CTRL_IFACE + } else if (strncmp(pos, "ctrl_interface=", 15) == 0) { + free(config->ctrl_interface); + config->ctrl_interface = strdup(pos + 15); + wpa_printf(MSG_DEBUG, "ctrl_interface='%s'", + config->ctrl_interface); +#ifndef CONFIG_CTRL_IFACE_UDP + } else if (strncmp(pos, "ctrl_interface_group=", 21) == 0) { + struct group *grp; + char *endp; + const char *group = pos + 21; + + grp = getgrnam(group); + if (grp) { + config->ctrl_interface_gid = grp->gr_gid; + config->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + (int) config->ctrl_interface_gid, + group); + continue; + } + + /* Group name not found - try to parse this as gid */ + config->ctrl_interface_gid = strtol(group, &endp, 10); + if (*group == '\0' || *endp != '\0') { + wpa_printf(MSG_DEBUG, "Line %d: Invalid group " + "'%s'", line, group); + errors++; + continue; + } + config->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + (int) config->ctrl_interface_gid); +#endif /* CONFIG_CTRL_IFACE_UDP */ +#endif /* CONFIG_CTRL_IFACE */ + } else if (strncmp(pos, "eapol_version=", 14) == 0) { + config->eapol_version = atoi(pos + 14); + if (config->eapol_version < 1 || + config->eapol_version > 2) { + wpa_printf(MSG_ERROR, "Line %d: Invalid EAPOL " + "version (%d): '%s'.", + line, config->eapol_version, pos); + errors++; + continue; + } + wpa_printf(MSG_DEBUG, "eapol_version=%d", + config->eapol_version); + } else if (strncmp(pos, "ap_scan=", 8) == 0) { + config->ap_scan = atoi(pos + 8); + wpa_printf(MSG_DEBUG, "ap_scan=%d", config->ap_scan); + } else if (strncmp(pos, "fast_reauth=", 12) == 0) { + config->fast_reauth = atoi(pos + 12); + wpa_printf(MSG_DEBUG, "fast_reauth=%d", + config->fast_reauth); + } else if (strncmp(pos, "opensc_engine_path=", 19) == 0) { + free(config->opensc_engine_path); + config->opensc_engine_path = strdup(pos + 19); + wpa_printf(MSG_DEBUG, "opensc_engine_path='%s'", + config->opensc_engine_path); + } else if (strncmp(pos, "pkcs11_engine_path=", 19) == 0) { + free(config->pkcs11_engine_path); + config->pkcs11_engine_path = strdup(pos + 19); + wpa_printf(MSG_DEBUG, "pkcs11_engine_path='%s'", + config->pkcs11_engine_path); + } else if (strncmp(pos, "pkcs11_module_path=", 19) == 0) { + free(config->pkcs11_module_path); + config->pkcs11_module_path = strdup(pos + 19); + wpa_printf(MSG_DEBUG, "pkcs11_module_path='%s'", + config->pkcs11_module_path); + } else if (strncmp(pos, "driver_param=", 13) == 0) { + free(config->driver_param); + config->driver_param = strdup(pos + 13); + wpa_printf(MSG_DEBUG, "driver_param='%s'", + config->driver_param); + } else if (strncmp(pos, "dot11RSNAConfigPMKLifetime=", 27) == + 0) { + config->dot11RSNAConfigPMKLifetime = atoi(pos + 27); + wpa_printf(MSG_DEBUG, "dot11RSNAConfigPMKLifetime=%d", + config->dot11RSNAConfigPMKLifetime); + } else if (strncmp(pos, "dot11RSNAConfigPMKReauthThreshold=", + 34) == + 0) { + config->dot11RSNAConfigPMKReauthThreshold = + atoi(pos + 34); + wpa_printf(MSG_DEBUG, + "dot11RSNAConfigPMKReauthThreshold=%d", + config->dot11RSNAConfigPMKReauthThreshold); + } else if (strncmp(pos, "dot11RSNAConfigSATimeout=", 25) == + 0) { + config->dot11RSNAConfigSATimeout = atoi(pos + 25); + wpa_printf(MSG_DEBUG, "dot11RSNAConfigSATimeout=%d", + config->dot11RSNAConfigSATimeout); + } else if (strncmp(pos, "update_config=", 14) == 0) { + config->update_config = atoi(pos + 14); + wpa_printf(MSG_DEBUG, "update_config=%d", + config->update_config); + } else { + wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " + "line '%s'.", line, pos); + errors++; + continue; + } + } + + fclose(f); + + config->ssid = head; + 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; + } + } + if (errors) { + wpa_config_free(config); + config = NULL; + head = NULL; + } + + return config; +} + + +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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + free(value); +} + + +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); + free(value); +} + + +static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) +{ + char field[20], *value; + + snprintf(field, sizeof(field), "wep_key%d", idx); + value = wpa_config_get(ssid, field); + if (value) { + fprintf(f, "\t%s=%s\n", field, value); + 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 INT_DEF(t, def) write_int(f, #t, ssid->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); + write_eap(f, ssid); + STR(identity); + STR(anonymous_identity); + STR(eappsk); + STR(nai); + STR(password); + STR(ca_cert); + STR(client_cert); + STR(private_key); + STR(private_key_passwd); + STR(dh_file); + STR(subject_match); + STR(altsubject_match); + STR(ca_cert2); + 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); + INT(engine); + INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); + for (i = 0; i < 4; i++) + write_wep_key(f, i, ssid); + INT(wep_tx_keyidx); + INT(priority); + INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); + STR(pac_file); + INT(mode); + INT(proactive_key_caching); + INT(disabled); + +#undef STR +#undef INT +#undef INT_DEF +} + + +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); + free(encoded); + return 0; +} + + +int wpa_config_write(const char *name, struct wpa_config *config) +{ + FILE *f; + struct wpa_ssid *ssid; + struct wpa_config_blob *blob; + 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; + } + +#ifdef CONFIG_CTRL_IFACE + if (config->ctrl_interface) + fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface); +#ifndef CONFIG_CTRL_IFACE_UDP + if (config->ctrl_interface_gid_set) { + fprintf(f, "ctrl_interface_group=%d\n", + (int) config->ctrl_interface_gid); + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +#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); + + for (ssid = config->ssid; ssid; ssid = ssid->next) { + fprintf(f, "\nnetwork={\n"); + wpa_config_write_network(f, ssid); + fprintf(f, "}\n"); + } + + for (blob = config->blobs; blob; blob = blob->next) { + ret = wpa_config_write_blob(f, blob); + if (ret) + break; + } + + fclose(f); + + wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", + name, ret ? "un" : ""); + return ret; +} diff --git a/contrib/wpa_supplicant/config_ssid.h b/contrib/wpa_supplicant/config_ssid.h index 44bc989..14dc0f4 100644 --- a/contrib/wpa_supplicant/config_ssid.h +++ b/contrib/wpa_supplicant/config_ssid.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / Network configuration structures + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -24,86 +38,731 @@ #define PMK_LEN 32 #define EAP_PSK_LEN 16 + +#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) + +/** + * 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 { - struct wpa_ssid *next; /* next network in global list */ - struct wpa_ssid *pnext; /* next network in per-priority list */ - int id; /* unique id for ctrl_iface */ + /** + * 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 + */ 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[PMK_LEN]; + + /** + * 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; - /* Bitfields of allowed Pairwise/Group Ciphers, WPA_CIPHER_* */ + + /** + * 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; - int proto; /* Bitfield of allowed protocols (WPA_PROTO_*) */ - int auth_alg; /* Bitfield of allow authentication algorithms - * (WPA_AUTH_ALG_*) */ - int scan_ssid; /* scan this SSID with Probe Requests */ - u8 *identity; /* EAP Identity */ + + /** + * 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; + + /** + * identity - EAP Identity + */ + u8 *identity; + + /** + * identity_len - EAP Identity length + */ size_t identity_len; - u8 *anonymous_identity; /* Anonymous EAP Identity (for unencrypted use - * with EAP types that support different - * tunnelled identity, e.g., EAP-TTLS) */ + + /** + * anonymous_identity - Anonymous EAP Identity + * + * This field is used for unencrypted use with EAP types that support + * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the + * real identity (identity field) only to the authentication server. + */ + u8 *anonymous_identity; + + /** + * anonymous_identity_len - Length of anonymous_identity + */ size_t anonymous_identity_len; + + /** + * eappsk - EAP-PSK pre-shared key + */ u8 *eappsk; + + /** + * eappsk_len - EAP-PSK pre-shared key length + * + * This field is always 16 for the current version of EAP-PSK. + */ size_t eappsk_len; + + /** + * nai - User NAI (for EAP-PSK) + */ u8 *nai; + + /** + * nai_len - Length of nai field + */ size_t nai_len; - u8 *server_nai; - size_t server_nai_len; + + /** + * password - Password string for EAP + */ u8 *password; + + /** + * password_len - Length of password field + */ size_t password_len; + + /** + * ca_cert - File path to CA certificate file (PEM/DER) + * + * This file can have one or more trusted CA certificates. If ca_cert + * and ca_path are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + * + * 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". + */ u8 *ca_cert; + + /** + * ca_path - Directory path for CA certificate files (PEM) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + */ + u8 *ca_path; + + /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with EAP method that use TLS authentication. + * Usually, this is only configured for EAP-TLS, even though this could + * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + */ u8 *client_cert; + + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + */ u8 *private_key; + + /** + * private_key_passwd - Password for private key file + * + * If left out, this will be asked through control interface. + */ u8 *private_key_passwd; + + /** + * dh_file - File path to DH/DSA parameters file (in PEM format) + * + * This is an optional configuration file for setting parameters for an + * ephemeral DH key exchange. In most cases, the default RSA + * authentication does not use this configuration. However, it is + * possible setup RSA to use ephemeral DH key exchange. In addition, + * ciphers with DSA keys always use ephemeral DH keys. This can be used + * to achieve forward secrecy. If the file is in DSA parameters format, + * it will be automatically converted into DH params. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + */ u8 *dh_file; + + /** + * subject_match - Constraint for server certificate subject + * + * This substring is matched against the subject of the authentication + * server certificate. If this string is set, the server sertificate is + * only accepted if it contains this string in the subject. The subject + * string is in following format: + * + * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com + */ u8 *subject_match; + + /** + * altsubject_match - Constraint for server certificate alt. subject + * + * This substring is 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 this string in an + * alternative subject name extension. + * + * altSubjectName string is in following format: TYPE:VALUE + * + * Example: DNS:server.example.com + * + * Following types are supported: EMAIL, DNS, URI + */ + u8 *altsubject_match; + + /** + * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2) + * + * This file can have one or more trusted CA certificates. If ca_cert2 + * and ca_path2 are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured. Full path to the file should be used since + * working directory may change when wpa_supplicant is run in the + * background. + * + * This field is like ca_cert, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + */ u8 *ca_cert2; + + /** + * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + * + * This field is like ca_path, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *ca_path2; + + /** + * client_cert2 - File path to client certificate file + * + * This field is like client_cert, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + */ u8 *client_cert2; + + /** + * private_key2 - File path to client private key file + * + * This field is like private_key, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + */ u8 *private_key2; + + /** + * private_key2_passwd - Password for private key file + * + * This field is like private_key_passwd, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ u8 *private_key2_passwd; + + /** + * dh_file2 - File path to DH/DSA parameters file (in PEM format) + * + * This field is like dh_file, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + */ u8 *dh_file2; + + /** + * subject_match2 - Constraint for server certificate subject + * + * This field is like subject_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ u8 *subject_match2; - u8 *eap_methods; /* zero (EAP_TYPE_NONE) terminated list of allowed - * EAP methods or NULL = any */ + + /** + * altsubject_match2 - Constraint for server certificate alt. subject + * + * This field is like altsubject_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *altsubject_match2; + + /** + * eap_methods - Allowed EAP methods + * + * Zero (EAP_TYPE_NONE) terminated list of allowed EAP methods or %NULL + * if all methods are accepted. + */ + u8 *eap_methods; + + /** + * phase1 - Phase 1 (outer authentication) parameters + * + * String with field-value pairs, e.g., "peapver=0" or + * "peapver=1 peaplabel=1". + * + * 'peapver' can be used to force which PEAP version (0 or 1) is used. + * + * 'peaplabel=1' can be used to force new label, "client PEAP + * encryption", to be used during key derivation when PEAPv1 or newer. + * + * Most existing PEAPv1 implementation seem to be using the old label, + * "client EAP encryption", and wpa_supplicant is now using that as the + * default value. + * + * Some servers, e.g., Radiator, may require peaplabel=1 configuration + * to interoperate with PEAPv1; see eap_testing.txt for more details. + * + * 'peap_outer_success=0' can be used to terminate PEAP authentication + * on tunneled EAP-Success. This is required with some RADIUS servers + * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., + * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode). + * + * include_tls_length=1 can be used to force wpa_supplicant to include + * TLS Message Length field in all TLS messages even if they are not + * fragmented. + * + * sim_min_num_chal=3 can be used to configure EAP-SIM to require three + * challenges (by default, it accepts 2 or 3). + * + * fast_provisioning=1 can be used to enable in-line provisioning of + * EAP-FAST credentials (PAC) + */ char *phase1; + + /** + * phase2 - Phase2 (inner authentication with TLS tunnel) parameters + * + * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or + * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. + */ char *phase2; + + /** + * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM + * + * This field is used to configure PC/SC smartcard interface. + * Currently, the only configuration is whether this field is %NULL (do + * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC. + * + * This field is used for EAP-SIM and EAP-AKA. + */ char *pcsc; + + /** + * pin - PIN for USIM, GSM SIM, and smartcards + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * If left out, this will be asked through control interface. + */ char *pin; + /** + * engine - Enable OpenSSL engine (e.g., for smartcard access) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + int engine; + + /** + * engine_id - Engine ID for OpenSSL engine + * + * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 + * engine. + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *engine_id; + + /** + * key_id - Key ID for OpenSSL engine + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *key_id; + #define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0) #define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1) - int eapol_flags; /* bit field of IEEE 802.1X/EAPOL options */ + /** + * eapol_flags - Bit field of IEEE 802.1X/EAPOL options (EAPOL_FLAG_*) + */ + int eapol_flags; #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; - /* Per SSID variables that are not read from the configuration file */ + /** + * 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; + + /** + * otp - One-time-password + * + * This field should not be set in configuration step. It is only used + * internally when OTP is entered through the control interface. + */ u8 *otp; + + /** + * otp_len - Length of the otp field + */ size_t otp_len; - int pending_req_identity, pending_req_password; + + /** + * pending_req_identity - Whether there is a pending identity request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_identity; + + /** + * pending_req_password - Whether there is a pending password request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_password; + + /** + * pending_req_pin - Whether there is a pending PIN request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_pin; + + /** + * pending_req_new_password - Pending password update request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_new_password; + + /** + * pending_req_passphrase - Pending passphrase request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_passphrase; + + /** + * pending_req_otp - Whether there is a pending OTP request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ char *pending_req_otp; + + /** + * pending_req_otp_len - Length of the pending OTP request + */ size_t pending_req_otp_len; - int leap, non_leap; + /** + * 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; + /** + * pac_file - File path or blob name for the PAC entries (EAP-FAST) + * + * wpa_supplicant will need to be able to create this file and write + * updates to it when PAC is being provisioned or refreshed. Full path + * to the file should be used since working directory may change when + * wpa_supplicant is run in the background. + * Alternatively, a named configuration blob can be used by setting + * this to blob://<blob name>. + */ char *pac_file; + /** + * 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) + * + * 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). + */ int mode; + + /** + * mschapv2_retry - MSCHAPv2 retry in progress + * + * This field is used internally by EAP-MSCHAPv2 and should not be set + * as part of configuration. + */ + int mschapv2_retry; + + /** + * new_password - New password for password update + * + * This field is used during MSCHAPv2 password update. This is normally + * requested from the user through the control interface and not set + * from configuration. + */ + u8 *new_password; + + /** + * new_password_len - Length of new_password field + */ + size_t new_password_len; + + /** + * 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). + */ + int disabled; }; int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method); -const char * wpa_cipher_txt(int cipher); -const char * wpa_key_mgmt_txt(int key_mgmt, int proto); #endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa_supplicant/config_types.h b/contrib/wpa_supplicant/config_types.h new file mode 100644 index 0000000..12b57cb --- /dev/null +++ b/contrib/wpa_supplicant/config_types.h @@ -0,0 +1,14 @@ +#ifndef CONFIG_TYPES_H +#define CONFIG_TYPES_H + +struct hostapd_ip_addr { + union { + struct in_addr v4; +#ifdef CONFIG_IPV6 + struct in6_addr v6; +#endif /* CONFIG_IPV6 */ + } u; + int af; /* AF_INET / AF_INET6 */ +}; + +#endif /* CONFIG_TYPES_H */ diff --git a/contrib/wpa_supplicant/crypto.c b/contrib/wpa_supplicant/crypto.c index cd278e0..b4c8189 100644 --- a/contrib/wpa_supplicant/crypto.c +++ b/contrib/wpa_supplicant/crypto.c @@ -12,11 +12,17 @@ * See README and COPYING for more details. */ +#include <string.h> +#include <sys/types.h> + #include <openssl/md4.h> +#include <openssl/md5.h> +#include <openssl/sha.h> #include <openssl/des.h> +#include <openssl/aes.h> #include "common.h" - +#include "crypto.h" #if OPENSSL_VERSION_NUMBER < 0x00907000 #define DES_key_schedule des_key_schedule @@ -27,7 +33,7 @@ #endif /* openssl < 0.9.7 */ -void md4_vector(size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD4_CTX ctx; int i; @@ -39,17 +45,6 @@ void md4_vector(size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) } -void md4(const u8 *addr, size_t len, u8 *mac) -{ - md4_vector(1, &addr, &len, mac); -} - - -/** - * @clear: 8 octets (in) - * @key: 7 octets (in) (no parity bits included) - * @cypher: 8 octets (out) - */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; @@ -69,3 +64,91 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, DES_ENCRYPT); } + + +#ifdef EAP_TLS_FUNCS +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + int i; + + MD5_Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5_Update(&ctx, addr[i], len[i]); + MD5_Final(mac, &ctx); +} + + +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + SHA_CTX ctx; + int i; + + SHA1_Init(&ctx); + for (i = 0; i < num_elem; i++) + SHA1_Update(&ctx, addr[i], len[i]); + SHA1_Final(mac, &ctx); +} + + +void sha1_transform(u8 *state, const u8 data[64]) +{ + SHA_CTX context; + memset(&context, 0, sizeof(context)); + memcpy(&context.h0, state, 5 * 4); + SHA1_Transform(&context, data); + memcpy(state, &context.h0, 5 * 4); +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + AES_KEY *ak; + ak = malloc(sizeof(*ak)); + if (ak == NULL) + return NULL; + if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { + free(ak); + return NULL; + } + return ak; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + AES_encrypt(plain, crypt, ctx); +} + + +void aes_encrypt_deinit(void *ctx) +{ + free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + AES_KEY *ak; + ak = malloc(sizeof(*ak)); + if (ak == NULL) + return NULL; + if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { + free(ak); + return NULL; + } + return ak; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + AES_decrypt(crypt, plain, ctx); +} + + +void aes_decrypt_deinit(void *ctx) +{ + free(ctx); +} +#endif /* EAP_TLS_FUNCS */ diff --git a/contrib/wpa_supplicant/crypto.h b/contrib/wpa_supplicant/crypto.h index 3e1a0e5..e664861 100644 --- a/contrib/wpa_supplicant/crypto.h +++ b/contrib/wpa_supplicant/crypto.h @@ -1,8 +1,123 @@ +/* + * WPA Supplicant / wrapper functions for crypto libraries + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 defines the cryptographic functions that need to be implemented + * for wpa_supplicant and hostapd. When TLS is not used, internal + * implementation of MD5, SHA1, and AES is used and no external libraries are + * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the + * crypto library used by the TLS implementation is expected to be used for + * non-TLS needs, too, in order to save space by not implementing these + * functions twice. + * + * Wrapper code for using each crypto library is in its own file (crypto*.c) + * and one of these files is build and linked in to provide the functions + * defined here. + */ + #ifndef CRYPTO_H #define CRYPTO_H +/** + * md4_vector - MD4 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); -void md4(const u8 *addr, size_t len, u8 *mac); + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * sha1_transform - Perform one SHA-1 transform step + * @state: SHA-1 state + * @data: Input data for the SHA-1 transform + * + * This function is used to implement random number generation specified in + * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is + * similar to SHA-1, but has different message padding and as such, access to + * just part of the SHA-1 is needed. + */ +void sha1_transform(u8 *state, const u8 data[64]); + +/** + * des_encrypt - Encrypt one block with DES + * @clear: 8 octets (in) + * @key: 7 octets (in) (no parity bits included) + * @cypher: 8 octets (out) + */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); +/** + * aes_encrypt_init - Initialize AES for encryption + * @key: Encryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_encrypt_init(const u8 *key, size_t len); + +/** + * aes_encrypt - Encrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @plain: Plaintext data to be encrypted (16 bytes) + * @crypt: Buffer for the encrypted data (16 bytes) + */ +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); + +/** + * aes_encrypt_deinit - Deinitialize AES encryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_encrypt_deinit(void *ctx); + +/** + * aes_decrypt_init - Initialize AES for decryption + * @key: Decryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_decrypt_init(const u8 *key, size_t len); + +/** + * aes_decrypt - Decrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @crypt: Encrypted data (16 bytes) + * @plain: Buffer for the decrypted data (16 bytes) + */ +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); + +/** + * aes_decrypt_deinit - Deinitialize AES decryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_decrypt_deinit(void *ctx); + + #endif /* CRYPTO_H */ diff --git a/contrib/wpa_supplicant/crypto_gnutls.c b/contrib/wpa_supplicant/crypto_gnutls.c new file mode 100644 index 0000000..15f9b54 --- /dev/null +++ b/contrib/wpa_supplicant/crypto_gnutls.c @@ -0,0 +1,163 @@ +/* + * WPA Supplicant / wrapper functions for libgcrypt + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 <stdio.h> +#include <sys/types.h> +#include <gcrypt.h> + +#include "common.h" +#include "crypto.h" + +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + gcry_md_hd_t hd; + unsigned char *p; + int i; + + if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR) + return; + for (i = 0; i < num_elem; i++) + gcry_md_write(hd, addr[i], len[i]); + p = gcry_md_read(hd, GCRY_MD_MD4); + if (p) + memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4)); + gcry_md_close(hd); +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + gcry_cipher_hd_t hd; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + gcry_cipher_open(&hd, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0); + gcry_err_code(gcry_cipher_setkey(hd, pkey, 8)); + gcry_cipher_encrypt(hd, cypher, 8, clear, 8); + gcry_cipher_close(hd); +} + + +#ifdef EAP_TLS_FUNCS +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + gcry_md_hd_t hd; + unsigned char *p; + int i; + + if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) + return; + for (i = 0; i < num_elem; i++) + gcry_md_write(hd, addr[i], len[i]); + p = gcry_md_read(hd, GCRY_MD_MD5); + if (p) + memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5)); + gcry_md_close(hd); +} + + +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + gcry_md_hd_t hd; + unsigned char *p; + int i; + + if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR) + return; + for (i = 0; i < num_elem; i++) + gcry_md_write(hd, addr[i], len[i]); + p = gcry_md_read(hd, GCRY_MD_SHA1); + if (p) + memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1)); + gcry_md_close(hd); +} + + +void sha1_transform(u8 *state, const u8 data[64]) +{ + /* FIX: how to do this with libgcrypt? */ +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + gcry_cipher_hd_t hd; + + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != + GPG_ERR_NO_ERROR) { + printf("cipher open failed\n"); + return NULL; + } + if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) { + printf("setkey failed\n"); + gcry_cipher_close(hd); + return NULL; + } + + return hd; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + gcry_cipher_hd_t hd = ctx; + gcry_cipher_encrypt(hd, crypt, 16, plain, 16); +} + + +void aes_encrypt_deinit(void *ctx) +{ + gcry_cipher_hd_t hd = ctx; + gcry_cipher_close(hd); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + gcry_cipher_hd_t hd; + + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != + GPG_ERR_NO_ERROR) + return NULL; + if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) { + gcry_cipher_close(hd); + return NULL; + } + + return hd; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + gcry_cipher_hd_t hd = ctx; + gcry_cipher_decrypt(hd, plain, 16, crypt, 16); +} + + +void aes_decrypt_deinit(void *ctx) +{ + gcry_cipher_hd_t hd = ctx; + gcry_cipher_close(hd); +} +#endif /* EAP_TLS_FUNCS */ diff --git a/contrib/wpa_supplicant/ctrl_iface.c b/contrib/wpa_supplicant/ctrl_iface.c index 25cf0db..01ea456 100644 --- a/contrib/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa_supplicant/ctrl_iface.c @@ -1,5 +1,5 @@ /* - * WPA Supplicant / UNIX domain socket -based control interface + * WPA Supplicant / UNIX domain and UDP socket -based control interface * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,8 @@ #include <errno.h> #ifndef CONFIG_NATIVE_WINDOWS #include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> #include <sys/un.h> #include <sys/uio.h> #endif /* CONFIG_NATIVE_WINDOWS */ @@ -34,12 +36,11 @@ #include "wpa_supplicant_i.h" #include "ctrl_iface.h" #include "l2_packet.h" +#include "preauth.h" +#include "wpa_ctrl.h" +#include "eap.h" -#ifdef CONFIG_NATIVE_WINDOWS -typedef int socklen_t; -#endif /* CONFIG_NATIVE_WINDOWS */ - #ifdef CONFIG_CTRL_IFACE_UDP #define CTRL_IFACE_SOCK struct sockaddr_in #else /* CONFIG_CTRL_IFACE_UDP */ @@ -47,6 +48,13 @@ typedef int socklen_t; #endif /* CONFIG_CTRL_IFACE_UDP */ +/** + * 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.c and should not be touched directly from other files. + */ struct wpa_ctrl_dst { struct wpa_ctrl_dst *next; CTRL_IFACE_SOCK addr; @@ -56,33 +64,11 @@ struct wpa_ctrl_dst { }; -static const char * wpa_state_txt(int state) -{ - switch (state) { - case WPA_DISCONNECTED: - return "DISCONNECTED"; - case WPA_SCANNING: - return "SCANNING"; - 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"; - } -} - - static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { char *value; + int ret = 0; value = strchr(cmd, ' '); if (value == NULL) @@ -102,9 +88,21 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, } else if (strcasecmp(cmd, "EAPOL::maxStart") == 0) { eapol_sm_configure(wpa_s->eapol, -1, -1, -1, atoi(value)); + } else if (strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, + atoi(value))) + ret = -1; + } else if (strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") == 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, + atoi(value))) + ret = -1; + } else if (strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { + if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value))) + ret = -1; } else - return -1; - return 0; + ret = -1; + + return ret; } @@ -120,8 +118,8 @@ static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, } wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid)); - rsn_preauth_deinit(wpa_s); - if (rsn_preauth_init(wpa_s, bssid)) + rsn_preauth_deinit(wpa_s->wpa); + if (rsn_preauth_init(wpa_s->wpa, bssid, wpa_s->current_ssid)) return -1; return 0; @@ -252,13 +250,7 @@ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", (u8 *) pos, strlen(pos)); - ssid = wpa_s->conf->ssid; - while (ssid) { - if (id == ssid->id) - break; - ssid = ssid->next; - } - + 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); @@ -279,6 +271,19 @@ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, ssid->pending_req_password = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; + } else if (strcmp(rsp, "NEW_PASSWORD") == 0) { + free(ssid->new_password); + ssid->new_password = (u8 *) strdup(pos); + ssid->new_password_len = strlen(pos); + ssid->pending_req_new_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (strcmp(rsp, "PIN") == 0) { + free(ssid->pin); + ssid->pin = strdup(pos); + ssid->pending_req_pin = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; } else if (strcmp(rsp, "OTP") == 0) { free(ssid->otp); ssid->otp = (u8 *) strdup(pos); @@ -286,6 +291,12 @@ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, free(ssid->pending_req_otp); ssid->pending_req_otp = NULL; ssid->pending_req_otp_len = 0; + } else if (strcmp(rsp, "PASSPHRASE") == 0) { + free(ssid->private_key_passwd); + ssid->private_key_passwd = (u8 *) strdup(pos); + ssid->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; @@ -299,46 +310,626 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, const char *params, char *buf, size_t buflen) { - char *pos, *end; + char *pos, *end, tmp[30]; int res, verbose; verbose = strcmp(params, "-VERBOSE") == 0; pos = buf; end = buf + buflen; - pos += snprintf(pos, end - pos, "bssid=" MACSTR "\n", - MAC2STR(wpa_s->bssid)); - if (wpa_s->current_ssid) { - pos += snprintf(pos, end - pos, "ssid=%s\n", - wpa_ssid_txt(wpa_s->current_ssid->ssid, - wpa_s->current_ssid->ssid_len)); - } - pos += snprintf(pos, end - pos, - "pairwise_cipher=%s\n" - "group_cipher=%s\n" - "key_mgmt=%s\n" - "wpa_state=%s\n", - wpa_cipher_txt(wpa_s->pairwise_cipher), - wpa_cipher_txt(wpa_s->group_cipher), - wpa_key_mgmt_txt(wpa_s->key_mgmt, wpa_s->proto), - wpa_state_txt(wpa_s->wpa_state)); - - res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, verbose); - if (res >= 0) - pos += res; + if (wpa_s->wpa_state >= WPA_ASSOCIATED) { + pos += snprintf(pos, end - pos, "bssid=" MACSTR "\n", + MAC2STR(wpa_s->bssid)); + if (wpa_s->current_ssid) { + pos += snprintf(pos, end - pos, "ssid=%s\n", + wpa_ssid_txt(wpa_s->current_ssid->ssid, + wpa_s->current_ssid-> + ssid_len)); + } + + pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose); + } + pos += snprintf(pos, end - pos, "wpa_state=%s\n", + wpa_supplicant_state_txt(wpa_s->wpa_state)); + + if (wpa_s->l2 && + l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) + pos += snprintf(pos, end - pos, "ip_address=%s\n", tmp); - if (wpa_s->preauth_eapol) { - pos += snprintf(pos, end - pos, "Pre-authentication " - "EAPOL state machines:\n"); - res = eapol_sm_get_status(wpa_s->preauth_eapol, - pos, end - pos, verbose); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X || + 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 = 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; + } + + memcpy(ssid->bssid, bssid, ETH_ALEN); + ssid->bssid_set = + memcmp(bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0; + + + 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; + + pos = buf; + end = buf + buflen; + pos += snprintf(pos, end - pos, "network id / ssid / bssid / flags\n"); + + ssid = wpa_s->conf->ssid; + while (ssid) { + pos += snprintf(pos, end - pos, "%d\t%s", + ssid->id, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + if (ssid->bssid_set) { + pos += snprintf(pos, end - pos, "\t" MACSTR, + MAC2STR(ssid->bssid)); + } else { + pos += snprintf(pos, end - pos, "\tany"); + } + pos += snprintf(pos, end - pos, "\t%s%s", + ssid == wpa_s->current_ssid ? "[CURRENT]" : "", + ssid->disabled ? "[DISABLED]" : ""); + pos += snprintf(pos, end - pos, "\n"); + + ssid = ssid->next; + } + return pos - buf; } +static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) +{ + int first = 1; + pos += snprintf(pos, end - pos, "-"); + if (cipher & WPA_CIPHER_NONE) { + pos += snprintf(pos, end - pos, "%sNONE", first ? "" : "+"); + first = 0; + } + if (cipher & WPA_CIPHER_WEP40) { + pos += snprintf(pos, end - pos, "%sWEP40", first ? "" : "+"); + first = 0; + } + if (cipher & WPA_CIPHER_WEP104) { + pos += snprintf(pos, end - pos, "%sWEP104", first ? "" : "+"); + first = 0; + } + if (cipher & WPA_CIPHER_TKIP) { + pos += snprintf(pos, end - pos, "%sTKIP", first ? "" : "+"); + first = 0; + } + if (cipher & WPA_CIPHER_CCMP) { + pos += snprintf(pos, end - pos, "%sCCMP", first ? "" : "+"); + first = 0; + } + return pos; +} + + +static char * wpa_supplicant_ie_txt(struct wpa_supplicant *wpa_s, + char *pos, char *end, const char *proto, + const u8 *ie, size_t ie_len) +{ + struct wpa_ie_data data; + int first; + + pos += snprintf(pos, end - pos, "[%s-", proto); + + if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) { + pos += snprintf(pos, end - pos, "?]"); + return pos; + } + + first = 1; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + pos += snprintf(pos, end - pos, "%sEAP", first ? "" : "+"); + first = 0; + } + if (data.key_mgmt & WPA_KEY_MGMT_PSK) { + pos += snprintf(pos, end - pos, "%sPSK", first ? "" : "+"); + first = 0; + } + if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) { + pos += snprintf(pos, end - pos, "%sNone", first ? "" : "+"); + first = 0; + } + + pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); + + if (data.capabilities & WPA_CAPABILITY_PREAUTH) + pos += snprintf(pos, end - pos, "-preauth"); + + pos += snprintf(pos, end - pos, "]"); + + return pos; +} + + +static int wpa_supplicant_ctrl_iface_scan_results( + struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + char *pos, *end; + struct wpa_scan_result *res; + int i; + + if (wpa_s->scan_results == NULL && + wpa_supplicant_get_scan_results(wpa_s) < 0) + return 0; + + pos = buf; + end = buf + buflen; + pos += snprintf(pos, end - pos, "bssid / frequency / signal level / " + "flags / ssid\n"); + + for (i = 0; i < wpa_s->num_scan_results; i++) { + res = &wpa_s->scan_results[i]; + pos += snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", + MAC2STR(res->bssid), res->freq, res->level); + if (res->wpa_ie_len) { + pos = wpa_supplicant_ie_txt(wpa_s, pos, end, "WPA", + res->wpa_ie, + res->wpa_ie_len); + } + if (res->rsn_ie_len) { + pos = wpa_supplicant_ie_txt(wpa_s, pos, end, "WPA2", + res->rsn_ie, + res->rsn_ie_len); + } + if (!res->wpa_ie_len && !res->rsn_ie_len && + res->caps & IEEE80211_CAP_PRIVACY) + pos += snprintf(pos, end - pos, "[WEP]"); + if (res->caps & IEEE80211_CAP_IBSS) + pos += snprintf(pos, end - pos, "[IBSS]"); + + pos += snprintf(pos, end - pos, "\t%s", + wpa_ssid_txt(res->ssid, res->ssid_len)); + + pos += snprintf(pos, end - pos, "\n"); + } + + 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 (strcmp(cmd, "any") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any"); + ssid = wpa_s->conf->ssid; + while (ssid) { + ssid->disabled = 0; + ssid = ssid->next; + } + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return 0; + } + + 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 != wpa_s->current_ssid && wpa_s->current_ssid) + wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING); + + /* Mark all other networks disabled and trigger reassociation */ + ssid = wpa_s->conf->ssid; + while (ssid) { + ssid->disabled = id != ssid->id; + ssid = ssid->next; + } + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + 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>" */ + 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 (wpa_s->current_ssid == NULL && ssid->disabled) { + /* + * 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); + } + ssid->disabled = 0; + + 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>" */ + 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 == wpa_s->current_ssid) + wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING); + ssid->disabled = 1; + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_add_network( + struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + struct wpa_ssid *ssid; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK"); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + ssid->disabled = 1; + wpa_config_set_network_defaults(ssid); + + return snprintf(buf, buflen, "%d\n", ssid->id); +} + + +static int wpa_supplicant_ctrl_iface_remove_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + /* cmd: "<network id>" */ + 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) + wpa_supplicant_disassociate(wpa_s, 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 = strchr(cmd, ' '); + if (name == NULL) + return -1; + *name++ = '\0'; + + value = strchr(name, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s' " + "value='%s'", id, name, 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' to '%s'", name, value); + return -1; + } + + if ((strcmp(name, "psk") == 0 && value[0] == '"' && ssid->ssid_len) || + (strcmp(name, "ssid") == 0 && ssid->passphrase)) + wpa_config_update_psk(ssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_get_network( + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) +{ + int id; + struct wpa_ssid *ssid; + char *name, *value; + + /* cmd: "<network id> <variable name>" */ + name = strchr(cmd, ' '); + if (name == NULL) + 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(ssid, name); + if (value == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " + "variable '%s'", name); + return -1; + } + + snprintf(buf, buflen, "%s", value); + + free(value); + + return strlen(buf); +} + + +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; +} + + +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, first = 1; + char *pos, *end; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field); + + if (strcmp(field, "eap") == 0) { + return eap_get_names(buf, buflen); + } + + res = wpa_drv_get_capa(wpa_s, &capa); + + pos = buf; + end = pos + buflen; + + if (strcmp(field, "pairwise") == 0) { + if (res < 0) + return snprintf(buf, buflen, "CCMP TKIP NONE"); + + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { + pos += snprintf(pos, end - pos, "%sCCMP", + first ? "" : " "); + first = 0; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { + pos += snprintf(pos, end - pos, "%sTKIP", + first ? "" : " "); + first = 0; + } + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { + pos += snprintf(pos, end - pos, "%sNONE", + first ? "" : " "); + first = 0; + } + + return pos - buf; + } + + if (strcmp(field, "group") == 0) { + if (res < 0) + return snprintf(buf, buflen, "CCMP TKIP WEP104 WEP40"); + + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { + pos += snprintf(pos, end - pos, "%sCCMP", + first ? "" : " "); + first = 0; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { + pos += snprintf(pos, end - pos, "%sTKIP", + first ? "" : " "); + first = 0; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { + pos += snprintf(pos, end - pos, "%sWEP104", + first ? "" : " "); + first = 0; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { + pos += snprintf(pos, end - pos, "%sWEP40", + first ? "" : " "); + first = 0; + } + + return pos - buf; + } + + if (strcmp(field, "key_mgmt") == 0) { + if (res < 0) { + return snprintf(buf, buflen, "WPA-PSK WPA-EAP " + "IEEE8021X WPA-NONE NONE"); + } + + pos += snprintf(pos, end - pos, "NONE IEEE8021X"); + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) + pos += snprintf(pos, end - pos, " WPA-EAP"); + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) + pos += snprintf(pos, end - pos, " WPA-PSK"); + + if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) + pos += snprintf(pos, end - pos, " WPA-NONE"); + + return pos - buf; + } + + if (strcmp(field, "proto") == 0) { + if (res < 0) + return snprintf(buf, buflen, "RSN WPA"); + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + pos += snprintf(pos, end - pos, "%sRSN", + first ? "" : " "); + first = 0; + } + + if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { + pos += snprintf(pos, end - pos, "%sWPA", + first ? "" : " "); + first = 0; + } + + return pos - buf; + } + + if (strcmp(field, "auth_alg") == 0) { + if (res < 0) + return snprintf(buf, buflen, "OPEN SHARED LEAP"); + + if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { + pos += snprintf(pos, end - pos, "%sOPEN", + first ? "" : " "); + first = 0; + } + + if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { + pos += snprintf(pos, end - pos, "%sSHARED", + first ? "" : " "); + first = 0; + } + + if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { + pos += snprintf(pos, end - pos, "%sLEAP", + first ? "" : " "); + first = 0; + } + + return pos - buf; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", + field); + + return -1; +} + + static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { @@ -359,7 +950,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, return; } buf[res] = '\0'; - if (strncmp(buf, "CTRL-RSP-", 9) == 0) { + if (strncmp(buf, WPA_CTRL_RSP, strlen(WPA_CTRL_RSP)) == 0) { wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); } else { @@ -380,7 +971,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, memcpy(reply, "PONG\n", 5); reply_len = 5; } else if (strcmp(buf, "MIB") == 0) { - reply_len = wpa_get_mib(wpa_s, reply, reply_size); + reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); if (reply_len >= 0) { res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, reply_size - reply_len); @@ -393,7 +984,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = wpa_supplicant_ctrl_iface_status( wpa_s, buf + 6, reply, reply_size); } else if (strcmp(buf, "PMKSA") == 0) { - reply_len = pmksa_cache_list(wpa_s, reply, reply_size); + reply_len = pmksa_cache_list(wpa_s->wpa, reply, reply_size); } else if (strncmp(buf, "SET ", 4) == 0) { if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) reply_len = -1; @@ -402,6 +993,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (strcmp(buf, "LOGOFF") == 0) { eapol_sm_notify_logoff(wpa_s->eapol, TRUE); } else if (strcmp(buf, "REASSOCIATE") == 0) { + wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (strncmp(buf, "PREAUTH ", 8) == 0) { @@ -419,8 +1011,9 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, if (wpa_supplicant_ctrl_iface_level(wpa_s, &from, fromlen, buf + 6)) reply_len = -1; - } else if (strncmp(buf, "CTRL-RSP-", 9) == 0) { - if (wpa_supplicant_ctrl_iface_ctrl_rsp(wpa_s, buf + 9)) + } else if (strncmp(buf, WPA_CTRL_RSP, strlen(WPA_CTRL_RSP)) == 0) { + if (wpa_supplicant_ctrl_iface_ctrl_rsp( + wpa_s, buf + strlen(WPA_CTRL_RSP))) reply_len = -1; else ctrl_rsp = 1; @@ -429,6 +1022,48 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = -1; } else if (strcmp(buf, "TERMINATE") == 0) { eloop_terminate(); + } else if (strncmp(buf, "BSSID ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6)) + reply_len = -1; + } else if (strcmp(buf, "LIST_NETWORKS") == 0) { + reply_len = wpa_supplicant_ctrl_iface_list_networks( + wpa_s, reply, reply_size); + } else if (strcmp(buf, "DISCONNECT") == 0) { + wpa_s->disconnected = 1; + wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING); + } else if (strcmp(buf, "SCAN") == 0) { + wpa_s->scan_req = 2; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (strcmp(buf, "SCAN_RESULTS") == 0) { + reply_len = wpa_supplicant_ctrl_iface_scan_results( + wpa_s, reply, reply_size); + } else if (strncmp(buf, "SELECT_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (strncmp(buf, "ENABLE_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (strncmp(buf, "DISABLE_NETWORK ", 16) == 0) { + if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16)) + reply_len = -1; + } else if (strcmp(buf, "ADD_NETWORK") == 0) { + reply_len = wpa_supplicant_ctrl_iface_add_network( + wpa_s, reply, reply_size); + } else if (strncmp(buf, "REMOVE_NETWORK ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15)) + reply_len = -1; + } else if (strncmp(buf, "SET_NETWORK ", 12) == 0) { + if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12)) + reply_len = -1; + } else if (strncmp(buf, "GET_NETWORK ", 12) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get_network( + wpa_s, buf + 12, reply, reply_size); + } else if (strcmp(buf, "SAVE_CONFIG") == 0) { + if (wpa_supplicant_ctrl_iface_save_config(wpa_s)) + reply_len = -1; + } else if (strncmp(buf, "GET_CAPABILITY ", 15) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get_capability( + wpa_s, buf + 15, reply, reply_size); } else { memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -448,6 +1083,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, } +#ifndef CONFIG_CTRL_IFACE_UDP static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) { char *buf; @@ -477,13 +1113,24 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) #endif /* __CYGWIN__ */ return buf; } +#endif /* CONFIG_CTRL_IFACE_UDP */ +/** + * wpa_supplicant_ctrl_iface_init - Initialize control interface + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * Initialize the control interface and start receiving commands from external + * programs. + */ int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) { CTRL_IFACE_SOCK addr; int s = -1; +#ifndef CONFIG_CTRL_IFACE_UDP char *fname = NULL; +#endif /* CONFIG_CTRL_IFACE_UDP */ wpa_s->ctrl_sock = -1; @@ -500,9 +1147,9 @@ int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl((127 << 24) | 1); - addr.sin_port = htons(9877); + addr.sin_port = htons(WPA_CTRL_IFACE_PORT); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(AF_UNIX)"); + perror("bind(AF_INET)"); goto fail; } #else /* CONFIG_CTRL_IFACE_UDP */ @@ -592,20 +1239,31 @@ int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) fail: if (s >= 0) close(s); +#ifndef CONFIG_CTRL_IFACE_UDP if (fname) { unlink(fname); free(fname); } +#endif /* CONFIG_CTRL_IFACE_UDP */ return -1; } +/** + * wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface + * @wpa_s: Pointer to wpa_supplicant data + * + * Deinitialize the control interface that was initialized with + * wpa_supplicant_ctrl_iface_init(). + */ void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s) { struct wpa_ctrl_dst *dst, *prev; if (wpa_s->ctrl_sock > -1) { +#ifndef CONFIG_CTRL_IFACE_UDP char *fname; +#endif /* CONFIG_CTRL_IFACE_UDP */ eloop_unregister_read_sock(wpa_s->ctrl_sock); if (wpa_s->ctrl_dst) { /* @@ -619,6 +1277,7 @@ void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s) } close(wpa_s->ctrl_sock); wpa_s->ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP fname = wpa_supplicant_ctrl_iface_path(wpa_s); if (fname) unlink(fname); @@ -633,6 +1292,7 @@ void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s) perror("rmdir[ctrl_interface]"); } } +#endif /* CONFIG_CTRL_IFACE_UDP */ } dst = wpa_s->ctrl_dst; @@ -644,6 +1304,15 @@ void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s) } +/** + * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors + * @wpa_s: Pointer to wpa_supplicant data + * @level: Priority level of the message + * @buf: Message data + * @len: Message length + * + * Send a packet to all monitor programs attached to the control interface. + */ void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, char *buf, size_t len) { @@ -678,9 +1347,7 @@ void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, if (sendto(wpa_s->ctrl_sock, sbuf, llen + len, 0, (struct sockaddr *) &dst->addr, sizeof(dst->addr)) < 0) { - fprintf(stderr, "CTRL_IFACE monitor[%d]: ", - idx); - perror("sendto"); + perror("sendto(CTRL_IFACE monitor)"); dst->errors++; if (dst->errors > 10) { wpa_supplicant_ctrl_iface_detach( @@ -720,9 +1387,7 @@ void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; if (sendmsg(wpa_s->ctrl_sock, &msg, 0) < 0) { - fprintf(stderr, "CTRL_IFACE monitor[%d]: ", - idx); - perror("sendmsg"); + perror("sendmsg(CTRL_IFACE monitor)"); dst->errors++; if (dst->errors > 10) { wpa_supplicant_ctrl_iface_detach( @@ -737,3 +1402,287 @@ void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, } #endif /* CONFIG_CTRL_IFACE_UDP */ } + + +/** + * wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor + * @wpa_s: Pointer to wpa_supplicant data + * + * 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. + */ +void wpa_supplicant_ctrl_iface_wait(struct wpa_supplicant *wpa_s) +{ + fd_set rfds; + + if (wpa_s->ctrl_sock < 0) + return; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", + wpa_s->ifname); + + FD_ZERO(&rfds); + FD_SET(wpa_s->ctrl_sock, &rfds); + select(wpa_s->ctrl_sock + 1, &rfds, NULL, NULL, NULL); +} + + +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> + */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); + + memset(&iface, 0, sizeof(iface)); + + do { + iface.ifname = pos = cmd; + pos = strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.ifname[0] == '\0') + return -1; + if (pos == NULL) + break; + + iface.confname = pos; + pos = strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.confname[0] == '\0') + iface.confname = NULL; + if (pos == NULL) + break; + + iface.driver = pos; + pos = strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.driver[0] == '\0') + iface.driver = NULL; + if (pos == NULL) + break; + + iface.ctrl_interface = pos; + pos = 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 = strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (iface.driver_param[0] == '\0') + iface.driver_param = 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_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_global *global = eloop_ctx; + char buf[256]; + int res; + CTRL_IFACE_SOCK from; + socklen_t fromlen = sizeof(from); + char *reply; + const int reply_size = 2048; + int reply_len; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface", (u8 *) buf, res); + + reply = malloc(reply_size); + if (reply == NULL) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + return; + } + + memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (strcmp(buf, "PING") == 0) { + memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (strncmp(buf, "INTERFACE_ADD ", 14) == 0) { + if (wpa_supplicant_global_iface_add(global, buf + 14)) + reply_len = -1; + } else if (strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) { + if (wpa_supplicant_global_iface_remove(global, buf + 17)) + reply_len = -1; + } else { + memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); + free(reply); +} + + +/** + * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: 0 on success, -1 on failure + * + * Initialize the global control interface and start receiving commands from + * external programs. + */ +int wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + CTRL_IFACE_SOCK addr; + int s = -1; +#ifndef CONFIG_CTRL_IFACE_UDP + char *fname = NULL; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + global->ctrl_sock = -1; + + if (global->params.ctrl_interface == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "Global control interface '%s'", + global->params.ctrl_interface); + +#ifdef CONFIG_CTRL_IFACE_UDP + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_INET)"); + goto fail; + } + + 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(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(AF_INET)"); + goto fail; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, global->params.ctrl_interface, + sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(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(s, (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; + } + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + + global->ctrl_sock = s; + eloop_register_read_sock(s, wpa_supplicant_global_ctrl_iface_receive, + global, NULL); + + return 0; + +fail: + if (s >= 0) + close(s); +#ifndef CONFIG_CTRL_IFACE_UDP + if (fname) { + unlink(fname); + free(fname); + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + return -1; +} + + +/** + * wpa_supplicant_global_ctrl_iface_deinit - Deinitialize global ctrl interface + * @global: Pointer to global data from wpa_supplicant_init() + * + * Deinitialize the global control interface that was initialized with + * wpa_supplicant_global_ctrl_iface_init(). + */ +void wpa_supplicant_global_ctrl_iface_deinit(struct wpa_global *global) +{ + if (global->ctrl_sock < 0) + return; + + eloop_unregister_read_sock(global->ctrl_sock); + close(global->ctrl_sock); + global->ctrl_sock = -1; + +#ifndef CONFIG_CTRL_IFACE_UDP + if (global->params.ctrl_interface) + unlink(global->params.ctrl_interface); +#endif /* CONFIG_CTRL_IFACE_UDP */ +} diff --git a/contrib/wpa_supplicant/ctrl_iface.h b/contrib/wpa_supplicant/ctrl_iface.h index d7ad6df..affa604 100644 --- a/contrib/wpa_supplicant/ctrl_iface.h +++ b/contrib/wpa_supplicant/ctrl_iface.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / UNIX domain socket -based control interface + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -7,6 +21,9 @@ int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s); void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s); void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, char *buf, size_t len); +void wpa_supplicant_ctrl_iface_wait(struct wpa_supplicant *wpa_s); +int wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global); +void wpa_supplicant_global_ctrl_iface_deinit(struct wpa_global *global); #else /* CONFIG_CTRL_IFACE */ @@ -26,6 +43,22 @@ wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, { } +static inline void +wpa_supplicant_ctrl_iface_wait(struct wpa_supplicant *wpa_s) +{ +} + +static inline int +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + return 0; +} + +static inline void +wpa_supplicant_global_ctrl_iface_deinit(struct wpa_global *global) +{ +} + #endif /* CONFIG_CTRL_IFACE */ #endif /* CTRL_IFACE_H */ diff --git a/contrib/wpa_supplicant/defconfig b/contrib/wpa_supplicant/defconfig index 7e8e381..be013b4 100644 --- a/contrib/wpa_supplicant/defconfig +++ b/contrib/wpa_supplicant/defconfig @@ -50,7 +50,9 @@ CONFIG_DRIVER_HOSTAP=y #CFLAGS += -I../madwifi/wpa # Driver interface for Prism54 driver -CONFIG_DRIVER_PRISM54=y +# (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 #CONFIG_DRIVER_NDISWRAPPER=y @@ -88,6 +90,9 @@ CONFIG_DRIVER_WEXT=y # Driver interface for development testing #CONFIG_DRIVER_TEST=y +# Driver interface for wired Ethernet drivers +CONFIG_DRIVER_WIRED=y + # Enable IEEE 802.1X Supplicant (automatically included if any EAP method is # included) CONFIG_IEEE8021X_EAPOL=y @@ -119,6 +124,9 @@ CONFIG_EAP_OTP=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 @@ -129,6 +137,10 @@ CONFIG_EAP_LEAP=y # 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 @@ -143,12 +155,22 @@ CONFIG_PKCS12=y # Include control interface for external programs, e.g, wpa_cli CONFIG_CTRL_IFACE=y -# Include interface for using external supplicant (Xsupplicant) for EAP -# authentication -#CONFIG_XSUPPLICANT_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 + +# Select configuration backend: +# file = text file (e.g., wpa_supplicant.conf) +CONFIG_BACKEND=file diff --git a/contrib/wpa_supplicant/defs.h b/contrib/wpa_supplicant/defs.h index a5a515c..6f9881d 100644 --- a/contrib/wpa_supplicant/defs.h +++ b/contrib/wpa_supplicant/defs.h @@ -1,14 +1,131 @@ +/* + * WPA Supplicant - Common definitions + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 DEFS_H #define DEFS_H -#ifdef CONFIG_NATIVE_WINDOWS #ifdef FALSE #undef FALSE #endif #ifdef TRUE #undef TRUE #endif -#endif /* CONFIG_NATIVE_WINDOWS */ typedef enum { FALSE = 0, TRUE = 1 } Boolean; + +typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg; +typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, + CIPHER_WEP104 } wpa_cipher; +typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, + KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE } wpa_key_mgmt; + +/** + * enum wpa_states - wpa_supplicant state + * + * These enumeration values are used to indicate the current wpa_supplicant + * state (wpa_s->wpa_state). The current state can be retrieved with + * wpa_supplicant_get_state() function and the state can be changed by calling + * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the + * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used + * to access the state variable. + */ +typedef enum { + /** + * WPA_DISCONNECTED - Disconnected state + * + * This state indicates that client is not associated, but is likely to + * start looking for an access point. This state is entered when a + * connection is lost. + */ + WPA_DISCONNECTED, + + /** + * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) + * + * This state is entered if there are no enabled networks in the + * configuration. wpa_supplicant is not trying to associate with a new + * network and external interaction (e.g., ctrl_iface call to add or + * enable a network) is needed to start association. + */ + WPA_INACTIVE, + + /** + * WPA_SCANNING - Scanning for a network + * + * This state is entered when wpa_supplicant starts scanning for a + * network. + */ + WPA_SCANNING, + + /** + * WPA_ASSOCIATING - Trying to associate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to associate with and the driver is configured to try to associate + * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this + * state is entered when the driver is configured to try to associate + * with a network using the configured SSID and security policy. + */ + WPA_ASSOCIATING, + + /** + * WPA_ASSOCIATED - Association completed + * + * This state is entered when the driver reports that association has + * been successfully completed with an AP. If IEEE 802.1X is used + * (with or without WPA/WPA2), wpa_supplicant remains in this state + * until the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_ASSOCIATED, + + /** + * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress + * + * This state is entered when WPA/WPA2 4-Way Handshake is started. In + * case of WPA-PSK, this happens when receiving the first EAPOL-Key + * frame after association. In case of WPA-EAP, this state is entered + * when the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_4WAY_HANDSHAKE, + + /** + * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress + * + * This state is entered when 4-Way Key Handshake has been completed + * (i.e., when the supplicant sends out message 4/4) and when Group + * Key rekeying is started by the AP (i.e., when supplicant receives + * message 1/2). + */ + WPA_GROUP_HANDSHAKE, + + /** + * WPA_COMPLETED - All authentication completed + * + * This state is entered when the full authentication process is + * completed. In case of WPA2, this happens when the 4-Way Handshake is + * successfully completed. With WPA, this state is entered after the + * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is + * completed after dynamic keys are received (or if not used, after + * the EAP authentication has been completed). With static WEP keys and + * plaintext connections, this state is entered when an association + * has been completed. + * + * This state indicates that the supplicant has completed its + * processing for the association phase and that data connection is + * fully configured. + */ + WPA_COMPLETED +} wpa_states; + #endif /* DEFS_H */ diff --git a/contrib/wpa_supplicant/doc/code_structure.doxygen b/contrib/wpa_supplicant/doc/code_structure.doxygen new file mode 100644 index 0000000..6a32e59 --- /dev/null +++ b/contrib/wpa_supplicant/doc/code_structure.doxygen @@ -0,0 +1,264 @@ +/** +\page code_structure Structure of the source code + +[ \ref wpa_supplicant_core "wpa_supplicant core functionality" | +\ref generic_helper_func "Generic helper functions" | +\ref crypto_func "Cryptographic functions" | +\ref configuration "Configuration" | +\ref ctrl_iface "Control interface" | +\ref wpa_code "WPA supplicant" | +\ref eap_peer "EAP peer" | +\ref eapol_supp "EAPOL supplicant" | +\ref win_port "Windows port" | +\ref test_programs "Test programs" ] + +%wpa_supplicant implementation is divided into number of independent +modules. Core code includes functionality for controlling the network +selection, association, and configuration. Independent modules include +WPA code (key handshake, PMKSA caching, pre-authentication), EAPOL +state machine, and EAP state machine and methods. In addition, there +are number of separate files for generic helper functions. + +Both WPA and EAPOL/EAP state machines can be used separately in other +programs than %wpa_supplicant. As an example, the included test +programs eapol_test and preauth_test are using these modules. + +\ref driver_wrapper "Driver interface API" is defined in driver.h and +all hardware/driver dependent functionality is implemented in +driver_*.c. + + +\section wpa_supplicant_core wpa_supplicant core functionality + +wpa_supplicant.c + Program initialization, main control loop + +main.c + main() for UNIX-like operating systems and MinGW (Windows); this + uses command line arguments to configure wpa_supplicant + +events.c + Driver event processing; wpa_supplicant_event() and related functions + +wpa_supplicant_i.h + Internal definitions for %wpa_supplicant core; should not be + included into independent modules + +wpa_supplicant.h + Definitions for driver event data and message logging + + +\section generic_helper_func Generic helper functions + +%wpa_supplicant uses generic helper functions some of which are shared +with with hostapd. The following C files are currently used: + +eloop.c and eloop.h + Event loop (select() loop with registerable timeouts, socket read + callbacks, and signal callbacks) + +common.c and common.h + Common helper functions + +defs.h + Definitions shared by multiple files + +l2_packet.h, l2_packet_linux.c, and l2_packet_pcap.c + Layer 2 (link) access wrapper (includes native Linux implementation + and wrappers for libdnet/libpcap). A new l2_packet implementation + may need to be added when porting to new operating systems that are + not supported by libdnet/libpcap. Makefile can be used to select which + l2_packet implementation is included. l2_packet_linux.c uses Linux + packet sockets and l2_packet_pcap.c has a more portable version using + libpcap and libdnet. + +pcsc_funcs.c and pcsc_funcs.h + Wrapper for PC/SC lite SIM and smart card readers + +priv_netlink.h + Private version of netlink definitions from Linux kernel header files; + this could be replaced with C library header file once suitable + version becomes commonly available + +version.h + Version number definitions + +wireless_copy.h + Private version of Linux wireless extensions definitions from kernel + header files; this could be replaced with C library header file once + suitable version becomes commonly available + + +\section crypto_func Cryptographic functions + +md5.c and md5.h + MD5 (replaced with a crypto library if TLS support is included) + HMAC-MD5 (keyed checksum for message authenticity validation) + +rc4.c and rc4.h + RC4 (broadcast/default key encryption) + +sha1.c and sha1.h + SHA-1 (replaced with a crypto library if TLS support is included) + HMAC-SHA-1 (keyed checksum for message authenticity validation) + PRF-SHA-1 (pseudorandom (key/nonce generation) function) + PBKDF2-SHA-1 (ASCII passphrase to shared secret) + T-PRF (for EAP-FAST) + TLS-PRF (RFC 2246) + +aes_wrap.c, aes_wrap.h, aes.c + AES (replaced with a crypto library if TLS support is included), + AES Key Wrap Algorithm with 128-bit KEK, RFC3394 (broadcast/default + key encryption), + One-Key CBC MAC (OMAC1) hash with AES-128, + AES-128 CTR mode encryption, + AES-128 EAX mode encryption/decryption, + AES-128 CBC + +crypto.h + Definition of crypto library wrapper + +crypto.c + Wrapper functions for libcrypto (OpenSSL) + +crypto_gnutls.c + Wrapper functions for libgcrypt (used by GnuTLS) + +ms_funcs.c and ms_funcs.h + Helper functions for MSCHAPV2 and LEAP + +tls.h + Definition of TLS library wrapper + +tls_none.c + Dummy implementation of TLS library wrapper for cases where TLS + functionality is not included. + +tls_openssl.c + TLS library wrapper for openssl + +tls_gnutls.c + TLS library wrapper for GnuTLS + + +\section configuration Configuration + +config_ssid.h + Definition of per network configuration items + +config.h + Definition of the %wpa_supplicant configuration + +config.c + Configuration parser and common functions + +config_file.c + Configuration backend for text files (e.g., wpa_supplicant.conf) + + +\section ctrl_iface Control interface + +%wpa_supplicant has a \ref ctrl_iface_page "control interface" +that can be used to get status +information and manage operations from external programs. An example +command line interface (wpa_cli) and GUI (wpa_gui) for this interface +are included in the %wpa_supplicant distribution. + +ctrl_iface.c and ctrl_iface.h + %wpa_supplicant-side of the control interface + +wpa_ctrl.c and wpa_ctrl.h + Library functions for external programs to provide access to the + %wpa_supplicant control interface + +wpa_cli.c + Example program for using %wpa_supplicant control interface + + +\section wpa_code WPA supplicant + +wpa.c and wpa.h + WPA state machine and 4-Way/Group Key Handshake processing + +preauth.c and preauth.h + PMKSA caching and pre-authentication (RSN/WPA2) + +wpa_i.h + Internal definitions for WPA code; not to be included to other modules. + +\section eap_peer EAP peer + +\ref eap_module "EAP peer implementation" is a separate module that +can be used by other programs than just %wpa_supplicant. + +eap.c and eap.h + EAP state machine and method interface + +eap_defs.h + Common EAP definitions + +eap_i.h + Internal definitions for EAP state machine and EAP methods; not to be + included in other modules + +eap_sim_common.c and eap_sim_common.h + Common code for EAP-SIM and EAP-AKA + +eap_tls_common.c and eap_tls_common.h + Common code for EAP-PEAP, EAP-TTLS, and EAP-FAST + +eap_tlv.c and eap_tlv.h + EAP-TLV code for EAP-PEAP and EAP-FAST + +eap_ttls.c and eap_ttls.h + EAP-TTLS + +eap_pax.c, eap_pax_common.h, eap_pax_common.c + EAP-PAX + +eap_psk.c, eap_psk_common.h, eap_psk_common.c + EAP-PSK (note: this is not needed for WPA-PSK) + +eap_aka.c, eap_fast.c, eap_gtc.c, eap_leap.c, eap_md5.c, eap_mschapv2.c, +eap_otp.c, eap_peap.c, eap_sim.c, eap_tls.c + Other EAP method implementations + + +\section eapol_supp EAPOL supplicant + +eapol_sm.c and eapol_sm.h + EAPOL supplicant state machine and IEEE 802.1X processing + + +\section win_port Windows port + +ndis_events.cpp + External program for receiving NdisMIndicateStatus() events and + delivering them to %wpa_supplicant in more easier to use form + +win_if_list.c + External program for listing current network interface + + +\section test_programs Test programs + +radius_client.c and radius_client.h + RADIUS authentication client implementation for eapol_test + +radius.c and radius.h + RADIUS message processing for eapol_test + +config_types.h and hostapd.h + Minimal version of hostapd header files for eapol_test + +eapol_test.c + Standalone EAP testing tool with integrated RADIUS authentication + client + +preauth_test.c + Standalone RSN pre-authentication tool + +wpa_passphrase.c + WPA ASCII passphrase to PSK conversion + +*/ diff --git a/contrib/wpa_supplicant/doc/ctrl_iface.doxygen b/contrib/wpa_supplicant/doc/ctrl_iface.doxygen new file mode 100644 index 0000000..fd1b9c4 --- /dev/null +++ b/contrib/wpa_supplicant/doc/ctrl_iface.doxygen @@ -0,0 +1,409 @@ +/** +\page ctrl_iface_page Control interface + +%wpa_supplicant implements a control interface that can be used by +external programs to control the operations of the %wpa_supplicant +daemon and to get status information and event notifications. There is +a small C library, in a form of a single C file, wpa_ctrl.c, that +provides helper functions to facilitate the use of the control +interface. External programs can link this file into them and then use +the library functions documented in wpa_ctrl.h to interact with +%wpa_supplicant. This library can also be used with C++. wpa_cli.c and +wpa_gui are example programs using this library. + +There are multiple mechanisms for inter-process communication. For +example, Linux version of %wpa_supplicant is using UNIX domain sockets +for the control interface and Windows version UDP sockets. The use of +the functions defined in wpa_ctrl.h can be used to hide the details of +the used IPC from external programs. + + +\section using_ctrl_iface Using the control interface + +External programs, e.g., a GUI or a configuration utility, that need to +communicate with %wpa_supplicant should link in wpa_ctrl.c. This +allows them to use helper functions to open connection to the control +interface with wpa_ctrl_open() and to send commands with +wpa_ctrl_request(). + +%wpa_supplicant uses the control interface for two types of communication: +commands and unsolicited event messages. Commands are a pair of +messages, a request from the external program and a response from +%wpa_supplicant. These can be executed using wpa_ctrl_request(). +Unsolicited event messages are sent by %wpa_supplicant to the control +interface connection without specific request from the external program +for receiving each message. However, the external program needs to +attach to the control interface with wpa_ctrl_attach() to receive these +unsolicited messages. + +If the control interface connection is used both for commands and +unsolicited event messages, there is potential for receiving an +unsolicited message between the command request and response. +wpa_ctrl_request() caller will need to supply a callback, msg_cb, +for processing these messages. Often it is easier to open two +control interface connections by calling wpa_ctrl_open() twice and +then use one of the connections for commands and the other one for +unsolicited messages. This way command request/response pairs will +not be broken by unsolicited messages. wpa_cli is an example of how +to use only one connection for both purposes and wpa_gui demonstrates +how to use two separate connections. + +Once the control interface connection is not needed anymore, it should +be closed by calling wpa_ctrl_close(). If the connection was used for +unsolicited event messages, it should be first detached by calling +wpa_ctrl_detach(). + + +\section ctrl_iface_cmds Control interface commands + +Following commands can be used with wpa_ctrl_request(): + +\subsection ctrl_iface_PING PING + +This command can be used to test whether %wpa_supplicant is replying +to the control interface commands. The expected reply is \c PONG if the +connection is open and %wpa_supplicant is processing commands. + + +\subsection ctrl_iface_MIB MIB + +Request a list of MIB variables (dot1x, dot11). The output is a text +block with each line in \c variable=value format. For example: + +\verbatim +dot11RSNAOptionImplemented=TRUE +dot11RSNAPreauthenticationImplemented=TRUE +dot11RSNAEnabled=FALSE +dot11RSNAPreauthenticationEnabled=FALSE +dot11RSNAConfigVersion=1 +dot11RSNAConfigPairwiseKeysSupported=5 +dot11RSNAConfigGroupCipherSize=128 +dot11RSNAConfigPMKLifetime=43200 +dot11RSNAConfigPMKReauthThreshold=70 +dot11RSNAConfigNumberOfPTKSAReplayCounters=1 +dot11RSNAConfigSATimeout=60 +dot11RSNAAuthenticationSuiteSelected=00-50-f2-2 +dot11RSNAPairwiseCipherSelected=00-50-f2-4 +dot11RSNAGroupCipherSelected=00-50-f2-4 +dot11RSNAPMKIDUsed= +dot11RSNAAuthenticationSuiteRequested=00-50-f2-2 +dot11RSNAPairwiseCipherRequested=00-50-f2-4 +dot11RSNAGroupCipherRequested=00-50-f2-4 +dot11RSNAConfigNumberOfGTKSAReplayCounters=0 +dot11RSNA4WayHandshakeFailures=0 +dot1xSuppPaeState=5 +dot1xSuppHeldPeriod=60 +dot1xSuppAuthPeriod=30 +dot1xSuppStartPeriod=30 +dot1xSuppMaxStart=3 +dot1xSuppSuppControlledPortStatus=Authorized +dot1xSuppBackendPaeState=2 +dot1xSuppEapolFramesRx=0 +dot1xSuppEapolFramesTx=440 +dot1xSuppEapolStartFramesTx=2 +dot1xSuppEapolLogoffFramesTx=0 +dot1xSuppEapolRespFramesTx=0 +dot1xSuppEapolReqIdFramesRx=0 +dot1xSuppEapolReqFramesRx=0 +dot1xSuppInvalidEapolFramesRx=0 +dot1xSuppEapLengthErrorFramesRx=0 +dot1xSuppLastEapolFrameVersion=0 +dot1xSuppLastEapolFrameSource=00:00:00:00:00:00 +\endverbatim + + +\subsection ctrl_iface_STATUS STATUS + +Request current WPA/EAPOL/EAP status information. The output is a text +block with each line in \c variable=value format. For example: + +\verbatim +bssid=02:00:01:02:03:04 +ssid=test network +pairwise_cipher=CCMP +group_cipher=CCMP +key_mgmt=WPA-PSK +wpa_state=COMPLETED +ip_address=192.168.1.21 +Supplicant PAE state=AUTHENTICATED +suppPortStatus=Authorized +EAP state=SUCCESS +\endverbatim + + +\subsection ctrl_iface_STATUS-VERBOSE STATUS-VERBOSE + +Same as STATUS, but with more verbosity (i.e., more \c variable=value pairs). + +\verbatim +bssid=02:00:01:02:03:04 +ssid=test network +pairwise_cipher=CCMP +group_cipher=CCMP +key_mgmt=WPA-PSK +wpa_state=COMPLETED +ip_address=192.168.1.21 +Supplicant PAE state=AUTHENTICATED +suppPortStatus=Authorized +heldPeriod=60 +authPeriod=30 +startPeriod=30 +maxStart=3 +portControl=Auto +Supplicant Backend state=IDLE +EAP state=SUCCESS +reqMethod=0 +methodState=NONE +decision=COND_SUCC +ClientTimeout=60 +\endverbatim + + +\subsection ctrl_iface_PMKSA PMKSA + +Show PMKSA cache + +\verbatim +Index / AA / PMKID / expiration (in seconds) / opportunistic +1 / 02:00:01:02:03:04 / 000102030405060708090a0b0c0d0e0f / 41362 / 0 +2 / 02:00:01:33:55:77 / 928389281928383b34afb34ba4212345 / 362 / 1 +\endverbatim + + +\subsection ctrl_iface_SET SET <variable> <value> + +Set variables: +- EAPOL::heldPeriod +- EAPOL::authPeriod +- EAPOL::startPeriod +- EAPOL::maxStart +- dot11RSNAConfigPMKLifetime +- dot11RSNAConfigPMKReauthThreshold +- dot11RSNAConfigSATimeout + +Example command: +\verbatim +SET EAPOL::heldPeriod 45 +\endverbatim + + +\subsection ctrl_iface_LOGON LOGON + +IEEE 802.1X EAPOL state machine logon. + + +\subsection ctrl_iface_LOGOFF LOGOFF + +IEEE 802.1X EAPOL state machine logoff. + + +\subsection ctrl_iface_REASSOCIATE REASSOCIATE + +Force reassociation. + + +\subsection ctrl_iface_PREAUTH PREAUTH <BSSID> + +Start pre-authentication with the given BSSID. + + +\subsection ctrl_iface_ATTACH ATTACH + +Attach the connection as a monitor for unsolicited events. This can +be done with wpa_ctrl_attach(). + + +\subsection ctrl_iface_DETACH DETACH + +Detach the connection as a monitor for unsolicited events. This can +be done with wpa_ctrl_detach(). + + +\subsection ctrl_iface_LEVEL LEVEL <debug level> + +Change debug level. + + +\subsection ctrl_iface_RECONFIGURE RECONFIGURE + +Force %wpa_supplicant to re-read its configuration data. + + +\subsection ctrl_iface_TERMINATE TERMINATE + +Terminate %wpa_supplicant process. + + +\subsection ctrl_iface_BSSID BSSID <network id> <BSSID> + +Set preferred BSSID for a network. Network id can be received from the +\c LIST_NETWORKS command output. + + +\subsection ctrl_iface_LIST_NETWORKS LIST_NETWORKS + +List configured networks. + +\verbatim +network id / ssid / bssid / flags +0 example network any [CURRENT] +\endverbatim + +(note: fields are separated with tabs) + + +\subsection ctrl_iface_DISCONNECT DISCONNECT + +Disconnect and wait for \c REASSOCIATE command before connecting. + + +\subsection ctrl_iface_SCAN SCAN + +Request a new BSS scan. + + +\subsection ctrl_iface_SCAN_RESULTS SCAN_RESULTS + +Get the latest scan results. + +\verbatim +bssid / frequency / signal level / flags / ssid +00:09:5b:95:e0:4e 2412 208 [WPA-PSK-CCMP] jkm private +02:55:24:33:77:a3 2462 187 [WPA-PSK-TKIP] testing +00:09:5b:95:e0:4f 2412 209 jkm guest +\endverbatim + +(note: fields are separated with tabs) + + +\subsection ctrl_iface_SELECT_NETWORK SELECT_NETWORK <network id> + +Select a network (disable others). Network id can be received from the +\c LIST_NETWORKS command output. + + +\subsection ctrl_iface_ENABLE_NETWORK ENABLE_NETWORK <network id> + +Enable a network. Network id can be received from the +\c LIST_NETWORKS command output. + + +\subsection ctrl_iface_DISABLE_NETWORK DISABLE_NETWORK <network id> + +Disable a network. Network id can be received from the +\c LIST_NETWORKS command output. + + +\subsection ctrl_iface_ADD_NETWORK ADD_NETWORK + +Add a new network. This command creates a new network with empty +configuration. The new network is disabled and once it has been +configured it can be enabled with \c ENABLE_NETWORK command. \c ADD_NETWORK +returns the network id of the new network or FAIL on failure. + + +\subsection ctrl_iface_REMOVE_NETWORK REMOVE_NETWORK <network id> + +Remove a network. Network id can be received from the +\c LIST_NETWORKS command output. + + +\subsection ctrl_iface_SET_NETWORK SET_NETWORK <network id> <variable> <value> + +Set network variables. Network id can be received from the +\c LIST_NETWORKS command output. + +This command uses the same variables and data formats as the +configuration file. See example wpa_supplicant.conf for more details. + +- ssid (network name, SSID) +- psk (WPA passphrase or pre-shared key) +- key_mgmt (key management protocol) +- identity (EAP identity) +- password (EAP password) +- ... + + +\subsection ctrl_iface_GET_NETWORK GET_NETWORK <network id> <variable> + +Get network variables. Network id can be received from the +\c LIST_NETWORKS command output. + + +\subsection ctrl_iface_SAVE_CONFIG SAVE_CONFIG + +Save the current configuration. + + +\section ctrl_iface_interactive Interactive requests + +If %wpa_supplicant needs additional information during authentication +(e.g., password), it will use a specific prefix, \c CTRL-REQ- +(\a WPA_CTRL_REQ macro) in an unsolicited event message. An external +program, e.g., a GUI, can provide such information by using +\c CTRL-RSP- (\a WPA_CTRL_RSP macro) prefix in a command with matching +field name. + +The following fields can be requested in this way from the user: +- IDENTITY (EAP identity/user name) +- PASSWORD (EAP password) +- NEW_PASSWORD (New password if the server is requesting password change) +- PIN (PIN code for accessing a SIM or smartcard) +- OTP (one-time password; like password, but the value is used only once) +- PASSPHRASE (passphrase for a private key file) + +\verbatim +CTRL-REQ-<field name>-<network id>-<human readable text> +CTRL-RSP-<field name>-<network id>-<value> +\endverbatim + +For example, request from %wpa_supplicant: +\verbatim +CTRL-REQ-PASSWORD-1-Password needed for SSID test-network +\endverbatim + +And a matching reply from the GUI: +\verbatim +CTRL-RSP-PASSWORD-1-secret +\endverbatim + + +\subsection ctrl_iface_GET_CAPABILITY GET_CAPABILITY <option> + +Get list of supported functionality (eap, pairwise, group, +proto). Supported functionality is shown as space separate lists of +values used in the same format as in %wpa_supplicant configuration. + +Example request/reply pairs: + +\verbatim +GET_CAPABILITY eap +AKA FAST GTC LEAP MD5 MSCHAPV2 OTP PAX PEAP PSK SIM TLS TTLS +\endverbatim + +\verbatim +GET_CAPABILITY pairwise +CCMP TKIP NONE +\endverbatim + +\verbatim +GET_CAPABILITY group +CCMP TKIP WEP104 WEP40 +\endverbatim + +\verbatim +GET_CAPABILITY key_mgmt +WPA-PSK WPA-EAP IEEE8021X NONE +\endverbatim + +\verbatim +GET_CAPABILITY proto +RSN WPA +\endverbatim + +\verbatim +GET_CAPABILITY auth_alg +OPEN SHARED LEAP +\endverbatim + +*/ diff --git a/contrib/wpa_supplicant/doc/docbook/Makefile b/contrib/wpa_supplicant/doc/docbook/Makefile new file mode 100644 index 0000000..15c019c --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/Makefile @@ -0,0 +1,25 @@ +all: man html pdf + +FILES += wpa_background +FILES += wpa_cli +FILES += wpa_passphrase +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_passphrase.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/contrib/wpa_supplicant/doc/docbook/wpa_background.8 b/contrib/wpa_supplicant/doc/docbook/wpa_background.8 new file mode 100644 index 0000000..3a7be28 --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_background.8 @@ -0,0 +1,84 @@ +.\" This manpage has been automatically generated by docbook2man +.\" from a DocBook document. This tool can be found at: +.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng <steve@ggi-project.org>. +.TH "WPA_BACKGROUND" "8" "08 February 2006" "" "" + +.SH NAME +wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i +.SH "WPA" +.PP +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. +.PP +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). +.PP +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. +.PP +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). +.PP +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). +.PP +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). +.SH "IEEE 802.11I / WPA2" +.PP +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). +.SH "SEE ALSO" +.PP +\fBwpa_supplicant\fR(8) +.SH "LEGAL" +.PP +wpa_supplicant is copyright (c) 2003-2005, +Jouni Malinen <jkmaline@cc.hut.fi> and +contributors. +All Rights Reserved. +.PP +This program is dual-licensed under both the GPL version 2 +and BSD license. Either license may be used at your option. diff --git a/contrib/wpa_supplicant/doc/docbook/wpa_background.sgml b/contrib/wpa_supplicant/doc/docbook/wpa_background.sgml new file mode 100644 index 0000000..dc64360 --- /dev/null +++ b/contrib/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-2005, + Jouni Malinen <email>jkmaline@cc.hut.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/contrib/wpa_supplicant/doc/docbook/wpa_cli.8 b/contrib/wpa_supplicant/doc/docbook/wpa_cli.8 new file mode 100644 index 0000000..ad039b5 --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_cli.8 @@ -0,0 +1,202 @@ +.\" This manpage has been automatically generated by docbook2man +.\" from a DocBook document. This tool can be found at: +.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng <steve@ggi-project.org>. +.TH "WPA_CLI" "8" "08 February 2006" "" "" + +.SH NAME +wpa_cli \- WPA command line client +.SH SYNOPSIS + +\fBwpa_cli\fR [ \fB-p \fIpath to ctrl sockets\fB\fR ] [ \fB-i \fIifname\fB\fR ] [ \fB-hvB\fR ] [ \fB-a \fIaction file\fB\fR ] [ \fB-P \fIpid file\fB\fR ] [ \fB\fIcommand ...\fB\fR ] + +.SH "OVERVIEW" +.PP +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. +.PP +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. +.PP +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. +.PP +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). +.PP +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. +.SH "INTERACTIVE AUTHENTICATION PARAMETERS REQUEST" +.PP +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. +.PP +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. +.PP +Example request for password and a matching reply: +.sp +.RS + +.nf +CTRL-REQ-PASSWORD-1:Password needed for SSID foobar +> password 1 mysecretpassword +.fi +.RE +.PP +Example request for generic token card challenge-response: +.sp +.RS + +.nf +CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar +> otp 2 9876 +.fi +.RE +.SH "COMMAND ARGUMENTS" +.TP +\fB-p path\fR +Change the path where control sockets should +be found. +.TP +\fB-i ifname\fR +Specify the interface that is being +configured. By default, choose the first interface found with +a control socket in the socket path. +.TP +\fB-h\fR +Help. Show a usage message. +.TP +\fB-v\fR +Show version information. +.TP +\fB-B\fR +Run as a daemon in the background. +.TP +\fB-a file\fR +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 "CONNECT" or "DISCONNECT" depending on the event. +This can be used +.TP +\fB-P file\fR +Set the location of the PID +file. +.TP +\fBcommand\fR +Run a command. The available commands are +listed in the next section. +.SH "COMMANDS" +.PP +The following commands are available: +.TP +\fBstatus\fR +get current WPA/EAPOL/EAP status +.TP +\fBmib\fR +get MIB variables (dot1x, dot11) +.TP +\fBhelp\fR +show this usage help +.TP +\fBinterface [ifname]\fR +show interfaces/select interface +.TP +\fBlevel <debug level>\fR +change debug level +.TP +\fBlicense\fR +show full wpa_cli license +.TP +\fBlogoff\fR +IEEE 802.1X EAPOL state machine logoff +.TP +\fBlogon\fR +IEEE 802.1X EAPOL state machine logon +.TP +\fBset\fR +set variables (shows list of variables when run without arguments) +.TP +\fBpmksa\fR +show PMKSA cache +.TP +\fBreassociate\fR +force reassociation +.TP +\fBreconfigure\fR +force wpa_supplicant to re-read its configuration file +.TP +\fBpreauthenticate <BSSID>\fR +force preauthentication +.TP +\fBidentity <network id> <identity>\fR +configure identity for an SSID +.TP +\fBpassword <network id> <password>\fR +configure password for an SSID +.TP +\fBpin <network id> <pin>\fR +configure pin for an SSID +.TP +\fBotp <network id> <password>\fR +configure one-time-password for an SSID +.TP +\fBbssid *lt;network id> <BSSID>\fR +set preferred BSSID for an SSID +.TP +\fBlist_networks\fR +list configured networks +.TP +\fBterminate\fR +terminate \fBwpa_supplicant\fR +.TP +\fBquit\fR +exit wpa_cli +.SH "SEE ALSO" +.PP +\fBwpa_supplicant\fR(8) +.SH "LEGAL" +.PP +wpa_supplicant is copyright (c) 2003-2005, +Jouni Malinen <jkmaline@cc.hut.fi> and +contributors. +All Rights Reserved. +.PP +This program is dual-licensed under both the GPL version 2 +and BSD license. Either license may be used at your option. diff --git a/contrib/wpa_supplicant/doc/docbook/wpa_cli.sgml b/contrib/wpa_supplicant/doc/docbook/wpa_cli.sgml new file mode 100644 index 0000000..e804239 --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_cli.sgml @@ -0,0 +1,330 @@ +<!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 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 '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.</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 "CONNECT" or "DISCONNECT" depending on the event. + This can be used </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 *lt;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-2005, + Jouni Malinen <email>jkmaline@cc.hut.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/contrib/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/contrib/wpa_supplicant/doc/docbook/wpa_passphrase.8 new file mode 100644 index 0000000..11e0f85 --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_passphrase.8 @@ -0,0 +1,39 @@ +.\" This manpage has been automatically generated by docbook2man +.\" from a DocBook document. This tool can be found at: +.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng <steve@ggi-project.org>. +.TH "WPA_PASSPHRASE" "8" "08 February 2006" "" "" + +.SH NAME +wpa_passphrase \- Set WPA passphrase for a SSID +.SH SYNOPSIS + +\fBwpa_passphrase\fR [ \fB\fIssid\fB\fR ] [ \fB\fIpassphrase\fB\fR ] + +.SH "OVERVIEW" +.PP +\fBwpa_passphrase\fR pre-computes PSK entries for +network configuration blocks of a +\fIwpa_supplicant.conf\fR file. +.SH "OPTIONS" +.TP +\fBssid\fR +The SSID whose passphrase should be derived. +.TP +\fBpassphrase\fR +The passphrase to use. If not included on the command line, +passphrase will be read from standard input. +.SH "SEE ALSO" +.PP +\fBwpa_supplicant.conf\fR(5) +\fBwpa_supplicant\fR(8) +.SH "LEGAL" +.PP +wpa_supplicant is copyright (c) 2003-2005, +Jouni Malinen <jkmaline@cc.hut.fi> and +contributors. +All Rights Reserved. +.PP +This program is dual-licensed under both the GPL version 2 +and BSD license. Either license may be used at your option. diff --git a/contrib/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/contrib/wpa_supplicant/doc/docbook/wpa_passphrase.sgml new file mode 100644 index 0000000..1380a74 --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_passphrase.sgml @@ -0,0 +1,72 @@ +<!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>Set WPA 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.</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-2005, + Jouni Malinen <email>jkmaline@cc.hut.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/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.8 new file mode 100644 index 0000000..fcd738b --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.8 @@ -0,0 +1,544 @@ +.\" This manpage has been automatically generated by docbook2man +.\" from a DocBook document. This tool can be found at: +.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng <steve@ggi-project.org>. +.TH "WPA_SUPPLICANT" "8" "08 February 2006" "" "" + +.SH NAME +wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant +.SH SYNOPSIS + +\fBwpa_supplicant\fR [ \fB-BddehLqqvw\fR ] [ \fB-i\fIifname\fB\fR ] [ \fB-c\fIconfig file\fB\fR ] [ \fB-D\fIdriver\fB\fR ] + +.SH "OVERVIEW" +.PP +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. +.PP +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. +.PP +\fBwpa_supplicant\fR 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. +.PP +\fBwpa_supplicant\fR is designed to be a +"daemon" program that runs in the background and acts as the +backend component controlling the wireless +connection. \fBwpa_supplicant\fR supports separate +frontend programs and an example text-based frontend, +\fBwpa_cli\fR, is included with +wpa_supplicant. +.PP +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 have be +loaded. Note, however, that the '-w' option of the wpa_supplicant +daemon instructs the daemon to continue running and to wait for +the interface to become available. Without the '-w' option, the +daemon will exit immediately if the device is not already +available. +.PP +After \fBwpa_supplicant\fR 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. +.PP +The following steps are used when associating with an AP +using WPA: +.TP 0.2i +\(bu +\fBwpa_supplicant\fR requests the kernel +driver to scan neighboring BSSes +.TP 0.2i +\(bu +\fBwpa_supplicant\fR selects a BSS based on +its configuration +.TP 0.2i +\(bu +\fBwpa_supplicant\fR requests the kernel +driver to associate with the chosen BSS +.TP 0.2i +\(bu +If WPA-EAP: integrated IEEE 802.1X Supplicant or +external Xsupplicant completes EAP authentication with the +authentication server (proxied by the Authenticator in the +AP) +.TP 0.2i +\(bu +If WPA-EAP: master key is received from the IEEE 802.1X +Supplicant +.TP 0.2i +\(bu +If WPA-PSK: \fBwpa_supplicant\fR uses PSK +as the master session key +.TP 0.2i +\(bu +\fBwpa_supplicant\fR completes WPA 4-Way +Handshake and Group Key Handshake with the Authenticator +(AP) +.TP 0.2i +\(bu +\fBwpa_supplicant\fR configures encryption +keys for unicast and broadcast +.TP 0.2i +\(bu +normal data packets can be transmitted and received +.SH "SUPPORTED FEATURES" +.PP +Supported WPA/IEEE 802.11i features: +.TP 0.2i +\(bu +WPA-PSK ("WPA-Personal") +.TP 0.2i +\(bu +WPA with EAP (e.g., with RADIUS authentication server) +("WPA-Enterprise") Following authentication methods are +supported with an integrate IEEE 802.1X Supplicant: +.RS +.TP 0.2i +\(bu +EAP-TLS +.RE +.RS +.TP 0.2i +\(bu +EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1) +.TP 0.2i +\(bu +EAP-PEAP/TLS (both PEAPv0 and PEAPv1) +.TP 0.2i +\(bu +EAP-PEAP/GTC (both PEAPv0 and PEAPv1) +.TP 0.2i +\(bu +EAP-PEAP/OTP (both PEAPv0 and PEAPv1) +.TP 0.2i +\(bu +EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1) +.TP 0.2i +\(bu +EAP-TTLS/EAP-MD5-Challenge +.TP 0.2i +\(bu +EAP-TTLS/EAP-GTC +.TP 0.2i +\(bu +EAP-TTLS/EAP-OTP +.TP 0.2i +\(bu +EAP-TTLS/EAP-MSCHAPv2 +.TP 0.2i +\(bu +EAP-TTLS/EAP-TLS +.TP 0.2i +\(bu +EAP-TTLS/MSCHAPv2 +.TP 0.2i +\(bu +EAP-TTLS/MSCHAP +.TP 0.2i +\(bu +EAP-TTLS/PAP +.TP 0.2i +\(bu +EAP-TTLS/CHAP +.TP 0.2i +\(bu +EAP-SIM +.TP 0.2i +\(bu +EAP-AKA +.TP 0.2i +\(bu +EAP-PSK +.TP 0.2i +\(bu +EAP-PAX +.TP 0.2i +\(bu +LEAP (note: requires special support from +the driver for IEEE 802.11 authentication) +.TP 0.2i +\(bu +(following methods are supported, but since +they do not generate keying material, they cannot be used +with WPA or IEEE 802.1X WEP keying) +.TP 0.2i +\(bu +EAP-MD5-Challenge +.TP 0.2i +\(bu +EAP-MSCHAPv2 +.TP 0.2i +\(bu +EAP-GTC +.TP 0.2i +\(bu +EAP-OTP +.RE +.TP 0.2i +\(bu +key management for CCMP, TKIP, WEP104, WEP40 +.TP 0.2i +\(bu +RSN/WPA2 (IEEE 802.11i) +.RS +.TP 0.2i +\(bu +pre-authentication +.TP 0.2i +\(bu +PMKSA caching +.RE +.SH "AVAILABLE DRIVERS" +.PP +The available drivers to specify with the -D option are: +.TP +\fBhostap\fR +(default) Host AP driver (Intersil Prism2/2.5/3). +(this can also be used with Linuxant DriverLoader). +.TP +\fBhermes\fR +Agere Systems Inc. driver (Hermes-I/Hermes-II). +.TP +\fBmadwifi\fR +MADWIFI 802.11 support (Atheros, etc.). +.TP +\fBatmel\fR +ATMEL AT76C5XXx (USB, PCMCIA). +.TP +\fBwext\fR +Linux wireless extensions (generic). +.TP +\fBndiswrapper\fR +Linux ndiswrapper. +.TP +\fBbroadcom\fR +Broadcom wl.o driver. +.TP +\fBipw\fR +Intel ipw2100/2200 driver. +.TP +\fBwired\fR +wpa_supplicant wired Ethernet driver +.TP +\fBbsd\fR +BSD 802.11 support (Atheros, etc.). +.TP +\fBndis\fR +Windows NDIS driver. +.SH "COMMAND LINE OPTIONS" +.TP +\fB-B\fR +Run daemon in the background. +.TP +\fB-i ifname\fR +Interface to listen on. +.TP +\fB-c filename\fR +Path to configuration file. +.TP +\fB-D driver\fR +Driver to use. See the available options below. +.TP +\fB-d\fR +Increase debugging verbosity (-dd even more). +.TP +\fB-K\fR +Include keys (passwords, etc.) in debug output. +.TP +\fB-t\fR +Include timestamp in debug messages. +.TP +\fB-e\fR +Use external IEEE 802.1X Supplicant (e.g., +\fBxsupplicant\fR) (this disables the internal +Supplicant). +.TP +\fB-h\fR +Help. Show a usage message. +.TP +\fB-L\fR +Show license (GPL and BSD). +.TP +\fB-q\fR +Decrease debugging verbosity (-qq even less). +.TP +\fB-v\fR +Show version. +.TP +\fB-w\fR +wait for interface to be added, if needed. normally, +\fBwpa_supplicant\fR will exit if the interface +is not there yet. +.TP +\fB-N\fR +Start describing new interface. +.SH "EXAMPLES" +.PP +In most common cases, \fBwpa_supplicant\fR is +started with: +.sp +.RS + +.nf +wpa_supplicant -Bw -c/etc/wpa_supplicant.conf -iwlan0 +.fi +.RE +.PP +This makes the process fork into background and wait for the wlan0 +interface if it is not available at startup time. +.PP +The easiest way to debug problems, and to get debug log for +bug reports, is to start \fBwpa_supplicant\fR on +foreground with debugging enabled: +.sp +.RS + +.nf +wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d +.fi +.RE +.PP +\fBwpa_supplicant\fR 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: +.sp +.RS + +.nf +wpa_supplicant \\ + -c wpa1.conf -i wlan0 -D hostap -N \\ + -c wpa2.conf -i ath0 -D madwifi +.fi +.RE +.SH "OS REQUIREMENTS" +.PP +Current hardware/software requirements: +.TP 0.2i +\(bu +Linux kernel 2.4.x or 2.6.x with Linux Wireless +Extensions v15 or newer +.TP 0.2i +\(bu +FreeBSD 6-CURRENT +.TP 0.2i +\(bu +Microsoft Windows with WinPcap (at least WinXP, may work +with other versions) +.SH "SUPPORTED DRIVERS" +.TP +\fBHost AP driver for Prism2/2.5/3 (development snapshot/v0.2.x)\fR +(http://hostap.epitest.fi/) Driver needs 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. +.TP +\fBLinuxant DriverLoader\fR +(http://www.linuxant.com/driverloader/) +with Windows NDIS driver for your wlan card supporting WPA. +.TP +\fBAgere Systems Inc. Linux Driver\fR +(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. +.TP +\fBmadwifi driver for cards based on Atheros chip set (ar521x)\fR +(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). +.TP +\fBATMEL AT76C5XXx driver for USB and PCMCIA cards\fR +(http://atmelwlandriver.sourceforge.net/). +.TP +\fBLinux ndiswrapper\fR +(http://ndiswrapper.sourceforge.net/) with Windows +NDIS driver. +.TP +\fBBroadcom wl.o driver\fR +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). +.TP +\fB Intel ipw2100 driver\fR +(http://sourceforge.net/projects/ipw2100/) +.TP +\fBIntel ipw2200 driver\fR +(http://sourceforge.net/projects/ipw2200/) +.TP +\fBLinux wireless extensions\fR +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. +.TP +\fBWired Ethernet drivers\fR +Use ap_scan=0. +.TP +\fBBSD net80211 layer (e.g., Atheros driver)\fR +At the moment, this is for FreeBSD 6-CURRENT branch. +.TP +\fBWindows NDIS\fR +The current Windows port requires WinPcap +(http://winpcap.polito.it/). See README-Windows.txt for more +information. +.PP +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. +.SH "ARCHITECTURE" +.PP +The +\fBwpa_supplicant\fR system consists of the following +components: +.TP +\fB\fIwpa_supplicant.conf\fB \fR +the configuration file describing all networks that the +user wants the computer to connect to. +.TP +\fBwpa_supplicant\fR +the program that directly interacts with the +network interface. +.TP +\fBwpa_cli\fR +the +client program that provides a high-level interface to the +functionality of the daemon. +.TP +\fBwpa_passphrase\fR +a utility needed to construct +\fIwpa_supplicant.conf\fR files that include +encrypted passwords. +.SH "QUICK START" +.PP +First, make a configuration file, e.g. +\fI/etc/wpa_supplicant.conf\fR, that describes the networks +you are interested in. See \fBwpa_supplicant\fR(5) +for details. +.PP +Once the configuration is ready, you can test whether the +configuration works by running \fBwpa_supplicant\fR +with following command to start it on foreground with debugging +enabled: +.sp +.RS + +.nf +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d + +.fi +.RE +.PP +Assuming everything goes fine, you can start using following +command to start \fBwpa_supplicant\fR on background +without debugging: +.sp +.RS + +.nf +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B + +.fi +.RE +.PP +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. +.SH "INTERFACE TO PCMCIA-CS/CARDMRG" +.PP +For example, following small changes to pcmcia-cs scripts +can be used to enable WPA support: +.PP +Add MODE="Managed" and WPA="y" to the network scheme in +\fI/etc/pcmcia/wireless.opts\fR\&. +.PP +Add the following block to the end of 'start' action handler +in \fI/etc/pcmcia/wireless\fR: +.sp +.RS + +.nf +if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + /usr/local/bin/wpa_supplicant -Bw -c/etc/wpa_supplicant.conf -i$DEVICE +fi + +.fi +.RE +.PP +Add the following block to the end of 'stop' action handler +(may need to be separated from other actions) in +\fI/etc/pcmcia/wireless\fR: +.sp +.RS + +.nf +if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + killall wpa_supplicant +fi + +.fi +.RE +.PP +This will make \fBcardmgr\fR start +\fBwpa_supplicant\fR when the card is plugged +in. \fBwpa_supplicant\fR will wait until the +interface is set up--either when a static IP address is configured +or when DHCP client is started--and will then negotiate keys with +the AP. +.SH "SEE ALSO" +.PP +\fBwpa_background\fR(8) +\fBwpa_supplicant.conf\fR(5) +\fBwpa_cli\fR(8) +\fBwpa_passphrase\fR(8) +.SH "LEGAL" +.PP +wpa_supplicant is copyright (c) 2003-2005, +Jouni Malinen <jkmaline@cc.hut.fi> and +contributors. +All Rights Reserved. +.PP +This program is dual-licensed under both the GPL version 2 +and BSD license. Either license may be used at your option. diff --git a/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 new file mode 100644 index 0000000..d5e5c62 --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 @@ -0,0 +1,230 @@ +.\" This manpage has been automatically generated by docbook2man +.\" from a DocBook document. This tool can be found at: +.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng <steve@ggi-project.org>. +.TH "WPA_SUPPLICANT.CONF" "5" "08 February 2006" "" "" + +.SH NAME +wpa_supplicant.conf \- configuration file for wpa_supplicant +.SH "OVERVIEW" +.PP +\fBwpa_supplicant\fR 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 \fB/usr/share/doc/wpa_supplicant/\fR, for +detailed information about the configuration format and supported +fields. +.PP +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. +.PP +Changes to configuration file can be reloaded be sending +SIGHUP signal to \fBwpa_supplicant\fR ('killall -HUP +wpa_supplicant'). Similarly, reloading can be triggered with +'wpa_cli reconfigure' command. +.PP +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 +prefered), and signal strength. +.SH "QUICK EXAMPLES" +.TP 3 +1. +WPA-Personal (PSK) as home network and WPA-Enterprise with +EAP-TLS as work network. +.sp +.RS + +.nf +# 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" +} +.fi +.RE +.TP 3 +2. +WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that +use old peaplabel (e.g., Funk Odyssey and SBR, Meetinghouse +Aegis, Interlink RAD-Series) +.sp +.RS + +.nf +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" +} +.fi +.RE +.TP 3 +3. +EAP-TTLS/EAP-MD5-Challenge configuration with anonymous +identity for the unencrypted use. Real identity is sent only +within an encrypted TLS tunnel. +.sp +.RS + +.nf +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" +} +.fi +.RE +.TP 3 +4. +IEEE 802.1X (i.e., no WPA) with dynamic WEP keys +(require both unicast and broadcast); use EAP-TLS for +authentication +.sp +.RS + +.nf +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 +} +.fi +.RE +.TP 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. +.sp +.RS + +.nf +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" +} +.fi +.RE +.TP 3 +6. +Authentication for wired Ethernet. This can be used with +'wired' interface (-Dwired on command line). +.sp +.RS + +.nf +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 +} +.fi +.RE +.SH "CERTIFICATES" +.PP +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"). +.PP +wpa_supplicant supports X.509 certificates in PEM and DER +formats. User certificate and private key can be included in the +same file. +.PP +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: +.sp +.RS + +.nf +# 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 +.fi +.RE +.SH "SEE ALSO" +.PP +\fBwpa_supplicant\fR(8) +\fBopenssl\fR(1) diff --git a/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml b/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml new file mode 100644 index 0000000..dbd9e37 --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml @@ -0,0 +1,244 @@ +<!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 + 'wpa_cli reconfigure' 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 betwork based on the order of network blocks in + the configuration file, network security level (WPA/WPA2 is + prefered), 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=/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" +} +</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=/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" +} +</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=/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" +} +</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=/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 +} +</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=/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" +} +</programlisting></blockquote> + </listitem> + + <listitem> + <para>Authentication for wired Ethernet. This can be used with + 'wired' interface (-Dwired on command line).</para> + +<blockquote><programlisting> +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 +} +</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/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.sgml new file mode 100644 index 0000000..cb35abf --- /dev/null +++ b/contrib/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -0,0 +1,761 @@ +<!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>-BddehLqqvw</arg> + <arg>-i<replaceable>ifname</replaceable></arg> + <arg>-c<replaceable>config file</replaceable></arg> + <arg>-D<replaceable>driver</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 have be + loaded. Note, however, that the '-w' option of the wpa_supplicant + daemon instructs the daemon to continue running and to wait for + the interface to become available. Without the '-w' option, 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 or + external Xsupplicant 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>The available drivers to specify with the -D option are:</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>atmel</term> + <listitem> + <para>ATMEL AT76C5XXx (USB, PCMCIA).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>wext</term> + <listitem> + <para>Linux wireless extensions (generic).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>ndiswrapper</term> + <listitem> + <para>Linux ndiswrapper.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>broadcom</term> + <listitem> + <para>Broadcom wl.o driver.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>ipw</term> + <listitem> + <para>Intel ipw2100/2200 driver.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>wired</term> + <listitem> + <para>wpa_supplicant wired Ethernet 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> + <variablelist> + <varlistentry> + <term>-B</term> + <listitem> + <para>Run daemon in the background.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-i ifname</term> + <listitem> + <para>Interface to listen on.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-c filename</term> + <listitem> + <para>Path to configuration file.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-D driver</term> + <listitem> + <para>Driver to use. See the available options below.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-d</term> + <listitem> + <para>Increase debugging verbosity (-dd even more).</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>-e</term> + <listitem> + <para>Use external IEEE 802.1X Supplicant (e.g., + <command>xsupplicant</command>) (this disables the internal + Supplicant).</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>-q</term> + <listitem> + <para>Decrease debugging verbosity (-qq even less).</para> + </listitem> + </varlistentry> + <varlistentry> + <term>-v</term> + <listitem> + <para>Show version.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-w</term> + <listitem> + <para>wait for interface to be added, if needed. normally, + <command>wpa_supplicant</command> will exit if the interface + is not there yet.</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 -Bw -c/etc/wpa_supplicant.conf -iwlan0 +</programlisting></blockquote> + + <para>This makes the process fork into background and wait for the wlan0 + interface if it is not available at startup time.</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><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 ('iwconfig wlan0 mode managed'). 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>ATMEL AT76C5XXx driver for USB and PCMCIA cards</term> + <listitem> + <para> (http://atmelwlandriver.sourceforge.net/).</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</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 'start' 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 -Bw -c/etc/wpa_supplicant.conf -i$DEVICE +fi + </programlisting></blockquote> + + + <para>Add the following block to the end of 'stop' 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. <command>wpa_supplicant</command> will wait until the + interface is set up--either when a static IP address is configured + or when DHCP client is started--and will then negotiate keys with + the AP.</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-2005, + Jouni Malinen <email>jkmaline@cc.hut.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/contrib/wpa_supplicant/doc/doxygen.fast b/contrib/wpa_supplicant/doc/doxygen.fast new file mode 100644 index 0000000..2eb9d27 --- /dev/null +++ b/contrib/wpa_supplicant/doc/doxygen.fast @@ -0,0 +1,243 @@ +# Doxyfile 1.4.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = wpa_supplicant +PROJECT_NUMBER = 0.4.x +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . \ + ../hostapd/aes.c \ + ../hostapd/rc4.c \ + ../hostapd/rc4.h \ + ../hostapd/md5.c \ + ../hostapd/md5.h \ + ../hostapd/sha1.c \ + ../hostapd/sha1.h \ + ../hostapd/common.c \ + ../hostapd/common.h \ + ../hostapd/eloop.c \ + ../hostapd/eloop.h \ + ../hostapd/aes_wrap.c \ + ../hostapd/aes_wrap.h +FILE_PATTERNS = *.c *.h *.doxygen +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = doc +INPUT_FILTER = kerneldoc2doxygen.pl +FILTER_PATTERNS = +FILTER_SOURCE_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = IEEE8021X_EAPOL +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/contrib/wpa_supplicant/doc/doxygen.full b/contrib/wpa_supplicant/doc/doxygen.full new file mode 100644 index 0000000..69cec65 --- /dev/null +++ b/contrib/wpa_supplicant/doc/doxygen.full @@ -0,0 +1,230 @@ +# Doxyfile 1.4.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = wpa_supplicant +PROJECT_NUMBER = 0.4.x +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . +FILE_PATTERNS = *.c *.h *.doxygen +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = doc +INPUT_FILTER = kerneldoc2doxygen.pl +FILTER_PATTERNS = +FILTER_SOURCE_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = IEEE8021X_EAPOL +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = YES diff --git a/contrib/wpa_supplicant/doc/driver_wrapper.doxygen b/contrib/wpa_supplicant/doc/driver_wrapper.doxygen new file mode 100644 index 0000000..232739a --- /dev/null +++ b/contrib/wpa_supplicant/doc/driver_wrapper.doxygen @@ -0,0 +1,180 @@ +/** +\page driver_wrapper Driver wrapper implementation (driver.h, drivers.c) + +All hardware and driver dependent functionality is in separate C files +that implement defined wrapper functions. Other parts +of the %wpa_supplicant are designed to be hardware, driver, and operating +system independent. + +Driver wrappers need to implement whatever calls are used in the +target operating system/driver for controlling wireless LAN +devices. As an example, in case of Linux, these are mostly some glue +code and ioctl() calls and netlink message parsing for Linux Wireless +Extensions (WE). Since features required for WPA were added only recently to +Linux Wireless Extensions (in version 18), some driver specific code is used +in number of driver interface implementations. These driver dependent parts +can be replaced with generic code in driver_wext.c once the target driver +includes full support for WE-18. After that, all Linux drivers, at +least in theory, could use the same driver wrapper code. + +A driver wrapper needs to implement some or all of the functions +defined in driver.h. These functions are registered by filling struct +wpa_driver_ops with function pointers. Hardware independent parts of +%wpa_supplicant will call these functions to control the driver/wlan +card. In addition, support for driver events is required. The event +callback function, wpa_supplicant_event(), and its parameters are +documented in wpa_supplicant.h. In addition, a pointer to the 'struct +wpa_driver_ops' needs to be registered in drivers.c file. + +When porting to other operating systems, the driver wrapper should be +modified to use the native interface of the target OS. It is possible +that some extra requirements for the interface between the driver +wrapper and generic %wpa_supplicant code are discovered during porting +to a new operating system. These will be addressed on case by case +basis by modifying the interface and updating the other driver +wrappers for this. The goal is to avoid changing this interface +without very good reasons in order to limit the number of changes +needed to other wrappers and hardware independent parts of +%wpa_supplicant. When changes are required, recommended way is to +make them in backwards compatible way that allows existing driver +interface implementations to be compiled without any modification. + +Generic Linux Wireless Extensions functions are implemented in +driver_wext.c. All Linux driver wrappers can use these when the kernel +driver supports the generic ioctl()s and wireless events. Driver +specific functions are implemented in separate C files, e.g., +driver_hostap.c. These files need to define struct wpa_driver_ops +entry that will be used in wpa_supplicant.c when calling driver +functions. struct wpa_driver_ops entries are registered in drivers.c. + +In general, it is likely to be useful to first take a look at couple +of driver interface examples before starting on implementing a new +one. driver_hostap.c and driver_wext.c include a complete +implementation for Linux drivers that use %wpa_supplicant-based control +of WPA IE and roaming. driver_ndis.c (with help from driver_ndis_.c) +is an example of a complete interface for Windows NDIS interface for +drivers that generate WPA IE themselves and decide when to roam. These +example implementations include full support for all security modes. + + +\section driver_req Driver requirements for WPA + +WPA introduces new requirements for the device driver. At least some +of these need to be implemented in order to provide enough support for +%wpa_supplicant. + +\subsection driver_tkip_ccmp TKIP/CCMP + +WPA requires that the pairwise cipher suite (encryption algorithm for +unicast data packets) is TKIP or CCMP. These are new encryption +protocols and thus, the driver will need to be modified to support +them. Depending on the used wlan hardware, some parts of these may be +implemented by the hardware/firmware. + +Specification for both TKIP and CCMP is available from IEEE (IEEE +802.11i amendment). Fully functional, hardware independent +implementation of both encryption protocols is also available in Host +AP driver (driver/modules/hostap_{tkip,ccmp}.c). In addition, Linux 2.6 +kernel tree has generic implementations for WEP, TKIP, and CCMP that can +be used in Linux drivers. + +The driver will also need to provide configuration mechanism to allow +user space programs to configure TKIP and CCMP. Linux Wireless Extensions +v18 added support for configuring these algorithms and +individual/non-default keys. If the target kernel does not include WE-18, +private ioctls can be used to provide similar functionality. + +\subsection driver_roaming Roaming control and scanning support + +%wpa_supplicant can optionally control AP selection based on the +information received from Beacon and/or Probe Response frames +(ap_scan=1 mode in configuration). This means that the driver should +support external control for scan process. In case of Linux, use of +new Wireless Extensions scan support (i.e., 'iwlist wlan0 scan') is +recommended. The current driver wrapper (driver_wext.c) uses this for +scan results. + +Scan results must also include the WPA information element. Support for +this was added in WE-18. With older versions, a custom event can be used +to provide the full WPA IE (including element id and length) as a hex +string that is included in the scan results. + +%wpa_supplicant needs to also be able to request the driver to +associate with a specific BSS. Current Host AP driver and matching +driver_hostap.c wrapper uses following sequence for this +request. Similar/identical mechanism should be usable also with other +drivers. + +- set WPA IE for AssocReq with private ioctl +- set SSID with SIOCSIWESSID +- set channel/frequency with SIOCSIWFREQ +- set BSSID with SIOCSIWAP + (this last ioctl will trigger the driver to request association) + +\subsection driver_wpa_ie WPA IE generation + +%wpa_supplicant selects which cipher suites and key management suites +are used. Based on this information, it generates a WPA IE. This is +provided to the driver interface in the associate call. This does not +match with Windows NDIS drivers which generate the WPA IE +themselves. + +%wpa_supplicant allows Windows NDIS-like behavior by providing the +selected cipher and key management suites in the associate call. If +the driver generates its own WPA IE and that differs from the one +generated by %wpa_supplicant, the driver has to inform %wpa_supplicant +about the used WPA IE (i.e., the one it used in (Re)Associate +Request). This notification is done using EVENT_ASSOCINFO event (see +wpa_supplicant.h). %wpa_supplicant is normally configured to use +ap_scan=2 mode with drivers that control WPA IE generation and roaming. + +\subsection driver_events Driver events + +%wpa_supplicant needs to receive event callbacks when certain events +occur (association, disassociation, Michael MIC failure, scan results +available, PMKSA caching candidate). These events and the callback +details are defined in wpa_supplicant.h (wpa_supplicant_event() function +and enum wpa_event_type). + +On Linux, association and disassociation can use existing Wireless +Extensions event that is reporting new AP with SIOCGIWAP +event. Similarly, completion of a scan can be reported with SIOCGIWSCAN +event. + +Michael MIC failure event was added in WE-18. Older versions of Wireless +Extensions will need to use a custom event. Host AP driver used a custom +event with following contents: MLME-MICHAELMICFAILURE.indication(keyid=# +broadcast/unicast addr=addr2). This is the recommended format until +the driver can be moved to use WE-18 mechanism. + +\subsection driver_wext_summary Summary of Linux Wireless Extensions use + +AP selection depends on ap_scan configuration: + +ap_scan=1: + +- %wpa_supplicant requests scan with SIOCSIWSCAN +- driver reports scan complete with wireless event SIOCGIWSCAN +- %wpa_supplicant reads scan results with SIOCGIWSCAN (multiple call if + a larget buffer is needed) +- %wpa_supplicant decides which AP to use based on scan results +- %wpa_supplicant configures driver to associate with the selected BSS + (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWFREQ, + SIOCSIWESSID, SIOCSIWAP) + +ap_scan=2: + +- %wpa_supplicant configures driver to associate with an SSID + (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWESSID) + + +After this, both modes use similar steps: + +- optionally (or required for drivers that generate WPA/RSN IE for + (Re)AssocReq), driver reports association parameters (AssocReq IEs) + with wireless event IWEVASSOCREQIE (and optionally IWEVASSOCRESPIE) +- driver reports association with wireless event SIOCGIWAP +- %wpa_supplicant takes care of EAPOL frame handling (validating + information from associnfo and if needed, from scan results if WPA/RSN + IE from the Beacon frame is not reported through associnfo) +*/ diff --git a/contrib/wpa_supplicant/doc/eap.doxygen b/contrib/wpa_supplicant/doc/eap.doxygen new file mode 100644 index 0000000..e1ae9c0 --- /dev/null +++ b/contrib/wpa_supplicant/doc/eap.doxygen @@ -0,0 +1,38 @@ +/** +\page eap_module EAP peer implementation + +%wpa_supplicant uses a separate code module for EAP peer +implementation. This module was designed to use only a minimal set of +direct function calls (mainly, to debug/event functions) in order for +it to be usable in other programs. The design of the EAP +implementation is based loosely on RFC 4137. The state machine is +defined in this RFC and so is the interface between the peer state +machine and methods. As such, this RFC provides useful information for +understanding the EAP peer implementation in %wpa_supplicant. + +Some of the terminology used in EAP state machine is referring to +EAPOL (IEEE 802.1X), but there is no strict requirement on the lower +layer being IEEE 802.1X if EAP module is built for other programs than +%wpa_supplicant. These terms should be understood to refer to the +lower layer as defined in RFC 4137. + + +\section adding_eap_methods Adding EAP methods + +Each EAP method is implemented as a separate module, usually as one C +file named eap_<name of the method>.c, e.g., eap_md5.c. All EAP +methods use the same interface between the peer state machine and +method specific functions. This allows new EAP methods to be added +without modifying the core EAP state machine implementation. + +New EAP methods need to be registered by adding them into build +(Makefile) and EAP method table in the beginning of eap.c. Each EAP +method should use a build-time configuration option, e.g., EAP_TLS, in +order to make it possible to select which of the methods are included +in the build. + +EAP methods must implement the interface defined in eap_i.h. struct +eap_method defines the needed function pointers that each EAP method +must provide. In addition, the EAP type and name are registered using +this structure. This interface is based on section 4.4 of RFC 4137. +*/ diff --git a/contrib/wpa_supplicant/doc/kerneldoc2doxygen.pl b/contrib/wpa_supplicant/doc/kerneldoc2doxygen.pl new file mode 100755 index 0000000..d46f7bd --- /dev/null +++ b/contrib/wpa_supplicant/doc/kerneldoc2doxygen.pl @@ -0,0 +1,129 @@ +#!/usr/bin/perl -w +# +########################################################################## +# Convert kernel-doc style comments to Doxygen comments. +########################################################################## +# +# This script reads a C source file from stdin, and writes +# to stdout. Normal usage: +# +# $ mv file.c file.c.gtkdoc +# $ kerneldoc2doxygen.pl <file.c.gtkdoc >file.c +# +# Or to do the same thing with multiple files: +# $ perl -i.gtkdoc kerneldoc2doxygen.pl *.c *.h +# +# This script may also be suitable for use as a Doxygen input filter, +# but that has not been tested. +# +# Back up your source files before using this script!! +# +########################################################################## +# Copyright (C) 2003 Jonathan Foster <jon@jon-foster.co.uk> +# Copyright (C) 2005 Jouni Malinen <jkmaline@cc.hut.fi> +# (modified for kerneldoc format used in wpa_supplicant) +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# or look at http://www.gnu.org/licenses/gpl.html +########################################################################## + + +########################################################################## +# +# This function converts a single comment from gtk-doc to Doxygen format. +# The parameter does not include the opening or closing lines +# (i.e. given a comment like this: +# "/**\n" +# " * FunctionName:\n" +# " * @foo: This describes the foo parameter\n" +# " * @bar: This describes the bar parameter\n" +# " * @Returns: This describes the return value\n" +# " *\n" +# " * This describes the function.\n" +# " */\n" +# This function gets: +# " * FunctionName:\n" +# " * @foo: This describes the foo parameter\n" +# " * @bar: This describes the bar parameter\n" +# " * @Returns: This describes the return value\n" +# " *\n" +# " * This describes the function.\n" +# And it returns: +# " * This describes the function.\n" +# " *\n" +# " * @param foo This describes the foo parameter\n" +# " * @param bar This describes the bar parameter\n" +# " * @return This describes the return value\n" +# ) +# +sub fixcomment { + $t = $_[0]; + + # " * func: foo" --> "\brief foo\n" + # " * struct bar: foo" --> "\brief foo\n" + # If this fails, not a kernel-doc comment ==> return unmodified. + ($t =~ s/^[\t ]*\*[\t ]*(struct )?([^ \t\n]*) - ([^\n]*)/\\brief $3\n/s) + or return $t; + + # " * Returns: foo" --> "\return foo" + $t =~ s/\n[\t ]*\*[\t ]*Returns:/\n\\return/sig; + + # " * @foo: bar" --> "\param foo bar" + # Handle two common typos: No ":", or "," instead of ":". + $t =~ s/\n[\t ]*\*[\t ]*\@([^ :,]*)[:,]?[\t ]*/\n\\param $1 /sg; + + return $t; +} + +########################################################################## +# Start of main code + +# Read entire stdin into memory - one multi-line string. +$_ = do { local $/; <> }; + +s{^/\*\n \*}{/\*\* \\file\n\\brief}; +s{ \* Copyright}{\\par Copyright\nCopyright}; + +# Fix any comments like "/*************" so they don't match. +# "/***" ===> "/* *" +s{/\*\*\*}{/\* \*}gs; + +# The main comment-detection code. +s{ + ( # $1 = Open comment + /\*\* # Open comment + (?!\*) # Do not match /*** (redundant due to fixup above). + [\t ]*\n? # If 1st line is whitespace, match the lot (including the newline). + ) + (.*?) # $2 = Body of comment (multi-line) + ( # $3 = Close comment + ( # If possible, match the whitespace before the close-comment + (?<=\n) # This part only matches after a newline + [\t ]* # Eat whitespace + )? + \*/ # Close comment + ) + } + { + $1 . fixcomment($2) . $3 + }gesx; +# ^^^^ Modes: g - Global, match all occurances. +# e - Evaluate the replacement as an expression. +# s - Single-line - allows the pattern to match across newlines. +# x - eXtended pattern, ignore embedded whitespace +# and allow comments. + +# Write results to stdout +print $_; + diff --git a/contrib/wpa_supplicant/doc/mainpage.doxygen b/contrib/wpa_supplicant/doc/mainpage.doxygen new file mode 100644 index 0000000..56882f4 --- /dev/null +++ b/contrib/wpa_supplicant/doc/mainpage.doxygen @@ -0,0 +1,56 @@ +/** +\mainpage Developers' documentation for %wpa_supplicant + +%wpa_supplicant is a WPA Supplicant for Linux, BSD and Windows with +support for WPA and WPA2 (IEEE 802.11i / RSN). Supplicant is the IEEE +802.1X/WPA component that is used in the client stations. It +implements key negotiation with a WPA Authenticator and it can optionally +control roaming and IEEE 802.11 authentication/association of the wlan +driver. + +The goal of this documentation and comments in the source code is to +give enough information for other developers to understand how +%wpa_supplicant has been implemented, how it can be modified, how new +drivers can be supported, and how %wpa_supplicant can be ported to +other operating systems. If any information is missing, feel free to +contact Jouni Malinen <jkmaline@cc.hut.fi> for more +information. Contributions as patch files are also very welcome at the +same address. Please note that %wpa_supplicant is licensed under dual +license, GPLv2 or BSD at user's choice. All contributions to +%wpa_supplicant are expected to use compatible licensing terms. + +The source code and read-only access to %wpa_supplicant CVS repository +is available from the project home page at +http://hostap.epitest.fi/wpa_supplicant/. This developers' documentation +is also available as a PDF file from +http://hostap.epitest.fi/wpa_supplicant/wpa_supplicant-devel.pdf . + +The design goal for %wpa_supplicant was to use hardware, driver, and +OS independent, portable C code for all WPA functionality. The source +code is divided into separate C files as shown on the \ref +code_structure "code structure page". All hardware/driver specific +functionality is in separate files that implement a \ref +driver_wrapper "well-defined driver API". Information about porting +to different target boards and operating systems is available on +the \ref porting "porting page". + +EAPOL (IEEE 802.1X) state machines are implemented as a separate +module that interacts with \ref eap_module "EAP peer implementation". +In addition to programs aimed at normal production use, +%wpa_supplicant source tree includes number of \ref testing_tools +"testing and development tools" that make it easier to test the +programs without having to setup a full test setup with wireless +cards. These tools can also be used to implement automatic test +suites. + +%wpa_supplicant implements a +\ref ctrl_iface_page "control interface" that can be used by +external programs to control the operations of the %wpa_supplicant +daemon and to get status information and event notifications. There is +a small C library that provides helper functions to facilitate the use of the +control interface. This library can also be used with C++. + +\image html wpa_supplicant.png "wpa_supplicant modules" +\image latex wpa_supplicant.eps "wpa_supplicant modules" width=15cm + +*/ diff --git a/contrib/wpa_supplicant/doc/porting.doxygen b/contrib/wpa_supplicant/doc/porting.doxygen new file mode 100644 index 0000000..db64a11 --- /dev/null +++ b/contrib/wpa_supplicant/doc/porting.doxygen @@ -0,0 +1,121 @@ +/** +\page porting Porting to different target boards and operating systems + +%wpa_supplicant was designed to be easily portable to different +hardware (board, CPU) and software (OS, drivers) targets. It is +already used with number of operating systems and numerous wireless +card models and drivers. The main %wpa_supplicant repository includes +support for Linux, FreeBSD, and Windows. In addition, at least VxWorks +and PalmOS are supported in separate repositories. On the hardware +side, %wpa_supplicant is used on various systems: desktops, laptops, +PDAs, and embedded devices with CPUs including x86, PowerPC, +arm/xscale, and MIPS. Both big and little endian configurations are +supported. + + +\section driver_iface_porting Driver interface + +Unless the target OS and driver is already supported, most porting +projects have to implement a driver wrapper. This may be done by +adding a new driver interface module or modifying an existing module +(driver_*.c) if the new target is similar to one of them. \ref +driver_wrapper "Driver wrapper implementation" describes the details +of the driver interface and discusses the tasks involved in porting +this part of %wpa_supplicant. + + +\section l2_packet_porting l2_packet (link layer access) + +%wpa_supplicant needs to have access to sending and receiving layer 2 +(link layer) packets with two Ethertypes: EAP-over-LAN (EAPOL) 0x888e +and RSN pre-authentication 0x88c7. l2_packet.h defines the interfaces +used for this in the core %wpa_supplicant implementation. + +If the target operating system supports a generic mechanism for link +layer access, that is likely the best mechanism for providing the +needed functionality for %wpa_supplicant. Linux packet socket is an +example of such a generic mechanism. If this is not available, a +separate interface may need to be implemented to the network stack or +driver. This is usually an intermediate or protocol driver that is +operating between the device driver and the OS network stack. If such +a mechanism is not feasible, the interface can also be implemented +directly in the device driver. + +The main %wpa_supplicant repository includes l2_packet implementations +for Linux using packet sockets (l2_packet_linux.c), more portable +version using libpcap/libdnet libraries (l2_packet_pcap.c; this +supports WinPcap, too), and FreeBSD specific version of libpcap +interface (l2_packet_freebsd.c). + +If the target operating system is supported by libpcap (receiving) and +libdnet (sending), l2_packet_pcap.c can likely be used with minimal or +no changes. If this is not a case or a proprietary interface for link +layer is required, a new l2_packet module may need to be +added. Alternatively, struct wpa_driver_ops::send_eapol() handler can +be used to override the l2_packet library if the link layer access is +integrated with the driver interface implementation. + + +\section eloop_porting Event loop + +%wpa_supplicant uses a single process/thread model and an event loop +to provide callbacks on events (registered timeout, received packet, +signal). eloop.h defines the event loop interface. eloop.c is an +implementation of such an event loop using select() and sockets. This +is suitable for most UNIX/POSIX systems. When porting to other +operating systems, it may be necessary to replace that implementation +with OS specific mechanisms that provide similar functionality. + + +\section ctrl_iface_porting Control interface + +%wpa_supplicant uses a \ref ctrl_iface_page "control interface" +to allow external processed +to get status information and to control the operations. Currently, +this is implemented with socket based communication; both UNIX domain +sockets and UDP sockets are supported. If the target OS does not +support sockets, this interface will likely need to be modified to use +another mechanism like message queues. The control interface is +optional component, so it is also possible to run %wpa_supplicant +without porting this part. + +The %wpa_supplicant side of the control interface is implemented in +ctrl_iface.c. Matching client side is implemented as a control +interface library in wpa_ctrl.c. + + +\section entry_point Program entry point + +%wpa_supplicant defines a set of functions that can be used to +initialize main supplicant processing. Each operating system has a +mechanism for starting new processing or threads. This is usually a +function with a specific set of arguments and calling convention. This +function is responsible on initializing %wpa_supplicant. + +main.c includes an entry point for UNIX-like operating system, i.e., +main() function that uses command line arguments for setting +parameters for %wpa_supplicant. When porting to other operating +systems, similar OS-specific entry point implementation is needed. It +can be implemented in a new file that is then linked with +%wpa_supplicant instead of main.o. main.c is also a good example on +how the initialization process should be done. + +The supplicant initialization functions are defined in +wpa_supplicant_i.h. In most cases, the entry point function should +start by fetching configuration parameters. After this, a global +%wpa_supplicant context is initialized with a call to +wpa_supplicant_init(). After this, existing network interfaces can be +added with wpa_supplicant_add_iface(). wpa_supplicant_run() is then +used to start the main event loop. Once this returns at program +termination time, wpa_supplicant_deinit() is used to release global +context data. + +wpa_supplicant_add_iface() and wpa_supplicant_remove_iface() can be +used dynamically to add and remove interfaces based on when +%wpa_supplicant processing is needed for them. This can be done, e.g., +when hotplug network adapters are being inserted and ejected. It is +also possible to do this when a network interface is being +enabled/disabled if it is desirable that %wpa_supplicant processing +for the interface is fully enabled/disabled at the same time. + +*/ diff --git a/contrib/wpa_supplicant/doc/testing_tools.doxygen b/contrib/wpa_supplicant/doc/testing_tools.doxygen new file mode 100644 index 0000000..c1e2408 --- /dev/null +++ b/contrib/wpa_supplicant/doc/testing_tools.doxygen @@ -0,0 +1,288 @@ +/** +\page testing_tools Testing and development tools + +[ \ref eapol_test "eapol_test" | +\ref preauth_test "preauth_test" | +\ref driver_test "driver_test" | +\ref unit_tests "Unit tests" ] + +%wpa_supplicant source tree includes number of testing and development +tools that make it easier to test the programs without having to setup +a full test setup with wireless cards. In addition, these tools can be +used to implement automatic tests suites. + +\section eapol_test eapol_test - EAP peer and RADIUS client testing + +eapol_test is a program that links together the same EAP peer +implementation that %wpa_supplicant is using and the RADIUS +authentication client code from hostapd. In addition, it has minimal +glue code to combine these two components in similar ways to IEEE +802.1X/EAPOL Authenticator state machines. In other words, it +integrates IEEE 802.1X Authenticator (normally, an access point) and +IEEE 802.1X Supplicant (normally, a wireless client) together to +generate a single program that can be used to test EAP methods without +having to setup an access point and a wireless client. + +The main uses for eapol_test are in interoperability testing of EAP +methods against RADIUS servers and in development testing for new EAP +methods. It can be easily used to automate EAP testing for +interoperability and regression since the program can be run from +shell scripts without require additional test components apart from a +RADIUS server. For example, the automated EAP tests described in +eap_testing.txt are implemented with eapol_test. Similarly, eapol_test +could be used to implement an automated regression test suite for a +RADIUS authentication server. + +eapol_test uses the same build time configuration file, .config, as +%wpa_supplicant. This file is used to select which EAP methods are +included in eapol_test. This program is not built with the default +Makefile target, so a separate make command needs to be used to +compile the tool: + +\verbatim +make eapol_test +\endverbatim + +The resulting eapol_test binary has following command like options: + +\verbatim +usage: +eapol_test [-nW] -c<conf> [-a<AS IP>] [-p<AS port>] [-s<AS secret>] [-r<count>] +eapol_test scard +eapol_test sim <PIN> <num triplets> [debug] + +options: + -c<conf> = configuration file + -a<AS IP> = IP address of the authentication server, default 127.0.0.1 + -p<AS port> = UDP port of the authentication server, default 1812 + -s<AS secret> = shared secret with the authentication server, default 'radius' + -r<count> = number of re-authentications + -W = wait for a control interface monitor before starting + -n = no MPPE keys expected +\endverbatim + + +As an example, +\verbatim +eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1 +\endverbatim +tries to complete EAP authentication based on the network +configuration from test.conf against the RADIUS server running on the +local host. A re-authentication is triggered to test fast +re-authentication. The configuration file uses the same format for +network blocks as %wpa_supplicant. + + +\section preauth_test preauth_test - WPA2 pre-authentication and EAP peer testing + +preauth_test is similar to eapol_test in the sense that in combines +EAP peer implementation with something else, in this case, with WPA2 +pre-authentication. This tool can be used to test pre-authentication +based on the code that %wpa_supplicant is using. As such, it tests +both the %wpa_supplicant implementation and the functionality of an +access point. + +preauth_test is built with: + +\verbatim +make preauth_test +\endverbatim + +and it uses following command line arguments: + +\verbatim +usage: preauth_test <conf> <target MAC address> <ifname> +\endverbatim + +For example, +\verbatim +preauth_test test.conf 02:11:22:33:44:55 eth0 +\endverbatim +would use network configuration from test.conf to try to complete +pre-authentication with AP using BSSID 02:11:22:33:44:55. The +pre-authentication packets would be sent using the eth0 interface. + + +\section driver_test driver_test - driver interface for testing wpa_supplicant + +%wpa_supplicant was designed to support number of different ways to +communicate with a network device driver. This design uses \ref +driver_wrapper "driver interface API" and number of driver interface +implementations. One of these is driver_test.c, i.e., a test driver +interface that is actually not using any drivers. Instead, it provides +a mechanism for running %wpa_supplicant without having to have a +device driver or wireless LAN hardware for that matter. + +driver_test can be used to talk directly with hostapd's driver_test +component to create a test setup where one or more clients and access +points can be tested within one test host and without having to have +multiple wireless cards. This makes it easier to test the core code in +%wpa_supplicant, and hostapd for that matter. Since driver_test uses +the same driver API than any other driver interface implementation, +the core code of %wpa_supplicant and hostapd can be tested with the +same coverage as one would get when using real wireless cards. The +only area that is not tested is the driver interface implementation +(driver_*.c). + +Having the possibility to use simulated network components makes it +much easier to do development testing while adding new features and to +reproduce reported bugs. As such, it is often easiest to just do most +of the development and bug fixing without using real hardware. Once +the driver_test setup has been used to implement a new feature or fix +a bug, the end result can be verified with wireless LAN cards. In many +cases, this may even be unnecessary, depending on what area the +feature/bug is relating to. Of course, changes to driver interfaces +will still require use of real hardware. + +Since multiple components can be run within a single host, testing of +complex network configuration, e.g., large number of clients +association with an access point, becomes quite easy. All the tests +can also be automated without having to resort to complex test setup +using remote access to multiple computers. + +driver_test can be included in the %wpa_supplicant build in the same +way as any other driver interface, i.e., by adding the following line +into .config: + +\verbatim +CONFIG_DRIVER_TEST=y +\endverbatim + +When running %wpa_supplicant, the test interface is selected by using +\a -Dtest command line argument. The interface name (\a -i argument) +can be selected arbitrarily, i.e., it does not need to match with any +existing network interface. The interface name is used to generate a +MAC address, so when using multiple clients, each should use a +different interface, e.g., \a sta1, \a sta2, and so on. + +%wpa_supplicant and hostapd are configured in the same way as they +would be for normal use. Following example shows a simple test setup +for WPA-PSK. + +hostapd is configured with following psk-test.conf configuration file: + +\verbatim +driver=test + +interface=ap1 +logger_stdout=-1 +logger_stdout_level=0 +debug=2 +dump_file=/tmp/hostapd.dump + +test_socket=/tmp/Test/ap1 + +ssid=jkm-test-psk + +wpa=1 +wpa_key_mgmt=WPA-PSK +wpa_pairwise=TKIP +wpa_passphrase=12345678 +\endverbatim + +and started with following command: + +\verbatim +hostapd psk-test.conf +\endverbatim + +%wpa_supplicant uses following configuration file: + +\verbatim +driver_param=test_socket=/tmp/Test/ap1 + +network={ + ssid="jkm-test-psk" + key_mgmt=WPA-PSK + psk="12345678" +} +\endverbatim + +%wpa_supplicant can then be started with following command: + +\verbatim +wpa_supplicant -Dtest -cpsk-test.conf -ista1 -ddK +\endverbatim + +If run without debug information, i.e., with + +\verbatim +wpa_supplicant -Dtest -cpsk-test.conf -ista1 +\endverbatim + +%wpa_supplicant completes authentication and prints following events: + +\verbatim +Trying to associate with 02:b8:a6:62:08:5a (SSID='jkm-test-psk' freq=0 MHz) +Associated with 02:b8:a6:62:08:5a +WPA: Key negotiation completed with 02:b8:a6:62:08:5a [PTK=TKIP GTK=TKIP] +CTRL-EVENT-CONNECTED - Connection to 02:b8:a6:62:08:5a completed (auth) +\endverbatim + +If test setup is using multiple clients, it is possible to run +multiple %wpa_supplicant processes. Alternatively, the support for +multiple interfaces can be used with just one process to save some +resources on single-CPU systems. For example, following command runs +two clients: + +\verbatim +./wpa_supplicant -Dtest -cpsk-test.conf -ista1 \ + -N -Dtest -cpsk-test.conf -ista2 +\endverbatim + +This shows following event log: + +\verbatim +Trying to associate with 02:b8:a6:62:08:5a (SSID='jkm-test-psk' freq=0 MHz) +Associated with 02:b8:a6:62:08:5a +WPA: Key negotiation completed with 02:b8:a6:62:08:5a [PTK=TKIP GTK=TKIP] +CTRL-EVENT-CONNECTED - Connection to 02:b8:a6:62:08:5a completed (auth) +Trying to associate with 02:b8:a6:62:08:5a (SSID='jkm-test-psk' freq=0 MHz) +Associated with 02:b8:a6:62:08:5a +WPA: Key negotiation completed with 02:b8:a6:62:08:5a [PTK=TKIP GTK=TKIP] +CTRL-EVENT-CONNECTED - Connection to 02:b8:a6:62:08:5a completed (auth) +\endverbatim + +hostapd shows this with following events: + +\verbatim +ap1: STA 02:b5:64:63:30:63 IEEE 802.11: associated +ap1: STA 02:b5:64:63:30:63 WPA: pairwise key handshake completed (WPA) +ap1: STA 02:b5:64:63:30:63 WPA: group key handshake completed (WPA) +ap1: STA 02:2a:c4:18:5b:f3 IEEE 802.11: associated +ap1: STA 02:2a:c4:18:5b:f3 WPA: pairwise key handshake completed (WPA) +ap1: STA 02:2a:c4:18:5b:f3 WPA: group key handshake completed (WPA) +\endverbatim + +By default, driver_param is simulating a driver that uses the WPA/RSN +IE generated by %wpa_supplicant. Driver-generated IE and AssocInfo +events can be tested by adding \a use_associnfo=1 to the \a driver_param +line in the configuration file. For example: + +\verbatim +driver_param=test_socket=/tmp/Test/ap1 use_associnfo=1 +\endverbatim + + +\section unit_tests Unit tests + +Number of the components (.c files) used in %wpa_supplicant define +their own unit tests for automated validation of the basic +functionality. Most of the tests for cryptographic algorithms are +using standard test vectors to validate functionality. These tests can +be useful especially when verifying port to a new CPU target. + +In most cases, these tests are implemented in the end of the same file +with functions that are normally commented out, but ca be included by +defining a pre-processor variable when building the file separately. +The details of the needed build options are included in the Makefile +(test-* targets). All automated unit tests can be run with + +\verbatim +make tests +\endverbatim + +This make target builds and runs each test and terminates with zero +exit code if all tests were completed successfully. + +*/ diff --git a/contrib/wpa_supplicant/doc/wpa_supplicant.fig b/contrib/wpa_supplicant/doc/wpa_supplicant.fig index dc1d9df..06abfb5 100644 --- a/contrib/wpa_supplicant/doc/wpa_supplicant.fig +++ b/contrib/wpa_supplicant/doc/wpa_supplicant.fig @@ -117,7 +117,7 @@ Single 9600 3000 10275 3000 10275 3300 9600 3300 9600 3000 4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3225 TLS\001 -6 -6 8100 4425 10425 6975 +6 8100 4425 10425 7350 6 8175 4725 9225 5025 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725 @@ -153,11 +153,6 @@ Single 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850 4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001 -6 -6 8175 6600 9675 6900 -2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 - 8175 6600 9675 6600 9675 6900 8175 6900 8175 6600 -4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 6825 EAP-MSCHAPv2\001 --6 6 9300 6225 10350 6525 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 9300 6225 10350 6225 10350 6525 9300 6525 9300 6225 @@ -173,10 +168,35 @@ Single 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850 4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001 -6 +6 8175 6975 9675 7275 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6975 9675 6975 9675 7275 8175 7275 8175 6975 +4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 7200 EAP-MSCHAPv2\001 +-6 +6 9300 6600 10350 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 6600 10350 6600 10350 6900 9300 6900 9300 6600 +4 0 0 50 -1 0 12 0.0000 4 135 870 9375 6825 EAP-FAST\001 +-6 +6 8175 6600 9225 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6600 9225 6600 9225 6900 8175 6900 8175 6600 +4 0 0 50 -1 0 12 0.0000 4 135 795 8250 6825 EAP-PAX\001 +-6 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 - 8100 6975 10425 6975 10425 4425 8100 4425 8100 6975 + 8100 7350 10425 7350 10425 4425 8100 4425 8100 7350 4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001 -6 +6 2775 5025 4050 5325 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 2775 5025 4050 5025 4050 5325 2775 5325 2775 5025 +4 0 0 50 -1 0 12 0.0000 4 135 990 2925 5250 driver events\001 +-6 +6 2775 3150 4050 3450 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 2775 3150 4050 3150 4050 3450 2775 3450 2775 3150 +4 0 0 50 -1 0 12 0.0000 4 180 990 2925 3375 configuration\001 +-6 2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 1275 4200 1875 4200 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 @@ -213,9 +233,15 @@ Single 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100 2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 9900 4425 9900 3300 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1 + 4350 3900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4350 3900 4050 3450 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4350 4425 4050 5025 4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001 4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001 4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001 4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001 4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001 -4 0 0 50 -1 2 14 0.0000 4 195 1425 1637 2371 wpa_supplicant\001 +4 0 0 50 -1 2 14 0.0000 4 210 1440 1637 2371 wpa_supplicant\001 diff --git a/contrib/wpa_supplicant/driver.h b/contrib/wpa_supplicant/driver.h index da28014..9864e9b 100644 --- a/contrib/wpa_supplicant/driver.h +++ b/contrib/wpa_supplicant/driver.h @@ -1,13 +1,23 @@ +/* + * WPA Supplicant - driver interface definition + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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_H #define DRIVER_H #define WPA_SUPPLICANT_DRIVER_VERSION 2 -typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg; -typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, - CIPHER_WEP104 } wpa_cipher; -typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, - KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE } wpa_key_mgmt; +#include "defs.h" #define AUTH_ALG_OPEN_SYSTEM 0x01 #define AUTH_ALG_SHARED_KEY 0x02 @@ -16,7 +26,31 @@ typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, #define IEEE80211_MODE_INFRA 0 #define IEEE80211_MODE_IBSS 1 +#define IEEE80211_CAP_ESS 0x0001 +#define IEEE80211_CAP_IBSS 0x0002 +#define IEEE80211_CAP_PRIVACY 0x0010 + #define SSID_MAX_WPA_IE_LEN 40 +/** + * struct wpa_scan_result - Scan results + * @bssid: BSSID + * @ssid: SSID + * @ssid_len: length of the ssid + * @wpa_ie: WPA IE + * @wpa_ie_len: length of the wpa_ie + * @rsn_ie: RSN IE + * @rsn_ie_len: length of the RSN IE + * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) + * @caps: capability information field in host byte order + * @qual: signal quality + * @noise: noise level + * @level: signal level + * @maxrate: maximum supported rate + * + * This structure is used as a generic format for scan results from the + * driver. Each driver interface implementation is responsible for converting + * the driver or OS specific scan results into this format. + */ struct wpa_scan_result { u8 bssid[ETH_ALEN]; u8 ssid[32]; @@ -25,40 +59,57 @@ struct wpa_scan_result { size_t wpa_ie_len; u8 rsn_ie[SSID_MAX_WPA_IE_LEN]; size_t rsn_ie_len; - int freq; /* MHz */ - int caps; /* e.g. privacy */ - int qual; /* signal quality */ + int freq; + u16 caps; + int qual; int noise; int level; int maxrate; }; -/* Parameters for associate driver_ops. */ +/** + * struct wpa_driver_associate_params - Association parameters + * Data for struct wpa_driver_ops::associate(). + */ struct wpa_driver_associate_params { - /* BSSID of the selected AP */ + /** + * bssid - BSSID of the selected AP + * This can be %NULL, if ap_scan=2 mode is used and the driver is + * responsible for selecting with which BSS to associate. */ const u8 *bssid; - /* The selected SSID and its length in bytes */ + /** + * ssid - The selected SSID + */ const u8 *ssid; size_t ssid_len; - /* frequency that the selected AP is using (in MHz as - * reported in the scan results) */ + /** + * freq - Frequency of the channel the selected AP is using + * Frequency that the selected AP is using (in MHz as + * reported in the scan results) + */ int freq; - /* WPA information element to be included in (Re)Association + /** + * wpa_ie - WPA information element for (Re)Association Request + * WPA information element to be included in (Re)Association * Request (including information element id and length). Use * of this WPA IE is optional. If the driver generates the WPA - * IE, it can use @pairwise_suite, @group_suite, and - * @key_mgmt_suite to select proper algorithms. In this case, + * IE, it can use pairwise_suite, group_suite, and + * key_mgmt_suite to select proper algorithms. In this case, * the driver has to notify wpa_supplicant about the used WPA * IE by generating an event that the interface code will * convert into EVENT_ASSOCINFO data (see wpa_supplicant.h). - * When using WPA2/IEEE 802.11i, @wpa_ie is used for RSN IE + * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE * instead. The driver can determine which version is used by * looking at the first byte of the IE (0xdd for WPA, 0x30 for - * WPA2/RSN). @wpa_ie_len: length of the @wpa_ie */ + * WPA2/RSN). + */ const u8 *wpa_ie; + /** + * wpa_ie_len - length of the wpa_ie + */ size_t wpa_ie_len; /* The selected pairwise/group cipher and key management @@ -67,10 +118,21 @@ struct wpa_driver_associate_params { wpa_cipher group_suite; wpa_key_mgmt key_mgmt_suite; - int auth_alg; /* bit field of AUTH_ALG_* */ - int mode; /* IEEE80211_MODE_* */ + /** + * auth_alg - Allowed authentication algorithms + * Bit field of AUTH_ALG_* + */ + int auth_alg; + + /** + * mode - Operation mode (infra/ibss) IEEE80211_MODE_* + */ + int mode; }; +/** + * struct wpa_driver_capa - Driver capability information + */ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002 @@ -97,33 +159,40 @@ struct wpa_driver_capa { }; +/** + * struct wpa_driver_ops - Driver interface API definition + * + * This structure defines the API that each driver interface needs to implement + * for core wpa_supplicant code. All driver specific functionality is captured + * in this wrapper. + */ struct wpa_driver_ops { - /* name of the driver interface */ + /** Name of the driver interface */ const char *name; - /* one line description of the driver interface */ + /** One line description of the driver interface */ const char *desc; /** - * get_bssid - get the current BSSID + * get_bssid - Get the current BSSID * @priv: private driver interface data * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) * * Returns: 0 on success, -1 on failure * - * Query kernel driver for the current BSSID and copy it to @bssid. - * Setting @bssid to 00:00:00:00:00:00 is recommended if the STA is not + * Query kernel driver for the current BSSID and copy it to bssid. + * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not * associated. */ int (*get_bssid)(void *priv, u8 *bssid); /** - * get_ssid - get the current SSID + * get_ssid - Get the current SSID * @priv: private driver interface data * @ssid: buffer for SSID (at least 32 bytes) * - * Returns: length of the SSID on success, -1 on failure + * Returns: Length of the SSID on success, -1 on failure * - * Query kernel driver for the current SSID and copy it to @ssid. + * Query kernel driver for the current SSID and copy it to ssid. * Returning zero is recommended if the STA is not associated. * * Note: SSID is an array of octets, i.e., it is not nul terminated and @@ -134,35 +203,44 @@ struct wpa_driver_ops { int (*get_ssid)(void *priv, u8 *ssid); /** - * set_wpa - enable/disable WPA support + * set_wpa - Enable/disable WPA support (OBSOLETE) * @priv: private driver interface data * @enabled: 1 = enable, 0 = disable * * Returns: 0 on success, -1 on failure * + * Note: This function is included for backwards compatibility. This is + * called only just after init and just before deinit, so these + * functions can be used to implement same functionality and the driver + * interface need not define this function. + * * Configure the kernel driver to enable/disable WPA support. This may * be empty function, if WPA support is always enabled. Common * configuration items are WPA IE (clearing it when WPA support is - * disabled), Privacy flag for capability field, roaming mode (need to - * allow wpa_supplicant to control roaming). + * disabled), Privacy flag configuration for capability field (note: + * this the value need to set in associate handler to allow plaintext + * mode to be used) when trying to associate with, roaming mode (can + * allow wpa_supplicant to control roaming if ap_scan=1 is used; + * however, drivers can also implement roaming if desired, especially + * ap_scan=2 mode is used for this). */ int (*set_wpa)(void *priv, int enabled); /** - * set_key - configure encryption key + * set_key - Configure encryption key * @priv: private driver interface data * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for * broadcast/default keys - * @key_idx: key index (0..3), always 0 for unicast keys + * @key_idx: key index (0..3), usually 0 for unicast keys * @set_tx: configure this key as the default Tx key (only used when * driver does not support separate unicast/individual key - * @seq: sequence number/packet number, @seq_len octets, the next + * @seq: sequence number/packet number, seq_len octets, the next * packet number to be used for in replay protection; configured * for Rx keys (in most cases, this is only used with broadcast * keys and set to zero for unicast keys) - * @seq_len: length of the @seq, depends on the algorithm: + * @seq_len: length of the seq, depends on the algorithm: * TKIP: 6 octets, CCMP: 6 octets * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, * 8-byte Rx Mic Key @@ -173,25 +251,33 @@ struct wpa_driver_ops { * * Configure the given key for the kernel driver. If the driver * supports separate individual keys (4 default keys + 1 individual), - * @addr can be used to determine whether the key is default or + * addr can be used to determine whether the key is default or * individual. If only 4 keys are supported, the default key with key * index 0 is used as the individual key. STA must be configured to use - * it as the default Tx key (@set_tx is set) and accept Rx for all the + * it as the default Tx key (set_tx is set) and accept Rx for all the * key indexes. In most cases, WPA uses only key indexes 1 and 2 for * broadcast keys, so key index 0 is available for this kind of * configuration. + * + * Please note that TKIP keys include separate TX and RX MIC keys and + * some drivers may expect them in different order than wpa_supplicant + * is using. If the TX/RX keys are swapped, all TKIP encrypted packets + * will tricker Michael MIC errors. This can be fixed by changing the + * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key + * in driver_*.c set_key() implementation, see driver_ndis.c for an + * example on how this can be done. */ int (*set_key)(void *priv, wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); /** - * init - initialize driver interface + * init - Initialize driver interface * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 * - * Return: pointer to private data, %NULL on failure + * Returns: Pointer to private data, %NULL on failure * * Initialize driver interface, including event processing for kernel * driver events (e.g., associated, scan results, Michael MIC failure). @@ -211,9 +297,8 @@ struct wpa_driver_ops { void * (*init)(void *ctx, const char *ifname); /** - * deinit - deinitialize driver interface - * @priv: pointer to private data (from matching - * wpa_driver_events_init()) + * deinit - Deinitialize driver interface + * @priv: private driver interface data from init() * * Shut down driver interface and processing of driver events. Free * private data buffer if one was allocated in init() handler. @@ -221,11 +306,23 @@ struct wpa_driver_ops { void (*deinit)(void *priv); /** - * set_countermeasures - enable/disable TKIP countermeasures + * set_param - Set driver configuration parameters + * @priv: private driver interface data from init() + * @param: driver specific configuration parameters + * + * Returns: 0 on success, -1 on failure + * + * Optional handler for notifying driver interface about configuration + * parameters (driver_param). + */ + int (*set_param)(void *priv, const char *param); + + /** + * set_countermeasures - Enable/disable TKIP countermeasures * @priv: private driver interface data * @enabled: 1 = countermeasures enabled, 0 = disabled * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure * * Configure TKIP countermeasures. When these are enabled, the driver * should drop all received and queued frames that are using TKIP. @@ -233,11 +330,11 @@ struct wpa_driver_ops { int (*set_countermeasures)(void *priv, int enabled); /** - * set_drop_unencrypted - enable/disable unencrypted frame filtering + * set_drop_unencrypted - Enable/disable unencrypted frame filtering * @priv: private driver interface data * @enabled: 1 = unencrypted Tx/Rx frames will be dropped, 0 = disabled * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure * * Configure the driver to drop all non-EAPOL frames (both receive and * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must @@ -246,14 +343,14 @@ struct wpa_driver_ops { int (*set_drop_unencrypted)(void *priv, int enabled); /** - * scan - request the driver to initiate scan + * scan - Request the driver to initiate scan * @priv: private driver interface data * @ssid: specific SSID to scan for (ProbeReq) or %NULL to scan for * all SSIDs (either active scan with broadcast SSID or passive * scan * @ssid_len: length of the SSID * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure * * Once the scan results are ready, the driver should report scan * results event for wpa_supplicant which will eventually request the @@ -262,14 +359,15 @@ struct wpa_driver_ops { int (*scan)(void *priv, const u8 *ssid, size_t ssid_len); /** - * get_scan_results - fetch the latest scan results + * get_scan_results - Fetch the latest scan results * @priv: private driver interface data * @results: pointer to buffer for scan results * @max_size: maximum number of entries (buffer size) * - * Return: number of scan result entries used on success, -1 on failure + * Returns: Number of scan result entries used on success, -1 on + * failure * - * If scan results include more than @max_size BSSes, @max_size will be + * If scan results include more than max_size BSSes, max_size will be * returned and the remaining entries will not be included in the * buffer. */ @@ -278,39 +376,39 @@ struct wpa_driver_ops { size_t max_size); /** - * deauthenticate - request driver to deauthenticate + * deauthenticate - Request driver to deauthenticate * @priv: private driver interface data * @addr: peer address (BSSID of the AP) * @reason_code: 16-bit reason code to be sent in the deauthentication * frame * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure */ int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); /** - * disassociate - request driver to disassociate + * disassociate - Request driver to disassociate * @priv: private driver interface data * @addr: peer address (BSSID of the AP) * @reason_code: 16-bit reason code to be sent in the disassociation * frame * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure */ int (*disassociate)(void *priv, const u8 *addr, int reason_code); /** - * associate - request driver to associate + * associate - Request driver to associate * @priv: private driver interface data * @params: association parameters * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure */ int (*associate)(void *priv, struct wpa_driver_associate_params *params); /** - * set_auth_alg - set IEEE 802.11 authentication algorithm + * set_auth_alg - Set IEEE 802.11 authentication algorithm * @priv: private driver interface data * @auth_alg: bit field of AUTH_ALG_* * @@ -326,43 +424,43 @@ struct wpa_driver_ops { * associate() params, so set_auth_alg may not be needed in case of * most drivers. * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure */ int (*set_auth_alg)(void *priv, int auth_alg); /** - * add_pmkid - add PMKSA cache entry to the driver + * add_pmkid - Add PMKSA cache entry to the driver * @priv: private driver interface data * @bssid: BSSID for the PMKSA cache entry * @pmkid: PMKID for the PMKSA cache entry * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure * * This function is called when a new PMK is received, as a result of * either normal authentication or RSN pre-authentication. * - * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), add_pmkid() can be used to add new PMKSA cache entries - * in the driver. If the driver uses @wpa_ie from wpa_supplicant, this + * in the driver. If the driver uses wpa_ie from wpa_supplicant, this * driver_ops function does not need to be implemented. Likewise, if * the driver does not support WPA, this function is not needed. */ int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); /** - * remove_pmkid - remove PMKSA cache entry to the driver + * remove_pmkid - Remove PMKSA cache entry to the driver * @priv: private driver interface data * @bssid: BSSID for the PMKSA cache entry * @pmkid: PMKID for the PMKSA cache entry * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure * * This function is called when the supplicant drops a PMKSA cache * entry for any reason. * - * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), remove_pmkid() can be used to synchronize PMKSA caches - * between the driver and wpa_supplicant. If the driver uses @wpa_ie + * between the driver and wpa_supplicant. If the driver uses wpa_ie * from wpa_supplicant, this driver_ops function does not need to be * implemented. Likewise, if the driver does not support WPA, this * function is not needed. @@ -370,17 +468,17 @@ struct wpa_driver_ops { int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); /** - * flush_pmkid - flush PMKSA cache + * flush_pmkid - Flush PMKSA cache * @priv: private driver interface data * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure * * This function is called when the supplicant drops all PMKSA cache * entries for any reason. * - * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), remove_pmkid() can be used to synchronize PMKSA caches - * between the driver and wpa_supplicant. If the driver uses @wpa_ie + * between the driver and wpa_supplicant. If the driver uses wpa_ie * from wpa_supplicant, this driver_ops function does not need to be * implemented. Likewise, if the driver does not support WPA, this * function is not needed. @@ -388,17 +486,17 @@ struct wpa_driver_ops { int (*flush_pmkid)(void *priv); /** - * flush_pmkid - flush PMKSA cache + * flush_pmkid - Flush PMKSA cache * @priv: private driver interface data * - * Return: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure * * Get driver/firmware/hardware capabilities. */ int (*get_capa)(void *priv, struct wpa_driver_capa *capa); /** - * poll - poll driver for association information + * poll - Poll driver for association information * @priv: private driver interface data * * This is an option callback that can be used when the driver does not @@ -412,25 +510,50 @@ struct wpa_driver_ops { void (*poll)(void *priv); /** - * get_ifname - get interface name + * get_ifname - Get interface name + * @priv: private driver interface data + * + * Returns: Pointer to the interface name. This can differ from the + * interface name used in init() call. * * This optional function can be used to allow the driver interface to * replace the interface name with something else, e.g., based on an * interface mapping from a more descriptive name. - * - * Returns a pointer to the interface name. This can differ from the - * interface name used in init() call. */ const char * (*get_ifname)(void *priv); /** - * get_mac_addr - get own MAC address + * get_mac_addr - Get own MAC address + * @priv: private driver interface data + * + * Returns: Pointer to own MAC address or %NULL on failure * * This optional function can be used to get the own MAC address of the * device from the driver interface code. This is only needed if the * l2_packet implementation for the OS does not provide easy access to * a MAC address. */ const u8 * (*get_mac_addr)(void *priv); + + /** + * send_eapol - Optional function for sending EAPOL packets + * @priv: private driver interface data + * @dest: Destination MAC address + * @proto: Ethertype + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Size of the EAPOL packet + * + * Returns: 0 on success, -1 on failure + * + * This optional function can be used to override l2_packet operations + * with driver specific functionality. If this function pointer is set, + * l2_packet module is not used at all and the driver interface code is + * responsible for receiving and sending all EAPOL packets. The + * received EAPOL packets are sent to core code by calling + * wpa_supplicant_rx_eapol(). The driver interface is required to + * implement get_mac_addr() handler if send_eapol() is used. + */ + int (*send_eapol)(void *priv, const u8 *dest, u16 proto, + const u8 *data, size_t data_len); }; #endif /* DRIVER_H */ diff --git a/contrib/wpa_supplicant/driver_hostap.h b/contrib/wpa_supplicant/driver_hostap.h new file mode 100644 index 0000000..7679105 --- /dev/null +++ b/contrib/wpa_supplicant/driver_hostap.h @@ -0,0 +1,153 @@ +/* + * WPA Supplicant - driver interaction with Linux Host AP driver + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 HOSTAP_DRIVER_H +#define HOSTAP_DRIVER_H + +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, + PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, +}; + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, + PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY 0x01 +#define HOSTAP_CRYPT_FLAG_PERMANENT 0x02 + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + +#endif /* HOSTAP_DRIVER_H */ diff --git a/contrib/wpa_supplicant/driver_ndis.c b/contrib/wpa_supplicant/driver_ndis.c index 1dba95a..5ad2a31 100644 --- a/contrib/wpa_supplicant/driver_ndis.c +++ b/contrib/wpa_supplicant/driver_ndis.c @@ -393,6 +393,20 @@ static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid) { struct wpa_driver_ndis_data *drv = priv; + if (drv->wired) { + /* + * Report PAE group address as the "BSSID" for wired + * connection. + */ + bssid[0] = 0x01; + bssid[1] = 0x80; + bssid[2] = 0xc2; + bssid[3] = 0x00; + bssid[4] = 0x00; + bssid[5] = 0x03; + return 0; + } + return ndis_get_oid(drv, OID_802_11_BSSID, bssid, ETH_ALEN) < 0 ? -1 : 0; } @@ -406,8 +420,13 @@ static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid) int res; res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); - if (!res) { + if (res < 4) { wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID"); + if (drv->wired) { + wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure " + "with a wired interface"); + return 0; + } return -1; } memcpy(ssid, buf.Ssid, buf.SsidLength); @@ -423,6 +442,12 @@ static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv, memset(&buf, 0, sizeof(buf)); buf.SsidLength = ssid_len; memcpy(buf.Ssid, ssid, ssid_len); + /* + * Make sure radio is marked enabled here so that scan request will not + * force SSID to be changed to a random one in order to enable radio at + * that point. + */ + drv->radio_enabled = 1; return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); } @@ -562,7 +587,7 @@ static int wpa_driver_ndis_get_scan_results(void *priv, memcpy(results[i].ssid, bss->Ssid.Ssid, bss->Ssid.SsidLength); results[i].ssid_len = bss->Ssid.SsidLength; if (bss->Privacy) - results[i].caps = 1; /* FIX? */ + results[i].caps |= IEEE80211_CAP_PRIVACY; results[i].level = (int) bss->Rssi; results[i].freq = bss->Configuration.DSConfig / 1000; for (j = 0; j < sizeof(bss->SupportedRates); j++) { @@ -574,6 +599,8 @@ static int wpa_driver_ndis_get_scan_results(void *priv, } wpa_driver_ndis_get_ies(&results[i], bss->IEs, bss->IELength); pos += bss->Length; + if (pos > (char *) b + blen) + break; } free(b); @@ -928,10 +955,12 @@ static int wpa_driver_ndis_flush_pmkid(void *priv) static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv) { - char buf[512]; + char buf[512], *pos; NDIS_802_11_ASSOCIATION_INFORMATION *ai; - int len; + int len, i; union wpa_event_data data; + NDIS_802_11_BSSID_LIST_EX *b; + size_t blen; len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf, sizeof(buf)); @@ -990,8 +1019,47 @@ static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv) data.assoc_info.req_ies_len = ai->RequestIELength; data.assoc_info.resp_ies = buf + ai->OffsetResponseIEs; data.assoc_info.resp_ies_len = ai->ResponseIELength; + + blen = 65535; + b = malloc(blen); + if (b == NULL) + goto skip_scan_results; + memset(b, 0, blen); + len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); + free(b); + b = NULL; + goto skip_scan_results; + } + wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo", + (unsigned int) b->NumberOfItems); + + pos = (char *) &b->Bssid[0]; + for (i = 0; i < b->NumberOfItems; i++) { + NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; + if (memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 && + bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) { + data.assoc_info.beacon_ies = + ((u8 *) bss->IEs) + + sizeof(NDIS_802_11_FIXED_IEs); + data.assoc_info.beacon_ies_len = + bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); + wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs", + data.assoc_info.beacon_ies, + data.assoc_info.beacon_ies_len); + break; + } + pos += bss->Length; + if (pos > (char *) b + blen) + break; + } + +skip_scan_results: wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + free(b); + return 0; } @@ -1001,6 +1069,9 @@ static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx) struct wpa_driver_ndis_data *drv = eloop_ctx; u8 bssid[ETH_ALEN]; + if (drv->wired) + return; + if (wpa_driver_ndis_get_bssid(drv, bssid)) { /* Disconnected */ if (memcmp(drv->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) @@ -1339,39 +1410,63 @@ static const u8 * wpa_driver_ndis_get_mac_addr(void *priv) static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) { - PTSTR names, pos; + PTSTR names, pos, pos2; ULONG len; BOOLEAN res; - const int MAX_ADAPTERS = 16; + const int MAX_ADAPTERS = 32; char *name[MAX_ADAPTERS]; char *desc[MAX_ADAPTERS]; int num_name, num_desc, i, found_name, found_desc; size_t dlen; - len = 1024; + wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", + PacketGetVersion()); + + len = 8192; names = malloc(len); if (names == NULL) return -1; memset(names, 0, len); res = PacketGetAdapterNames(names, &len); - if (!res && len > 1024) { + if (!res && len > 8192) { free(names); names = malloc(len); if (names == NULL) return -1; memset(names, 0, len); res = PacketGetAdapterNames(names, &len); - if (!res) { - free(names); - return -1; - } + } + + if (!res) { + wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " + "(PacketGetAdapterNames)"); + free(names); + return -1; } /* wpa_hexdump_ascii(MSG_DEBUG, "NDIS: AdapterNames", names, len); */ + if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " + "UNICODE"); + /* Convert to ASCII */ + pos2 = pos = names; + while (pos2 < names + len) { + if (pos2[0] == '\0' && pos2[1] == '\0' && + pos2[2] == '\0' && pos2[3] == '\0') { + pos2 += 4; + break; + } + *pos++ = pos2[0]; + pos2 += 2; + } + memcpy(pos + 2, names, pos - names); + pos += 2; + } else + pos = names; + num_name = 0; - pos = names; while (pos < names + len) { name[num_name] = pos; while (*pos && pos < names + len) @@ -1420,6 +1515,13 @@ static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) } } + /* + * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter + * descriptions. Fill in dummy descriptors to work around this. + */ + while (num_desc < num_name) + desc[num_desc++] = "dummy description"; + if (num_name != num_desc) { wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " "description counts (%d != %d)", @@ -1537,6 +1639,13 @@ static void * wpa_driver_ndis_init(void *ctx, const char *ifname) "OID_802_11_INFRASTRUCTURE_MODE (%d)", (int) mode); /* Try to continue anyway */ + + if (!drv->has_capability && drv->capa.enc == 0) { + wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide " + "any wireless capabilities - assume it is " + "a wired interface"); + drv->wired = 1; + } } return drv; @@ -1565,7 +1674,7 @@ static void wpa_driver_ndis_deinit(void *priv) } -struct wpa_driver_ops wpa_driver_ndis_ops = { +const struct wpa_driver_ops wpa_driver_ndis_ops = { .name = "ndis", .desc = "Windows NDIS driver", .init = wpa_driver_ndis_init, diff --git a/contrib/wpa_supplicant/driver_ndis.h b/contrib/wpa_supplicant/driver_ndis.h index a79afcb..7906fdc 100644 --- a/contrib/wpa_supplicant/driver_ndis.h +++ b/contrib/wpa_supplicant/driver_ndis.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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_NDIS_H #define DRIVER_NDIS_H @@ -21,6 +35,7 @@ struct wpa_driver_ndis_data { struct ndis_pmkid_entry *pmkid; int event_sock; char *adapter_desc; + int wired; }; #endif /* DRIVER_NDIS_H */ diff --git a/contrib/wpa_supplicant/driver_wired.c b/contrib/wpa_supplicant/driver_wired.c new file mode 100644 index 0000000..359a21b --- /dev/null +++ b/contrib/wpa_supplicant/driver_wired.c @@ -0,0 +1,271 @@ +/* + * WPA Supplicant - wired Ethernet driver interface + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netpacket/packet.h> +#include <net/if.h> + +#include "common.h" +#include "driver.h" +#include "wpa_supplicant.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + +struct wpa_driver_wired_data { + void *ctx; + int pf_sock; + char ifname[IFNAMSIZ + 1]; + int membership, multi, iff_allmulti, iff_up; +}; + + +static int wpa_driver_wired_set_wpa(void *priv, int enabled) +{ + return 0; +} + + +static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + close(s); + return -1; + } + close(s); + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCSIFFLAGS]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); + + if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOC{ADD/DEL}MULTI]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int wpa_driver_wired_membership(struct wpa_driver_wired_data *drv, + const u8 *addr, int add) +{ +#ifdef __linux__ + struct packet_mreq mreq; + + if (drv->pf_sock == -1) + return -1; + + memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = if_nametoindex(drv->ifname); + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + memcpy(mreq.mr_address, addr, ETH_ALEN); + + if (setsockopt(drv->pf_sock, SOL_PACKET, + add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt"); + return -1; + } + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +static void * wpa_driver_wired_init(void *ctx, const char *ifname) +{ + struct wpa_driver_wired_data *drv; + int flags; + + drv = malloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + memset(drv, 0, sizeof(*drv)); + strncpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ctx = ctx; + +#ifdef __linux__ + drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->pf_sock < 0) + perror("socket(PF_PACKET)"); +#else + drv->pf_sock = -1; +#endif + + if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { + drv->iff_up = 1; + } + + if (wpa_driver_wired_membership(drv, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "packet socket", __func__); + drv->membership = 1; + } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "SIOCADDMULTI", __func__); + drv->multi = 1; + } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) { + wpa_printf(MSG_INFO, "%s: Could not get interface " + "flags", __func__); + free(drv); + return NULL; + } else if (flags & IFF_ALLMULTI) { + wpa_printf(MSG_DEBUG, "%s: Interface is already configured " + "for multicast", __func__); + } else if (wpa_driver_wired_set_ifflags(ifname, + flags | IFF_ALLMULTI) < 0) { + wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", + __func__); + free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", + __func__); + drv->iff_allmulti = 1; + } + + return drv; +} + + +static void wpa_driver_wired_deinit(void *priv) +{ + struct wpa_driver_wired_data *drv = priv; + int flags; + + if (drv->membership && + wpa_driver_wired_membership(drv, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (PACKET)", __func__); + } + + if (drv->multi && + wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (SIOCDELMULTI)", __func__); + } + + if (drv->iff_allmulti && + (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 || + wpa_driver_wired_set_ifflags(drv->ifname, + flags & ~IFF_ALLMULTI) < 0)) { + wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", + __func__); + } + + if (drv->iff_up && + wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 && + (flags & IFF_UP) && + wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", + __func__); + } + + if (drv->pf_sock != -1) + close(drv->pf_sock); + + free(drv); +} + + +const struct wpa_driver_ops wpa_driver_wired_ops = { + .name = "wired", + .desc = "wpa_supplicant wired Ethernet driver", + .set_wpa = wpa_driver_wired_set_wpa, + .get_ssid = wpa_driver_wired_get_ssid, + .get_bssid = wpa_driver_wired_get_bssid, + .init = wpa_driver_wired_init, + .deinit = wpa_driver_wired_deinit, +}; diff --git a/contrib/wpa_supplicant/drivers.c b/contrib/wpa_supplicant/drivers.c index cb016ba..4fd0509 100644 --- a/contrib/wpa_supplicant/drivers.c +++ b/contrib/wpa_supplicant/drivers.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / driver interface list - * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -49,6 +49,9 @@ extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #ifdef CONFIG_DRIVER_NDIS extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ #endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_WIRED +extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ +#endif /* CONFIG_DRIVER_WIRED */ #ifdef CONFIG_DRIVER_TEST extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ #endif /* CONFIG_DRIVER_TEST */ @@ -89,6 +92,9 @@ struct wpa_driver_ops *wpa_supplicant_drivers[] = #ifdef CONFIG_DRIVER_NDIS &wpa_driver_ndis_ops, #endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_WIRED + &wpa_driver_wired_ops, +#endif /* CONFIG_DRIVER_WIRED */ #ifdef CONFIG_DRIVER_TEST &wpa_driver_test_ops, #endif /* CONFIG_DRIVER_TEST */ diff --git a/contrib/wpa_supplicant/eap.c b/contrib/wpa_supplicant/eap.c index 267907c..5d81870 100644 --- a/contrib/wpa_supplicant/eap.c +++ b/contrib/wpa_supplicant/eap.c @@ -1,5 +1,5 @@ /* - * WPA Supplicant / EAP state machines + * WPA Supplicant / EAP state machines (RFC 4137) * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -15,13 +15,16 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <ctype.h> #include "common.h" #include "eap_i.h" #include "wpa_supplicant.h" #include "config_ssid.h" #include "tls.h" -#include "md5.h" +#include "crypto.h" +#include "pcsc_funcs.h" +#include "wpa_ctrl.h" #define EAP_MAX_AUTH_ROUNDS 50 @@ -63,6 +66,9 @@ extern const struct eap_method eap_method_aka; #ifdef EAP_FAST extern const struct eap_method eap_method_fast; #endif +#ifdef EAP_PAX +extern const struct eap_method eap_method_pax; +#endif static const struct eap_method *eap_methods[] = { @@ -102,10 +108,18 @@ static const struct eap_method *eap_methods[] = #ifdef EAP_FAST &eap_method_fast, #endif +#ifdef EAP_PAX + &eap_method_pax, +#endif }; #define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0])) +/** + * eap_sm_get_eap_methods - Get EAP method based on type number + * @method: EAP type number + * Returns: Pointer to EAP method of %NULL if not found + */ const struct eap_method * eap_sm_get_eap_methods(int method) { int i; @@ -119,10 +133,11 @@ const struct eap_method * eap_sm_get_eap_methods(int method) static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method); static u8 * eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len); -static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len); -static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len); +static void eap_sm_processIdentity(struct eap_sm *sm, const u8 *req, + size_t len); +static void eap_sm_processNotify(struct eap_sm *sm, const u8 *req, size_t len); static u8 * eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len); -static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len); +static void eap_sm_parseEapReq(struct eap_sm *sm, const u8 *req, size_t len); static const char * eap_sm_method_state_txt(int state); static const char * eap_sm_decision_txt(int decision); @@ -219,12 +234,14 @@ SM_STATE(EAP, INITIALIZE) eapol_set_bool(sm, EAPOL_eapRestart, FALSE); sm->lastId = -1; /* new session - make sure this does not match with * the first EAP-Packet */ - /* draft-ietf-eap-statemachine-02.pdf does not reset eapResp and - * eapNoResp here. However, this seemed to be able to trigger cases - * where both were set and if EAPOL state machine uses eapNoResp first, - * it may end up not sending a real reply correctly. This occurred - * when the workaround in FAIL state set eapNoResp = TRUE.. Maybe that - * workaround needs to be fixed to do something else(?) */ + /* + * RFC 4137 does not reset eapResp and eapNoResp here. However, this + * seemed to be able to trigger cases where both were set and if EAPOL + * state machine uses eapNoResp first, it may end up not sending a real + * reply correctly. This occurred when the workaround in FAIL state set + * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do + * something else(?) + */ eapol_set_bool(sm, EAPOL_eapResp, FALSE); eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); sm->num_rounds = 0; @@ -246,7 +263,7 @@ SM_STATE(EAP, IDLE) SM_STATE(EAP, RECEIVED) { - u8 *eapReqData; + const u8 *eapReqData; size_t eapReqDataLen; SM_ENTRY(EAP, RECEIVED); @@ -284,14 +301,35 @@ SM_STATE(EAP, GET_METHOD) else sm->eap_method_priv = sm->m->init(sm); if (sm->eap_method_priv == NULL) { - wpa_printf(MSG_DEBUG, "EAP: Failed to " - "initialize EAP method %d", - sm->selectedMethod); + struct wpa_ssid *config = eap_get_config(sm); + wpa_msg(sm->msg_ctx, MSG_INFO, + "EAP: Failed to initialize EAP method " + "%d (%s)", + sm->selectedMethod, sm->m->name); sm->m = NULL; sm->methodState = METHOD_NONE; sm->selectedMethod = EAP_TYPE_NONE; + if (sm->reqMethod == EAP_TYPE_TLS && + config && + (config->pending_req_pin || + config->pending_req_passphrase)) { + /* + * Return without generating Nak in + * order to allow entering of PIN code + * or passphrase to retry the current + * EAP packet. + */ + wpa_printf(MSG_DEBUG, "EAP: Pending " + "PIN/passphrase request - " + "skip Nak"); + return; + } } else { sm->methodState = METHOD_INIT; + wpa_msg(sm->msg_ctx, MSG_INFO, + WPA_EVENT_EAP_METHOD + "EAP method %d (%s) selected", + sm->selectedMethod, sm->m->name); return; } } @@ -381,7 +419,7 @@ SM_STATE(EAP, DISCARD) SM_STATE(EAP, IDENTITY) { - u8 *eapReqData; + const u8 *eapReqData; size_t eapReqDataLen; SM_ENTRY(EAP, IDENTITY); @@ -395,7 +433,7 @@ SM_STATE(EAP, IDENTITY) SM_STATE(EAP, NOTIFICATION) { - u8 *eapReqData; + const u8 *eapReqData; size_t eapReqDataLen; SM_ENTRY(EAP, NOTIFICATION); @@ -429,15 +467,24 @@ SM_STATE(EAP, SUCCESS) if (sm->eapKeyData != NULL) sm->eapKeyAvailable = TRUE; eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); - /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but - * this seems to be required to avoid processing the same request - * twice when state machine is initialized. */ + + /* + * RFC 4137 does not clear eapReq here, but this seems to be required + * to avoid processing the same request twice when state machine is + * initialized. + */ eapol_set_bool(sm, EAPOL_eapReq, FALSE); - /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here, but - * this seems to be required to get EAPOL Supplicant backend state - * machine into SUCCESS state. In addition, either eapResp or eapNoResp - * is required to be set after processing the received EAP frame. */ + + /* + * RFC 4137 does not set eapNoResp here, but this seems to be required + * to get EAPOL Supplicant backend state machine into SUCCESS state. In + * addition, either eapResp or eapNoResp is required to be set after + * processing the received EAP frame. + */ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP authentication completed successfully"); } @@ -445,14 +492,23 @@ SM_STATE(EAP, FAILURE) { SM_ENTRY(EAP, FAILURE); eapol_set_bool(sm, EAPOL_eapFail, TRUE); - /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but - * this seems to be required to avoid processing the same request - * twice when state machine is initialized. */ + + /* + * RFC 4137 does not clear eapReq here, but this seems to be required + * to avoid processing the same request twice when state machine is + * initialized. + */ eapol_set_bool(sm, EAPOL_eapReq, FALSE); - /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here. - * However, either eapResp or eapNoResp is required to be set after - * processing the received EAP frame. */ + + /* + * RFC 4137 does not set eapNoResp here. However, either eapResp or + * eapNoResp is required to be set after processing the received EAP + * frame. + */ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + "EAP authentication failed"); } @@ -461,9 +517,8 @@ static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) /* * At least Microsoft IAS and Meetinghouse Aegis seem to be sending * EAP-Success/Failure with lastId + 1 even though RFC 3748 and - * draft-ietf-eap-statemachine-05.pdf require that reqId == lastId. - * In addition, it looks like Ringmaster v2.1.2.0 would be using - * lastId + 2 in EAP-Success. + * RFC 4137 require that reqId == lastId. In addition, it looks like + * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success. * * Accept this kind of Id if EAP workarounds are enabled. These are * unauthenticated plaintext messages, so this should have minimal @@ -490,13 +545,13 @@ SM_STEP(EAP) if (eapol_get_bool(sm, EAPOL_eapRestart) && eapol_get_bool(sm, EAPOL_portEnabled)) SM_ENTER_GLOBAL(EAP, INITIALIZE); - else if (!eapol_get_bool(sm, EAPOL_portEnabled)) + else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled) SM_ENTER_GLOBAL(EAP, DISABLED); else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { - wpa_printf(MSG_DEBUG, "EAP: more than %d " - "authentication rounds - abort", - EAP_MAX_AUTH_ROUNDS); + wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d " + "authentication rounds - abort", + EAP_MAX_AUTH_ROUNDS); sm->num_rounds++; SM_ENTER_GLOBAL(EAP, FAILURE); } @@ -505,7 +560,8 @@ SM_STEP(EAP) SM_ENTER(EAP, IDLE); break; case EAP_DISABLED: - if (eapol_get_bool(sm, EAPOL_portEnabled)) + if (eapol_get_bool(sm, EAPOL_portEnabled) && + !sm->force_disabled) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: @@ -533,16 +589,18 @@ SM_STEP(EAP) SM_ENTER(EAP, SUCCESS); break; case EAP_RECEIVED: - duplicate = sm->reqId == sm->lastId; + duplicate = (sm->reqId == sm->lastId) && sm->rxReq; if (sm->workaround && duplicate && memcmp(sm->req_md5, sm->last_md5, 16) != 0) { - /* draft-ietf-eap-statemachine-05.txt uses - * (reqId == lastId) as the only verification for - * duplicate EAP requests. However, this misses cases - * where the AS is incorrectly using the same id again; - * and unfortunately, such implementations exist. Use - * MD5 hash as an extra verification for the packets - * being duplicate to workaround these issues. */ + /* + * RFC 4137 uses (reqId == lastId) as the only + * verification for duplicate EAP requests. However, + * this misses cases where the AS is incorrectly using + * the same id again; and unfortunately, such + * implementations exist. Use MD5 hash as an extra + * verification for the packets being duplicate to + * workaround these issues. + */ wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again," " but EAP packets were not identical"); wpa_printf(MSG_DEBUG, "EAP: workaround - assume this " @@ -558,7 +616,9 @@ SM_STEP(EAP) else if (sm->methodState != METHOD_CONT && ((sm->rxFailure && sm->decision != DECISION_UNCOND_SUCC) || - (sm->rxSuccess && sm->decision == DECISION_FAIL)) && + (sm->rxSuccess && sm->decision == DECISION_FAIL && + (sm->selectedMethod != EAP_TYPE_LEAP || + sm->methodState != METHOD_MAY_CONT))) && (sm->reqId == sm->lastId || eap_success_workaround(sm, sm->reqId, sm->lastId))) SM_ENTER(EAP, FAILURE); @@ -657,7 +717,8 @@ static u8 *eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len) *pos++ = EAP_TYPE_NAK; for (i = 0; i < NUM_EAP_METHODS; i++) { - if (wpa_config_allowed_eap_method(config, + if (eap_methods[i]->method != sm->reqMethod && + wpa_config_allowed_eap_method(config, eap_methods[i]->method)) { *pos++ = eap_methods[i]->method; (*len)++; @@ -677,11 +738,16 @@ static u8 *eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len) } -static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len) +static void eap_sm_processIdentity(struct eap_sm *sm, const u8 *req, + size_t len) { - struct eap_hdr *hdr = (struct eap_hdr *) req; - u8 *pos = (u8 *) (hdr + 1); + const struct eap_hdr *hdr = (const struct eap_hdr *) req; + const u8 *pos = (const u8 *) (hdr + 1); pos++; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED + "EAP authentication started"); + /* TODO: could save displayable message so that it can be shown to the * user in case of interaction is required */ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", @@ -689,8 +755,77 @@ static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len) } -u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, - int encrypted) +static int eap_sm_imsi_identity(struct eap_sm *sm, struct wpa_ssid *ssid) +{ + int aka = 0; + char imsi[100]; + size_t imsi_len; + u8 *pos = ssid->eap_methods; + + imsi_len = sizeof(imsi); + if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { + wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); + + while (pos && *pos != EAP_TYPE_NONE) { + if (*pos == EAP_TYPE_AKA) { + aka = 1; + break; + } + pos++; + } + + free(ssid->identity); + ssid->identity = malloc(1 + imsi_len); + if (ssid->identity == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate buffer for " + "IMSI-based identity"); + return -1; + } + + ssid->identity[0] = aka ? '0' : '1'; + memcpy(ssid->identity + 1, imsi, imsi_len); + ssid->identity_len = 1 + imsi_len; + return 0; +} + + +static int eap_sm_get_scard_identity(struct eap_sm *sm, struct wpa_ssid *ssid) +{ + if (scard_set_pin(sm->scard_ctx, ssid->pin)) { + /* + * Make sure the same PIN is not tried again in order to avoid + * blocking SIM. + */ + free(ssid->pin); + ssid->pin = NULL; + + wpa_printf(MSG_WARNING, "PIN validation failed"); + eap_sm_request_pin(sm, ssid); + return -1; + } + + return eap_sm_imsi_identity(sm, ssid); +} + + +/** + * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @id: EAP identifier for the packet + * @len: Pointer to variable that will be set to the length of the response + * @encrypted: Whether the packet is for enrypted tunnel (EAP phase 2) + * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on + * failure + * + * This function allocates and builds an EAP-Identity/Response packet for the + * current network. The caller is responsible for freeing the returned data. + */ +u8 * eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, + int encrypted) { struct wpa_ssid *config = eap_get_config(sm); struct eap_hdr *resp; @@ -724,8 +859,17 @@ u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, if (identity == NULL) { wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " "configuration was not available"); - eap_sm_request_identity(sm, config); - return NULL; + if (config->pcsc) { + if (eap_sm_get_scard_identity(sm, config) < 0) + return NULL; + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " + "IMSI", identity, identity_len); + } else { + eap_sm_request_identity(sm, config); + return NULL; + } } @@ -745,14 +889,33 @@ u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, } -static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len) +static void eap_sm_processNotify(struct eap_sm *sm, const u8 *req, size_t len) { - struct eap_hdr *hdr = (struct eap_hdr *) req; - u8 *pos = (u8 *) (hdr + 1); + const struct eap_hdr *hdr = (const struct eap_hdr *) req; + const u8 *pos; + char *msg; + size_t msg_len; + int i; + + pos = (const u8 *) (hdr + 1); pos++; - /* TODO: log the Notification Request and make it available for UI */ + + msg_len = be_to_host16(hdr->length); + if (msg_len < 5) + return; + msg_len -= 5; wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", - pos, be_to_host16(hdr->length) - 5); + pos, msg_len); + + msg = malloc(msg_len + 1); + if (msg == NULL) + return; + for (i = 0; i < msg_len; i++) + msg[i] = isprint(pos[i]) ? (char) pos[i] : '_'; + msg[msg_len] = '\0'; + wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s", + WPA_EVENT_EAP_NOTIFICATION, msg); + free(msg); } @@ -777,11 +940,10 @@ static u8 *eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len) } -static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len) +static void eap_sm_parseEapReq(struct eap_sm *sm, const u8 *req, size_t len) { - struct eap_hdr *hdr; + const struct eap_hdr *hdr; size_t plen; - MD5_CTX context; sm->rxReq = sm->rxSuccess = sm->rxFailure = FALSE; sm->reqId = 0; @@ -790,7 +952,7 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len) if (req == NULL || len < sizeof(*hdr)) return; - hdr = (struct eap_hdr *) req; + hdr = (const struct eap_hdr *) req; plen = be_to_host16(hdr->length); if (plen > len) { wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " @@ -802,9 +964,7 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len) sm->reqId = hdr->identifier; if (sm->workaround) { - MD5Init(&context); - MD5Update(&context, req, len); - MD5Final(sm->req_md5, &context); + md5_vector(1, (const u8 **) &req, &len, sm->req_md5); } switch (hdr->code) { @@ -843,10 +1003,22 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len) } -struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, - void *msg_ctx) +/** + * eap_sm_init - Allocate and initialize EAP state machine + * @eapol_ctx: Context data to be used with eapol_cb calls + * @eapol_cb: Pointer to EAPOL callback functions + * @msg_ctx: Context data for wpa_msg() calls + * @conf: EAP configuration + * Returns: Pointer to the allocated EAP state machine or %NULL on failure + * + * This function allocates and initializes an EAP state machine. In addition, + * this initializes TLS library for the new EAP state machine. + */ +struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx, struct eap_config *conf) { struct eap_sm *sm; + struct tls_config tlsconf; sm = malloc(sizeof(*sm)); if (sm == NULL) @@ -857,7 +1029,11 @@ struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, sm->msg_ctx = msg_ctx; sm->ClientTimeout = 60; - sm->ssl_ctx = tls_init(); + memset(&tlsconf, 0, sizeof(tlsconf)); + tlsconf.opensc_engine_path = conf->opensc_engine_path; + tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path; + tlsconf.pkcs11_module_path = conf->pkcs11_module_path; + sm->ssl_ctx = tls_init(&tlsconf); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " "context."); @@ -869,6 +1045,13 @@ struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, } +/** + * eap_sm_deinit - Deinitialize and free an EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * This function deinitializes EAP state machine and frees all allocated + * resources. + */ void eap_sm_deinit(struct eap_sm *sm) { if (sm == NULL) @@ -882,6 +1065,15 @@ void eap_sm_deinit(struct eap_sm *sm) } +/** + * eap_sm_step - Step EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * Returns: 1 if EAP state was changed or 0 if not + * + * This function advances EAP state machine to a new state to match with the + * current variables. This should be called whenever variables used by the EAP + * state machine have changed. + */ int eap_sm_step(struct eap_sm *sm) { int res = 0; @@ -895,10 +1087,15 @@ int eap_sm_step(struct eap_sm *sm) } +/** + * eap_sm_abort - Abort EAP authentication + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * Release system resources that have been allocated for the authentication + * session without fully deinitializing the EAP state machine. + */ void eap_sm_abort(struct eap_sm *sm) { - /* release system resources that may have been allocated for the - * authentication session */ free(sm->eapRespData); sm->eapRespData = NULL; free(sm->eapKeyData); @@ -975,6 +1172,19 @@ static const char * eap_sm_decision_txt(int decision) } +/** + * eap_sm_get_status - Get EAP state machine status + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @buf: buffer for status information + * @buflen: maximum buffer length + * @verbose: whether to include verbose status information + * Returns: number of bytes written to buf. + * + * Query EAP state machine for status information. This function fills in a + * text area with current status information from the EAPOL state machine. If + * the buffer (buf) is not large enough, status information will be truncated + * to fit the buffer. + */ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) { int len; @@ -1025,10 +1235,14 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) } -typedef enum { TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP } eap_ctrl_req_type; +typedef enum { + TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD, + TYPE_PASSPHRASE +} eap_ctrl_req_type; static void eap_sm_request(struct eap_sm *sm, struct wpa_ssid *config, - eap_ctrl_req_type type, char *msg, size_t msglen) + eap_ctrl_req_type type, const char *msg, + size_t msglen) { char *buf; size_t buflen; @@ -1050,6 +1264,16 @@ static void eap_sm_request(struct eap_sm *sm, struct wpa_ssid *config, txt = "Password"; config->pending_req_password++; break; + case TYPE_NEW_PASSWORD: + field = "NEW_PASSWORD"; + txt = "New Password"; + config->pending_req_new_password++; + break; + case TYPE_PIN: + field = "PIN"; + txt = "PIN"; + config->pending_req_pin++; + break; case TYPE_OTP: field = "OTP"; if (msg) { @@ -1070,6 +1294,11 @@ static void eap_sm_request(struct eap_sm *sm, struct wpa_ssid *config, txt = config->pending_req_otp; } break; + case TYPE_PASSPHRASE: + field = "PASSPHRASE"; + txt = "Private key passphrase"; + config->pending_req_passphrase++; + break; default: return; } @@ -1078,37 +1307,123 @@ static void eap_sm_request(struct eap_sm *sm, struct wpa_ssid *config, buf = malloc(buflen); if (buf == NULL) return; - len = snprintf(buf, buflen, "CTRL-REQ-%s-%d:%s needed for SSID ", + len = snprintf(buf, buflen, WPA_CTRL_REQ "%s-%d:%s needed for SSID ", field, config->id, txt); if (config->ssid && buflen > len + config->ssid_len) { memcpy(buf + len, config->ssid, config->ssid_len); len += config->ssid_len; buf[len] = '\0'; } - wpa_msg(sm->msg_ctx, MSG_INFO, buf); + wpa_msg(sm->msg_ctx, MSG_INFO, "%s", buf); free(buf); } +/** + * eap_sm_request_identity - Request identity from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @config: Pointer to the current network configuration + * + * EAP methods can call this function to request identity information for the + * current network. This is normally called when the identity is not included + * in the network configuration. The request will be sent to monitor programs + * through the control interface. + */ void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config) { eap_sm_request(sm, config, TYPE_IDENTITY, NULL, 0); } +/** + * eap_sm_request_password - Request password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @config: Pointer to the current network configuration + * + * EAP methods can call this function to request password information for the + * current network. This is normally called when the password is not included + * in the network configuration. The request will be sent to monitor programs + * through the control interface. + */ void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config) { eap_sm_request(sm, config, TYPE_PASSWORD, NULL, 0); } +/** + * eap_sm_request_new_password - Request new password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @config: Pointer to the current network configuration + * + * EAP methods can call this function to request new password information for + * the current network. This is normally called when the EAP method indicates + * that the current password has expired and password change is required. The + * request will be sent to monitor programs through the control interface. + */ +void eap_sm_request_new_password(struct eap_sm *sm, struct wpa_ssid *config) +{ + eap_sm_request(sm, config, TYPE_NEW_PASSWORD, NULL, 0); +} + + +/** + * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @config: Pointer to the current network configuration + * + * EAP methods can call this function to request SIM or smart card PIN + * information for the current network. This is normally called when the PIN is + * not included in the network configuration. The request will be sent to + * monitor programs through the control interface. + */ +void eap_sm_request_pin(struct eap_sm *sm, struct wpa_ssid *config) +{ + eap_sm_request(sm, config, TYPE_PIN, NULL, 0); +} + + +/** + * eap_sm_request_otp - Request one time password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @config: Pointer to the current network configuration + * @msg: Message to be displayed to the user when asking for OTP + * @msg_len: Length of the user displayable message + * + * EAP methods can call this function to request open time password (OTP) for + * the current network. The request will be sent to monitor programs through + * the control interface. + */ void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config, - char *msg, size_t msg_len) + const char *msg, size_t msg_len) { eap_sm_request(sm, config, TYPE_OTP, msg, msg_len); } +/** + * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @config: Pointer to the current network configuration + * + * EAP methods can call this function to request passphrase for a private key + * for the current network. This is normally called when the passphrase is not + * included in the network configuration. The request will be sent to monitor + * programs through the control interface. + */ +void eap_sm_request_passphrase(struct eap_sm *sm, struct wpa_ssid *config) +{ + eap_sm_request(sm, config, TYPE_PASSPHRASE, NULL, 0); +} + + +/** + * eap_sm_notify_ctrl_attached - Notification of attached monitor + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * Notify EAP state machines that a monitor was attached to the control + * interface to trigger re-sending of pending requests for user input. + */ void eap_sm_notify_ctrl_attached(struct eap_sm *sm) { struct wpa_ssid *config = eap_get_config(sm); @@ -1124,11 +1439,25 @@ void eap_sm_notify_ctrl_attached(struct eap_sm *sm) eap_sm_request_identity(sm, config); if (config->pending_req_password) eap_sm_request_password(sm, config); + if (config->pending_req_new_password) + eap_sm_request_new_password(sm, config); if (config->pending_req_otp) eap_sm_request_otp(sm, config, NULL, 0); + if (config->pending_req_pin) + eap_sm_request_pin(sm, config); + if (config->pending_req_passphrase) + eap_sm_request_passphrase(sm, config); } +/** + * eap_get_type - Get EAP type for the given EAP method name + * @name: EAP method name, e.g., TLS + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers based on the list of + * EAP methods included in the build. + */ u8 eap_get_type(const char *name) { int i; @@ -1140,6 +1469,49 @@ u8 eap_get_type(const char *name) } +/** + * eap_get_name - Get EAP method name for the given EAP type + * @type: EAP method type + * Returns: EAP method name, e.g., TLS, or %NULL if not found + * + * This function maps EAP type numbers into EAP type names based on the list of + * EAP methods included in the build. + */ +const char * eap_get_name(EapType type) +{ + int i; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (eap_methods[i]->method == type) + return eap_methods[i]->name; + } + return NULL; +} + + +/** + * eap_get_names - Get space separated list of names for supported EAP methods + * @buf: Buffer for names + * @buflen: Buffer length + * Returns: Number of characters written into buf (not including nul + * termination) + */ +size_t eap_get_names(char *buf, size_t buflen) +{ + char *pos, *end; + int i; + + pos = buf; + end = pos + buflen; + + for (i = 0; i < NUM_EAP_METHODS; i++) { + pos += snprintf(pos, end - pos, "%s%s", + i == 0 ? "" : " ", eap_methods[i]->name); + } + + return pos - buf; +} + + static int eap_allowed_phase2_type(int type) { return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && @@ -1147,6 +1519,15 @@ static int eap_allowed_phase2_type(int type) } +/** + * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name + * @name: EAP method name, e.g., MD5 + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers that are allowed for + * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with + * EAP-PEAP, EAP-TTLS, and EAP-FAST. + */ u8 eap_get_phase2_type(const char *name) { u8 type = eap_get_type(name); @@ -1156,6 +1537,15 @@ u8 eap_get_phase2_type(const char *name) } +/** + * eap_get_phase2_types - Get list of allowed EAP phase 2 types + * @config: Pointer to a network configuration + * @count: Pointer to variable filled with number of returned EAP types + * Returns: Pointer to allocated type list or %NULL on failure + * + * This function generates an array of allowed EAP phase 2 (tunneled) types for + * the given network configuration. + */ u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count) { u8 *buf, method; @@ -1181,30 +1571,59 @@ u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count) } +/** + * eap_set_fast_reauth - Update fast_reauth setting + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @enabled: 1 = fast reauthentication is enabled, 0 = disabled + */ void eap_set_fast_reauth(struct eap_sm *sm, int enabled) { sm->fast_reauth = enabled; } +/** + * eap_set_workaround - Update EAP workarounds setting + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds + */ void eap_set_workaround(struct eap_sm *sm, unsigned int workaround) { sm->workaround = workaround; } +/** + * eap_get_config - Get current network configuration + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * Returns: Pointer to the current network configuration or %NULL if not found + */ struct wpa_ssid * eap_get_config(struct eap_sm *sm) { return sm->eapol_cb->get_config(sm->eapol_ctx); } +/** + * eap_key_available - Get key availability (eapKeyAvailable variable) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * Returns: 1 if EAP keying material is available, 0 if not + */ int eap_key_available(struct eap_sm *sm) { return sm ? sm->eapKeyAvailable : 0; } +/** + * eap_notify_success - Notify EAP state machine about external success trigger + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * This function is called when external event, e.g., successful completion of + * WPA-PSK key handshake, is indicating that EAP state machine should move to + * success state. This is mainly used with security modes that do not use EAP + * state machine (e.g., WPA-PSK). + */ void eap_notify_success(struct eap_sm *sm) { if (sm) { @@ -1212,9 +1631,45 @@ void eap_notify_success(struct eap_sm *sm) sm->EAP_state = EAP_SUCCESS; } } +/** + * eap_notify_lower_layer_success - Notification of lower layer success + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * Notify EAP state machines that a lower layer has detected a successful + * authentication. This is used to recover from dropped EAP-Success messages. + */ +void eap_notify_lower_layer_success(struct eap_sm *sm) +{ + if (sm == NULL) + return; + if (eapol_get_bool(sm, EAPOL_eapSuccess) || + sm->decision == DECISION_FAIL || + (sm->methodState != METHOD_MAY_CONT && + sm->methodState != METHOD_DONE)) + return; -u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP authentication completed successfully (based on lower " + "layer success)"); +} + + +/** + * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the key + * Returns: Pointer to the EAP keying data or %NULL on failure + * + * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The + * key is available only after a successful authentication. EAP state machine + * continues to manage the key data and the caller must not change or free the + * returned data. + */ +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) { if (sm == NULL || sm->eapKeyData == NULL) { *len = 0; @@ -1226,6 +1681,17 @@ u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) } +/** + * eap_get_eapKeyData - Get EAP response data + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @len: Pointer to variable that will be set to the length of the response + * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure + * + * Fetch EAP response (eapRespData) from the EAP state machine. This data is + * available when EAP state machine has processed an incoming EAP request. The + * EAP state machine does not maintain a reference to the response after this + * function is called and the caller is responsible for freeing the data. + */ u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len) { u8 *resp; @@ -1244,8 +1710,91 @@ u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len) } +/** + * eap_sm_register_scard_ctx - Notification of smart card context + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @ctx: context data for smart card operations + * + * Notify EAP state machines of context data for smart card operations. This + * context data will be used as a parameter for scard_*() functions. + */ void eap_register_scard_ctx(struct eap_sm *sm, void *ctx) { if (sm) sm->scard_ctx = ctx; } + + +/** + * eap_hdr_validate - Validate EAP header + * @eap_type: Expected EAP type number + * @msg: EAP frame (starting with EAP header) + * @msglen: Length of msg + * @plen: Pointer for return payload length + * Returns: Pointer to EAP payload (after type field), or %NULL on failure + * + * This is a helper function for EAP method implementations. This is usually + * called in the beginning of struct eap_method::process() function. + */ +const u8 * eap_hdr_validate(EapType eap_type, const u8 *msg, size_t msglen, + size_t *plen) +{ + const struct eap_hdr *hdr; + const u8 *pos; + size_t len; + + hdr = (const struct eap_hdr *) msg; + pos = (const u8 *) (hdr + 1); + if (msglen < sizeof(*hdr) + 1 || *pos != eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid frame type"); + return NULL; + } + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + 1 || len > msglen) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return NULL; + } + *plen = len - sizeof(*hdr) - 1; + return pos + 1; +} + + +/** + * eap_set_config_blob - Set or add a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an existing + * blob. + */ +void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob) +{ + sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob); +} + + +/** + * eap_get_config_blob - Get a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ +const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, + const char *name) +{ + return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name); +} + + +/** + * eap_set_force_disabled - Set force_disabled flag + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @disabled: 1 = EAP disabled, 0 = EAP enabled + * + * This function is used to force EAP state machine to be disabled when it is + * not in use (e.g., with WPA-PSK or plaintext connections). + */ +void eap_set_force_disabled(struct eap_sm *sm, int disabled) +{ + sm->force_disabled = disabled; +} diff --git a/contrib/wpa_supplicant/eap.h b/contrib/wpa_supplicant/eap.h index 3d7cc72..e00e29b 100644 --- a/contrib/wpa_supplicant/eap.h +++ b/contrib/wpa_supplicant/eap.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / EAP state machine functions (RFC 4137) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_H #define EAP_H @@ -6,55 +20,237 @@ struct eap_sm; struct wpa_ssid; +struct wpa_config_blob; #ifdef IEEE8021X_EAPOL +/** + * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine + * + * These variables are used in the interface between EAP peer state machine and + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is + * expected to maintain these variables and register a callback functions for + * EAP state machine to get and set the variables. + */ enum eapol_bool_var { - EAPOL_eapSuccess, EAPOL_eapRestart, EAPOL_eapFail, EAPOL_eapResp, - EAPOL_eapNoResp, EAPOL_eapReq, EAPOL_portEnabled, EAPOL_altAccept, + /** + * EAPOL_eapSuccess - EAP SUCCESS state reached + * + * EAP state machine reads and writes this value. + */ + EAPOL_eapSuccess, + + /** + * EAPOL_eapRestart - Lower layer request to restart authentication + * + * Set to TRUE in lower layer, FALSE in EAP state machine. + */ + EAPOL_eapRestart, + + /** + * EAPOL_eapFail - EAP FAILURE state reached + * + * EAP state machine writes this value. + */ + EAPOL_eapFail, + + /** + * EAPOL_eapResp - Response to send + * + * Set to TRUE in EAP state machine, FALSE in lower layer. + */ + EAPOL_eapResp, + + /** + * EAPOL_eapNoResp - Request has been process; no response to send + * + * Set to TRUE in EAP state machine, FALSE in lower layer. + */ + EAPOL_eapNoResp, + + /** + * EAPOL_eapReq - EAP request available from lower layer + * + * Set to TRUE in lower layer, FALSE in EAP state machine. + */ + EAPOL_eapReq, + + /** + * EAPOL_portEnabled - Lower layer is ready for communication + * + * EAP state machines reads this value. + */ + EAPOL_portEnabled, + + /** + * EAPOL_altAccept - Alternate indication of success (RFC3748) + * + * EAP state machines reads this value. + */ + EAPOL_altAccept, + + /** + * EAPOL_altReject - Alternate indication of failure (RFC3748) + * + * EAP state machines reads this value. + */ EAPOL_altReject }; +/** + * enum eapol_int_var - EAPOL integer state variables for EAP state machine + * + * These variables are used in the interface between EAP peer state machine and + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is + * expected to maintain these variables and register a callback functions for + * EAP state machine to get and set the variables. + */ enum eapol_int_var { + /** + * EAPOL_idleWhile - Outside time for EAP peer timeout + * + * This integer variable is used to provide an outside timer that the + * external (to EAP state machine) code must decrement by one every + * second until the value reaches zero. This is used in the same way as + * EAPOL state machine timers. EAP state machine reads and writes this + * value. + */ EAPOL_idleWhile }; +/** + * struct eapol_callbacks - Callback functions from EAP to lower layer + * + * This structure defines the callback functions that EAP state machine + * requires from the lower layer (usually EAPOL state machine) for updating + * state variables and requesting information. eapol_ctx from eap_sm_init() + * call will be used as the ctx parameter for these callback functions. + */ struct eapol_callbacks { + /** + * get_config - Get pointer to the current network configuration + * @ctx: eapol_ctx from eap_sm_init() call + */ struct wpa_ssid * (*get_config)(void *ctx); + + /** + * get_bool - Get a boolean EAPOL state variable + * @variable: EAPOL boolean variable to get + * Returns: Value of the EAPOL variable + */ Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); + + /** + * set_bool - Set a boolean EAPOL state variable + * @ctx: eapol_ctx from eap_sm_init() call + * @variable: EAPOL boolean variable to set + * @value: Value for the EAPOL variable + */ void (*set_bool)(void *ctx, enum eapol_bool_var variable, Boolean value); + + /** + * get_int - Get an integer EAPOL state variable + * @ctx: eapol_ctx from eap_sm_init() call + * @variable: EAPOL integer variable to get + * Returns: Value of the EAPOL variable + */ unsigned int (*get_int)(void *ctx, enum eapol_int_var variable); + + /** + * set_int - Set an integer EAPOL state variable + * @ctx: eapol_ctx from eap_sm_init() call + * @variable: EAPOL integer variable to set + * @value: Value for the EAPOL variable + */ void (*set_int)(void *ctx, enum eapol_int_var variable, unsigned int value); + + /** + * get_eapReqData - Get EAP-Request data + * @ctx: eapol_ctx from eap_sm_init() call + * @len: Pointer to variable that will be set to eapReqDataLen + * Returns: Reference to eapReqData (EAP state machine will not free + * this) or %NULL if eapReqData not available. + */ u8 * (*get_eapReqData)(void *ctx, size_t *len); + + /** + * set_config_blob - Set named configuration blob + * @ctx: eapol_ctx from eap_sm_init() call + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an + * existing blob. + */ + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + + /** + * get_config_blob - Get a named configuration blob + * @ctx: eapol_ctx from eap_sm_init() call + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); +}; + +/** + * struct eap_config - Configuration for EAP state machine + */ +struct eap_config { + /** + * opensc_engine_path - OpenSC engine for OpenSSL engine support + * + * Usually, path to engine_opensc.so. + */ + const char *opensc_engine_path; + /** + * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support + * + * Usually, path to engine_pkcs11.so. + */ + const char *pkcs11_engine_path; + /** + * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine + * + * Usually, path to opensc-pkcs11.so. + */ + const char *pkcs11_module_path; }; -struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, - void *msg_ctx); +struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx, struct eap_config *conf); void eap_sm_deinit(struct eap_sm *sm); int eap_sm_step(struct eap_sm *sm); void eap_sm_abort(struct eap_sm *sm); int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose); -u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, - int encrypted); +u8 * eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, + int encrypted); const struct eap_method * eap_sm_get_eap_methods(int method); void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config); void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config); +void eap_sm_request_new_password(struct eap_sm *sm, struct wpa_ssid *config); +void eap_sm_request_pin(struct eap_sm *sm, struct wpa_ssid *config); void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config, - char *msg, size_t msg_len); + const char *msg, size_t msg_len); +void eap_sm_request_passphrase(struct eap_sm *sm, struct wpa_ssid *config); void eap_sm_notify_ctrl_attached(struct eap_sm *sm); u8 eap_get_type(const char *name); +const char * eap_get_name(EapType type); +size_t eap_get_names(char *buf, size_t buflen); u8 eap_get_phase2_type(const char *name); u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count); void eap_set_fast_reauth(struct eap_sm *sm, int enabled); void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); +void eap_set_force_disabled(struct eap_sm *sm, int disabled); struct wpa_ssid * eap_get_config(struct eap_sm *sm); int eap_key_available(struct eap_sm *sm); void eap_notify_success(struct eap_sm *sm); -u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); +void eap_notify_lower_layer_success(struct eap_sm *sm); +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len); void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); @@ -65,6 +261,16 @@ static inline u8 eap_get_type(const char *name) return EAP_TYPE_NONE; } +static inline const char * eap_get_name(EapType type) +{ + return NULL; +} + +static inline size_t eap_get_names(char *buf, size_t buflen) +{ + return 0; +} + #endif /* IEEE8021X_EAPOL */ #endif /* EAP_H */ diff --git a/contrib/wpa_supplicant/eap_aka.c b/contrib/wpa_supplicant/eap_aka.c index 7fe6449..b05cc72 100644 --- a/contrib/wpa_supplicant/eap_aka.c +++ b/contrib/wpa_supplicant/eap_aka.c @@ -20,7 +20,7 @@ #include "eap_i.h" #include "wpa_supplicant.h" #include "config_ssid.h" -#include "sha1.h" +#include "crypto.h" #include "pcsc_funcs.h" #include "eap_sim_common.h" @@ -211,7 +211,7 @@ static int eap_aka_learn_ids(struct eap_aka_data *data, static u8 * eap_aka_client_error(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen, int err) { struct eap_sim_msg *msg; @@ -229,7 +229,7 @@ static u8 * eap_aka_client_error(struct eap_sm *sm, struct eap_aka_data *data, static u8 * eap_aka_authentication_reject(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen) { struct eap_sim_msg *msg; @@ -249,7 +249,7 @@ static u8 * eap_aka_authentication_reject(struct eap_sm *sm, static u8 * eap_aka_synchronization_failure(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen) { struct eap_sim_msg *msg; @@ -271,7 +271,7 @@ static u8 * eap_aka_synchronization_failure(struct eap_sm *sm, static u8 * eap_aka_response_identity(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen, enum eap_sim_id_req id_req) { @@ -300,7 +300,7 @@ static u8 * eap_aka_response_identity(struct eap_sm *sm, eap_aka_clear_identities(data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", - req->identifier); + req->identifier); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY); @@ -317,13 +317,13 @@ static u8 * eap_aka_response_identity(struct eap_sm *sm, static u8 * eap_aka_response_challenge(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", - req->identifier); + req->identifier); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_RES"); @@ -337,7 +337,7 @@ static u8 * eap_aka_response_challenge(struct eap_sm *sm, static u8 * eap_aka_response_reauth(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen, int counter_too_small) { struct eap_sim_msg *msg; @@ -377,7 +377,7 @@ static u8 * eap_aka_response_reauth(struct eap_sm *sm, static u8 * eap_aka_response_notification(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen, u16 notification) { @@ -416,7 +416,8 @@ static u8 * eap_aka_response_notification(struct eap_sm *sm, static u8 * eap_aka_process_identity(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, size_t reqDataLen, + const struct eap_hdr *req, + size_t reqDataLen, size_t *respDataLen, struct eap_sim_attrs *attr) { @@ -458,7 +459,8 @@ static u8 * eap_aka_process_identity(struct eap_sm *sm, static u8 * eap_aka_process_challenge(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, size_t reqDataLen, + const struct eap_hdr *req, + size_t reqDataLen, size_t *respDataLen, struct eap_sim_attrs *attr) { @@ -512,8 +514,8 @@ static u8 * eap_aka_process_challenge(struct eap_sm *sm, "derivation", identity, identity_len); eap_aka_derive_mk(data, identity, identity_len); eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); - if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, - (u8 *) "", 0)) { + if (eap_sim_verify_mac(data->k_aut, (const u8 *) req, reqDataLen, + attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " "used invalid AT_MAC"); return eap_aka_client_error(sm, data, req, respDataLen, @@ -527,14 +529,17 @@ static u8 * eap_aka_process_challenge(struct eap_sm *sm, CLEAR_EAP_ID); if (attr->encr_data) { - if (eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, - 0)) { + u8 *decrypted; + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, + &eattr, 0); + if (decrypted == NULL) { return eap_aka_client_error( sm, data, req, respDataLen, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } eap_aka_learn_ids(data, &eattr); + free(decrypted); } if (data->state != FAILURE) @@ -551,11 +556,12 @@ static u8 * eap_aka_process_challenge(struct eap_sm *sm, static int eap_aka_process_notification_reauth(struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t reqDataLen, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; + u8 *decrypted; if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " @@ -563,8 +569,10 @@ static int eap_aka_process_notification_reauth(struct eap_aka_data *data, return -1; } - if (eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, 0)) { + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " "data from notification message"); return -1; @@ -574,15 +582,17 @@ static int eap_aka_process_notification_reauth(struct eap_aka_data *data, wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " "message does not match with counter in reauth " "message"); + free(decrypted); return -1; } + free(decrypted); return 0; } static int eap_aka_process_notification_auth(struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t reqDataLen, struct eap_sim_attrs *attr) { @@ -592,8 +602,8 @@ static int eap_aka_process_notification_auth(struct eap_aka_data *data, return -1; } - if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, - (u8 *) "", 0)) { + if (eap_sim_verify_mac(data->k_aut, (const u8 *) req, reqDataLen, + attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " "used invalid AT_MAC"); return -1; @@ -612,7 +622,7 @@ static int eap_aka_process_notification_auth(struct eap_aka_data *data, static u8 * eap_aka_process_notification(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t reqDataLen, size_t *respDataLen, struct eap_sim_attrs *attr) @@ -649,12 +659,13 @@ static u8 * eap_aka_process_notification(struct eap_sm *sm, static u8 * eap_aka_process_reauthentication(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t reqDataLen, size_t *respDataLen, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; + u8 *decrypted; wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); @@ -666,7 +677,7 @@ static u8 * eap_aka_process_reauthentication(struct eap_sm *sm, } data->reauth = 1; - if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, + if (eap_sim_verify_mac(data->k_aut, (const u8 *) req, reqDataLen, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " "did not have valid AT_MAC"); @@ -681,8 +692,10 @@ static u8 * eap_aka_process_reauthentication(struct eap_sm *sm, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } - if (eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, 0)) { + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " "data from reauthentication message"); return eap_aka_client_error(sm, data, req, respDataLen, @@ -693,6 +706,7 @@ static u8 * eap_aka_process_reauthentication(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", !eattr.nonce_s ? " AT_NONCE_S" : "", eattr.counter < 0 ? " AT_COUNTER" : ""); + free(decrypted); return eap_aka_client_error(sm, data, req, respDataLen, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } @@ -711,6 +725,7 @@ static u8 * eap_aka_process_reauthentication(struct eap_sm *sm, data->last_eap_identity_len = data->reauth_id_len; data->reauth_id = NULL; data->reauth_id_len = 0; + free(decrypted); return eap_aka_response_reauth(sm, data, req, respDataLen, 1); } data->counter = eattr.counter; @@ -735,19 +750,21 @@ static u8 * eap_aka_process_reauthentication(struct eap_sm *sm, "fast reauths performed - force fullauth"); eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); } + free(decrypted); return eap_aka_response_reauth(sm, data, req, respDataLen, 0); } static u8 * eap_aka_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_aka_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *req; - u8 *pos, subtype, *res; + const struct eap_hdr *req; + u8 subtype, *res; + const u8 *pos; struct eap_sim_attrs attr; size_t len; @@ -759,21 +776,19 @@ static u8 * eap_aka_process(struct eap_sm *sm, void *priv, return NULL; } - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_AKA || - (len = be_to_host16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); + pos = eap_hdr_validate(EAP_TYPE_AKA, reqData, reqDataLen, &len); + if (pos == NULL || len < 1) { ret->ignore = TRUE; return NULL; } + req = (const struct eap_hdr *) reqData; + len = be_to_host16(req->length); ret->ignore = FALSE; - ret->methodState = METHOD_CONT; + ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; - pos++; subtype = *pos++; wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); pos += 2; /* Reserved */ @@ -818,7 +833,7 @@ done: ret->decision = DECISION_FAIL; ret->methodState = METHOD_DONE; } else if (data->state == SUCCESS) { - ret->decision = DECISION_UNCOND_SUCC; + ret->decision = DECISION_COND_SUCC; ret->methodState = METHOD_DONE; } diff --git a/contrib/wpa_supplicant/eap_defs.h b/contrib/wpa_supplicant/eap_defs.h index effe665..9cd4490 100644 --- a/contrib/wpa_supplicant/eap_defs.h +++ b/contrib/wpa_supplicant/eap_defs.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant/hostapd / Shared EAP definitions + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_DEFS_H #define EAP_DEFS_H @@ -6,7 +20,7 @@ struct eap_hdr { u8 code; u8 identifier; - u16 length; /* including code and identifier */ + u16 length; /* including code and identifier; network byte order */ /* followed by length-4 octets of data */ } __attribute__ ((packed)); @@ -18,12 +32,12 @@ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, typedef enum { EAP_TYPE_NONE = 0, - EAP_TYPE_IDENTITY = 1, - EAP_TYPE_NOTIFICATION = 2, - EAP_TYPE_NAK = 3 /* Response only */, - EAP_TYPE_MD5 = 4, - EAP_TYPE_OTP = 5 /* RFC 2284 */, - EAP_TYPE_GTC = 6, /* RFC 2284 */ + EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, + EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, + EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, + EAP_TYPE_MD5 = 4, /* RFC 3748 */ + EAP_TYPE_OTP = 5 /* RFC 3748 */, + EAP_TYPE_GTC = 6, /* RFC 3748 */ EAP_TYPE_TLS = 13 /* RFC 2716 */, EAP_TYPE_LEAP = 17 /* Cisco proprietary */, EAP_TYPE_SIM = 18 /* draft-haverinen-pppext-eap-sim-12.txt */, @@ -33,9 +47,10 @@ typedef enum { EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, EAP_TYPE_FAST = 43 /* draft-cam-winget-eap-fast-00.txt */, + EAP_TYPE_PAX = 46, /* draft-clancy-eap-pax-04.txt */ EAP_TYPE_EXPANDED_NAK = 254 /* RFC 3748 */, EAP_TYPE_PSK = 255 /* EXPERIMENTAL - type not yet allocated - * draft-bersani-eap-psk-03 */ + * draft-bersani-eap-psk-09 */ } EapType; #endif /* EAP_DEFS_H */ diff --git a/contrib/wpa_supplicant/eap_fast.c b/contrib/wpa_supplicant/eap_fast.c index 1c9c3ff..ff8f76c 100644 --- a/contrib/wpa_supplicant/eap_fast.c +++ b/contrib/wpa_supplicant/eap_fast.c @@ -24,10 +24,12 @@ #include "tls.h" #include "eap_tlv.h" #include "sha1.h" +#include "config.h" /* TODO: * - encrypt PAC-Key in the PAC file * - test session resumption and enable it if it interoperates + * - password change (pending mschapv2 packet; replay decrypted packet) */ #define EAP_FAST_VERSION 1 @@ -36,7 +38,8 @@ #define TLS_EXT_PAC_OPAQUE 35 -static char *pac_file_hdr = "wpa_supplicant EAP-FAST PAC file - version 1"; +static const char *pac_file_hdr = + "wpa_supplicant EAP-FAST PAC file - version 1"; static void eap_fast_deinit(struct eap_sm *sm, void *priv); @@ -58,28 +61,18 @@ struct pac_tlv_hdr { }; -/* draft-cam-winget-eap-fast-00.txt, 6.2 EAP-FAST Authentication Phase 1 */ +/* draft-cam-winget-eap-fast-02.txt: + * 6.2 EAP-FAST Authentication Phase 1: Key Derivations */ struct eap_fast_key_block_auth { - /* RC4-128-SHA */ - u8 client_write_MAC_secret[20]; - u8 server_write_MAC_secret[20]; - u8 client_write_key[16]; - u8 server_write_key[16]; - /* u8 client_write_IV[0]; */ - /* u8 server_write_IV[0]; */ + /* Extra key material after TLS key_block */ u8 session_key_seed[40]; }; -/* draft-cam-winget-eap-fast-00.txt, 7.3 EAP-FAST Provisioning Exchange */ +/* draft-cam-winget-eap-fast-provisioning-01.txt: + * 3.4 Key Derivations Used in the EAP-FAST Provisioning Exchange */ struct eap_fast_key_block_provisioning { - /* AES128-SHA */ - u8 client_write_MAC_secret[20]; - u8 server_write_MAC_secret[20]; - u8 client_write_key[16]; - u8 server_write_key[16]; - u8 client_write_IV[16]; - u8 server_write_IV[16]; + /* Extra key material after TLS key_block */ u8 session_key_seed[40]; u8 server_challenge[16]; u8 client_challenge[16]; @@ -249,12 +242,34 @@ static int eap_fast_add_pac(struct eap_fast_data *data, } -static int eap_fast_read_line(FILE *f, char *buf, size_t buf_len) +struct eap_fast_read_ctx { + FILE *f; + const char *pos; + const char *end; +}; + +static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char *buf, + size_t buf_len) { char *pos; - if (fgets(buf, buf_len, f) == NULL) { - return -1; + if (rc->f) { + if (fgets(buf, buf_len, rc->f) == NULL) + return -1; + } else { + const char *l_end; + size_t len; + if (rc->pos >= rc->end) + return -1; + l_end = rc->pos; + while (l_end < rc->end && *l_end != '\n') + l_end++; + len = l_end - rc->pos; + if (len >= buf_len) + len = buf_len - 1; + memcpy(buf, rc->pos, len); + buf[len] = '\0'; + rc->pos = l_end + 1; } buf[buf_len - 1] = '\0'; @@ -293,9 +308,10 @@ static u8 * eap_fast_parse_hex(const char *value, size_t *len) } -static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file) +static int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_data *data, + const char *pac_file) { - FILE *f; + struct eap_fast_read_ctx rc; struct eap_fast_pac *pac = NULL; int count = 0; char *buf, *pos; @@ -305,11 +321,27 @@ static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file) if (pac_file == NULL) return -1; - f = fopen(pac_file, "r"); - if (f == NULL) { - wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - assume no " - "PAC entries have been provisioned", pac_file); - return 0; + memset(&rc, 0, sizeof(rc)); + + if (strncmp(pac_file, "blob://", 7) == 0) { + const struct wpa_config_blob *blob; + blob = eap_get_config_blob(sm, pac_file + 7); + if (blob == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file + 7); + return 0; + } + rc.pos = (char *) blob->data; + rc.end = (char *) blob->data + blob->len; + } else { + rc.f = fopen(pac_file, "r"); + if (rc.f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file); + return 0; + } } buf = malloc(buf_len); @@ -318,16 +350,17 @@ static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file) } line++; - if (eap_fast_read_line(f, buf, buf_len) < 0 || + if (eap_fast_read_line(&rc, buf, buf_len) < 0 || strcmp(pac_file_hdr, buf) != 0) { wpa_printf(MSG_INFO, "EAP-FAST: Unrecognized header line in " "PAC file '%s'", pac_file); free(buf); - fclose(f); + if (rc.f) + fclose(rc.f); return -1; } - while (eap_fast_read_line(f, buf, buf_len) == 0) { + while (eap_fast_read_line(&rc, buf, buf_len) == 0) { line++; pos = strchr(buf, '='); if (pos) { @@ -423,7 +456,8 @@ static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file) } free(buf); - fclose(f); + if (rc.f) + fclose(rc.f); if (ret == 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: read %d PAC entries from " @@ -434,67 +468,124 @@ static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file) } -static void eap_fast_write(FILE *f, const char *field, const u8 *data, +static void eap_fast_write(char **buf, char **pos, size_t *buf_len, + const char *field, const u8 *data, size_t len, int txt) { int i; + size_t need; - if (data == NULL) + if (data == NULL || *buf == NULL) return; - fprintf(f, "%s=", field); + need = strlen(field) + len * 2 + 30; + if (txt) + need += strlen(field) + len + 20; + + if (*pos - *buf + need > *buf_len) { + char *nbuf = realloc(*buf, *buf_len + need); + if (nbuf == NULL) { + free(*buf); + *buf = NULL; + return; + } + *buf = nbuf; + *buf_len += need; + } + + *pos += snprintf(*pos, *buf + *buf_len - *pos, "%s=", field); for (i = 0; i < len; i++) { - fprintf(f, "%02x", data[i]); + *pos += snprintf(*pos, *buf + *buf_len - *pos, + "%02x", data[i]); } - fprintf(f, "\n"); + *pos += snprintf(*pos, *buf + *buf_len - *pos, "\n"); if (txt) { - fprintf(f, "%s-txt=", field); + *pos += snprintf(*pos, *buf + *buf_len - *pos, + "%s-txt=", field); for (i = 0; i < len; i++) { - fprintf(f, "%c", data[i]); + *pos += snprintf(*pos, *buf + *buf_len - *pos, + "%c", data[i]); } - fprintf(f, "\n"); + *pos += snprintf(*pos, *buf + *buf_len - *pos, "\n"); } } -static int eap_fast_save_pac(struct eap_fast_data *data, const char *pac_file) +static int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_data *data, + const char *pac_file) { FILE *f; struct eap_fast_pac *pac; int count = 0; + char *buf, *pos; + size_t buf_len; if (pac_file == NULL) return -1; - f = fopen(pac_file, "w"); - if (f == NULL) { - wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC file '%s' " - "for writing", pac_file); + buf_len = 1024; + pos = buf = malloc(buf_len); + if (buf == NULL) return -1; - } - fprintf(f, "%s\n", pac_file_hdr); + pos += snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); pac = data->pac; while (pac) { - fprintf(f, "START\n"); - eap_fast_write(f, "PAC-Key", pac->pac_key, + pos += snprintf(pos, buf + buf_len - pos, "START\n"); + eap_fast_write(&buf, &pos, &buf_len, "PAC-Key", pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0); - eap_fast_write(f, "PAC-Opaque", pac->pac_opaque, - pac->pac_opaque_len, 0); - eap_fast_write(f, "PAC-Info", pac->pac_info, + eap_fast_write(&buf, &pos, &buf_len, "PAC-Opaque", + pac->pac_opaque, pac->pac_opaque_len, 0); + eap_fast_write(&buf, &pos, &buf_len, "PAC-Info", pac->pac_info, pac->pac_info_len, 0); - eap_fast_write(f, "A-ID", pac->a_id, pac->a_id_len, 0); - eap_fast_write(f, "I-ID", pac->i_id, pac->i_id_len, 1); - eap_fast_write(f, "A-ID-Info", pac->a_id_info, - pac->a_id_info_len, 1); - fprintf(f, "END\n"); + eap_fast_write(&buf, &pos, &buf_len, "A-ID", pac->a_id, + pac->a_id_len, 0); + eap_fast_write(&buf, &pos, &buf_len, "I-ID", pac->i_id, + pac->i_id_len, 1); + eap_fast_write(&buf, &pos, &buf_len, "A-ID-Info", + pac->a_id_info, pac->a_id_info_len, 1); + pos += snprintf(pos, buf + buf_len - pos, "END\n"); count++; pac = pac->next; + + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC " + "data"); + return -1; + } } - fclose(f); + if (strncmp(pac_file, "blob://", 7) == 0) { + struct wpa_config_blob *blob; + blob = malloc(sizeof(*blob)); + if (blob == NULL) { + free(buf); + return -1; + } + memset(blob, 0, sizeof(*blob)); + blob->data = (u8 *) buf; + blob->len = pos - buf; + buf = NULL; + blob->name = strdup(pac_file + 7); + if (blob->name == NULL) { + wpa_config_free_blob(blob); + return -1; + } + eap_set_config_blob(sm, blob); + } else { + f = fopen(pac_file, "w"); + if (f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC " + "file '%s' for writing", pac_file); + free(buf); + return -1; + } + fprintf(f, "%s", buf); + free(buf); + fclose(f); + } wpa_printf(MSG_DEBUG, "EAP-FAST: wrote %d PAC entries into '%s'", count, pac_file); @@ -590,7 +681,7 @@ static void * eap_fast_init(struct eap_sm *sm) * TODO: consider making this configurable */ tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn); - if (eap_fast_load_pac(data, config->pac_file) < 0) { + if (eap_fast_load_pac(sm, data, config->pac_file) < 0) { eap_fast_deinit(sm, data); return NULL; } @@ -632,7 +723,7 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) static int eap_fast_encrypt(struct eap_sm *sm, struct eap_fast_data *data, - int id, u8 *plain, size_t plain_len, + int id, const u8 *plain, size_t plain_len, u8 **out_data, size_t *out_len) { int res; @@ -799,32 +890,37 @@ static u8 * eap_fast_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len) { struct tls_keys keys; - u8 *random; + u8 *rnd; u8 *out; + int block_size; if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) return NULL; - out = malloc(len); - random = malloc(keys.client_random_len + keys.server_random_len); - if (out == NULL || random == NULL) { + block_size = tls_connection_get_keyblock_size(sm->ssl_ctx, data->conn); + if (block_size < 0) + return NULL; + out = malloc(block_size + len); + rnd = malloc(keys.client_random_len + keys.server_random_len); + if (out == NULL || rnd == NULL) { free(out); - free(random); + free(rnd); return NULL; } - memcpy(random, keys.server_random, keys.server_random_len); - memcpy(random + keys.server_random_len, keys.client_random, + memcpy(rnd, keys.server_random, keys.server_random_len); + memcpy(rnd + keys.server_random_len, keys.client_random, keys.client_random_len); wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " "expansion", keys.master_key, keys.master_key_len); if (tls_prf(keys.master_key, keys.master_key_len, - label, random, keys.client_random_len + - keys.server_random_len, out, len)) { - free(random); + label, rnd, keys.client_random_len + + keys.server_random_len, out, block_size + len)) { + free(rnd); free(out); return NULL; } - free(random); + free(rnd); + memmove(out, out + block_size, len); return out; } @@ -836,6 +932,11 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, data->key_block_a = (struct eap_fast_key_block_auth *) eap_fast_derive_key(sm, &data->ssl, "key expansion", sizeof(*data->key_block_a)); + if (data->key_block_a == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " + "session_key_seed"); + return; + } wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed", data->key_block_a->session_key_seed, sizeof(data->key_block_a->session_key_seed)); @@ -878,7 +979,7 @@ static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) static int eap_fast_phase2_request(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, + const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { @@ -1333,7 +1434,7 @@ static u8 * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data, } eap_fast_add_pac(data, &entry); - eap_fast_save_pac(data, config->pac_file); + eap_fast_save_pac(sm, data, config->pac_file); if (data->provisioning) { /* EAP-FAST provisioning does not provide keying material and @@ -1356,12 +1457,13 @@ static u8 * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data, static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, - struct eap_method_ret *ret, struct eap_hdr *req, - u8 *in_data, size_t in_len, + struct eap_method_ret *ret, + const struct eap_hdr *req, + const u8 *in_data, size_t in_len, u8 **out_data, size_t *out_len) { u8 *in_decrypted, *pos, *end; - int buf_len, len_decrypted, len, res; + int buf_len, len_decrypted, len; struct eap_hdr *hdr; u8 *resp = NULL; size_t resp_len; @@ -1371,13 +1473,17 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, int iresult = 0, result = 0; struct eap_tlv_crypto_binding__tlv *crypto_binding = NULL; size_t crypto_binding_len = 0; + const u8 *msg; + size_t msg_len; + int need_more_input; wpa_printf(MSG_DEBUG, "EAP-FAST: received %lu bytes encrypted data for" " Phase 2", (unsigned long) in_len); - res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); - if (res < 0 || res == 1) - return res; + msg = eap_tls_data_reassemble(sm, &data->ssl, in_data, in_len, + &msg_len, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; buf_len = in_len; if (data->ssl.tls_in_total > buf_len) @@ -1393,7 +1499,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, } len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, + msg, msg_len, in_decrypted, buf_len); free(data->ssl.tls_in); data->ssl.tls_in = NULL; @@ -1417,11 +1523,12 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, pos = in_decrypted; end = in_decrypted + len_decrypted; - while (pos < end) { + while (pos + 4 < end) { mandatory = pos[0] & 0x80; - tlv_type = ((pos[0] & 0x3f) << 8) | pos[1]; - len = (pos[2] << 8) | pos[3]; - pos += 4; + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; if (pos + len > end) { free(in_decrypted); wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); @@ -1447,7 +1554,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, result = EAP_TLV_RESULT_FAILURE; break; } - result = (pos[0] << 16) | pos[1]; + result = WPA_GET_BE16(pos); if (result != EAP_TLV_RESULT_SUCCESS && result != EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown " @@ -1467,7 +1574,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, iresult = EAP_TLV_RESULT_FAILURE; break; } - iresult = (pos[0] << 16) | pos[1]; + iresult = WPA_GET_BE16(pos); if (iresult != EAP_TLV_RESULT_SUCCESS && iresult != EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown " @@ -1584,7 +1691,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, if (!resp && pac && result != EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV " - "acnowledging success"); + "acknowledging success"); resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, &resp_len); if (!resp) { @@ -1607,7 +1714,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, if (resp == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send " "empty response packet"); - resp = malloc(0); + resp = malloc(1); if (resp == NULL) return 0; resp_len = 0; @@ -1628,65 +1735,25 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, static u8 * eap_fast_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { - struct eap_hdr *req; - int left, res; - unsigned int tls_msg_len; - u8 flags, *pos, *resp, id; + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, *resp, id; + const u8 *pos; struct eap_fast_data *data = priv; - if (tls_get_errors(sm->ssl_ctx)) { - wpa_printf(MSG_INFO, "EAP-FAST: TLS errors detected"); - ret->ignore = TRUE; - return NULL; - } - - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_FAST || - (left = host_to_be16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame"); - ret->ignore = TRUE; + pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret, + reqData, reqDataLen, &left, &flags); + if (pos == NULL) return NULL; - } - left -= sizeof(struct eap_hdr); + req = (const struct eap_hdr *) reqData; id = req->identifier; - pos++; - flags = *pos++; - left -= 2; - wpa_printf(MSG_DEBUG, "EAP-FAST: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) reqDataLen, flags); - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-FAST: Short frame with TLS " - "length"); - ret->ignore = TRUE; - return NULL; - } - tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | - pos[3]; - wpa_printf(MSG_DEBUG, "EAP-FAST: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } - - ret->ignore = FALSE; - ret->methodState = METHOD_CONT; - ret->decision = DECISION_FAIL; - ret->allowNotifications = TRUE; if (flags & EAP_TLS_FLAGS_START) { - u8 *a_id; + const u8 *a_id; size_t a_id_len; struct pac_tlv_hdr *hdr; diff --git a/contrib/wpa_supplicant/eap_gtc.c b/contrib/wpa_supplicant/eap_gtc.c index 42a7a49..3665746 100644 --- a/contrib/wpa_supplicant/eap_gtc.c +++ b/contrib/wpa_supplicant/eap_gtc.c @@ -53,29 +53,27 @@ static void eap_gtc_deinit(struct eap_sm *sm, void *priv) static u8 * eap_gtc_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_gtc_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *req, *resp; - u8 *pos, *password; - size_t password_len, len, msg_len; - - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_GTC || - (len = be_to_host16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); + const struct eap_hdr *req; + struct eap_hdr *resp; + const u8 *pos, *password; + u8 *rpos; + size_t password_len, len; + + pos = eap_hdr_validate(EAP_TYPE_GTC, reqData, reqDataLen, &len); + if (pos == NULL) { ret->ignore = TRUE; return NULL; } - pos++; - msg_len = len - sizeof(*req) - 1; - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", - pos, msg_len); + req = (const struct eap_hdr *) reqData; + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len); if (data->prefix && - (msg_len < 10 || memcmp(pos, "CHALLENGE=", 10) != 0)) { + (len < 10 || memcmp(pos, "CHALLENGE=", 10) != 0)) { wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with " "expected prefix"); @@ -90,16 +88,15 @@ static u8 * eap_gtc_process(struct eap_sm *sm, void *priv, resp->code = EAP_CODE_RESPONSE; resp->identifier = req->identifier; resp->length = host_to_be16(*respDataLen); - pos = (u8 *) (resp + 1); - *pos++ = EAP_TYPE_GTC; + rpos = (u8 *) (resp + 1); + *rpos++ = EAP_TYPE_GTC; return (u8 *) resp; } if (config == NULL || (config->password == NULL && config->otp == NULL)) { wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); - eap_sm_request_otp(sm, config, (char *) pos, - len - sizeof(*req) - 1); + eap_sm_request_otp(sm, config, (const char *) pos, len); ret->ignore = TRUE; return NULL; } @@ -128,16 +125,16 @@ static u8 * eap_gtc_process(struct eap_sm *sm, void *priv, resp->code = EAP_CODE_RESPONSE; resp->identifier = req->identifier; resp->length = host_to_be16(*respDataLen); - pos = (u8 *) (resp + 1); - *pos++ = EAP_TYPE_GTC; + rpos = (u8 *) (resp + 1); + *rpos++ = EAP_TYPE_GTC; if (data->prefix) { - memcpy(pos, "RESPONSE=", 9); - pos += 9; - memcpy(pos, config->identity, config->identity_len); - pos += config->identity_len; - *pos++ = '\0'; + memcpy(rpos, "RESPONSE=", 9); + rpos += 9; + memcpy(rpos, config->identity, config->identity_len); + rpos += config->identity_len; + *rpos++ = '\0'; } - memcpy(pos, password, password_len); + memcpy(rpos, password, password_len); wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", (u8 *) (resp + 1) + 1, *respDataLen - sizeof(struct eap_hdr) - 1); diff --git a/contrib/wpa_supplicant/eap_i.h b/contrib/wpa_supplicant/eap_i.h index c719812..d134548 100644 --- a/contrib/wpa_supplicant/eap_i.h +++ b/contrib/wpa_supplicant/eap_i.h @@ -1,9 +1,23 @@ +/* + * WPA Supplicant / EAP state machines internal structures (RFC 4137) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_I_H #define EAP_I_H #include "eap.h" -/* draft-ietf-eap-statemachine-05.pdf - Peer state machine */ +/* RFC 4137 - EAP Peer state machine */ typedef enum { DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC @@ -13,37 +27,190 @@ typedef enum { METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE } EapMethodState; +/** + * struct eap_method_ret - EAP return values from struct eap_method::process() + * + * These structure contains OUT variables for the interface between peer state + * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as + * the return value of struct eap_method::process() so it is not included in + * this structure. + */ struct eap_method_ret { + /** + * ignore - Whether method decided to drop the current packed (OUT) + */ Boolean ignore; + + /** + * methodState - Method-specific state (IN/OUT) + */ EapMethodState methodState; + + /** + * decision - Authentication decision (OUT) + */ EapDecision decision; + + /** + * allowNotifications - Whether method allows notifications (OUT) + */ Boolean allowNotifications; }; +/** + * struct eap_method - EAP method interface + * This structure defines the EAP method interface. Each method will need to + * register its own EAP type, EAP name, and set of function pointers for method + * specific operations. This interface is based on section 4.4 of RFC 4137. + */ struct eap_method { + /** + * method - EAP type number (EAP_TYPE_*) + */ EapType method; + + /** + * name - Name of the method (e.g., "TLS") + */ const char *name; + /** + * init - Initialize an EAP method + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * Returns: Pointer to allocated private data, or %NULL on failure + * + * This function is used to initialize the EAP method explicitly + * instead of using METHOD_INIT state as specific in RFC 4137. The + * method is expected to initialize it method-specific state and return + * a pointer that will be used as the priv argument to other calls. + */ void * (*init)(struct eap_sm *sm); + + /** + * deinit - Deinitialize an EAP method + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * Deinitialize the EAP method and free any allocated private data. + */ void (*deinit)(struct eap_sm *sm, void *priv); + + /** + * process - Process an EAP request + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * @reqDataLen: Length of the EAP request + * @respDataLen: Length of the returned EAP response + * Returns: Pointer to allocated EAP response packet (eapRespData) + * + * This function is a combination of m.check(), m.process(), and + * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other + * words, this function validates the incoming request, processes it, + * and build a response packet. m.check() and m.process() return values + * are returned through struct eap_method_ret *ret variable. Caller is + * responsible for freeing the returned EAP response packet. + */ u8 * (*process)(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen); + + /** + * isKeyAvailable - Find out whether EAP method has keying material + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: %TRUE if key material (eapKeyData) is available + */ Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv); + + /** + * getKey - Get EAP method specific keying material (eapKeyData) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to variable to store key length (eapKeyDataLen) + * Returns: Keying material (eapKeyData) or %NULL if not available + * + * This function can be used to get the keying material from the EAP + * method. The key may already be stored in the method-specific private + * data or this function may derive the key. + */ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * get_status - Get EAP method status + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf + * + * Query EAP method for status information. This function fills in a + * text area with current status information from the EAP method. If + * the buffer (buf) is not large enough, status information will be + * truncated to fit the buffer. + */ int (*get_status)(struct eap_sm *sm, void *priv, char *buf, size_t buflen, int verbose); - /* Optional handlers for fast re-authentication */ + /** + * has_reauth_data - Whether method is ready for fast reauthentication + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: %TRUE or %FALSE based on whether fast reauthentication is + * possible + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. + */ Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv); + + /** + * deinit_for_reauth - Release data that is not needed for fast re-auth + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. This is called + * when authentication has been completed and EAP state machine is + * requesting that enough state information is maintained for fast + * re-authentication + */ void (*deinit_for_reauth)(struct eap_sm *sm, void *priv); + + /** + * init_for_reauth - Prepare for start of fast re-authentication + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. This is called + * when EAP authentication is started and EAP state machine is + * requesting fast re-authentication to be used. + */ void * (*init_for_reauth)(struct eap_sm *sm, void *priv); + + /** + * get_identity - Get method specific identity for re-authentication + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Length of the returned identity + * Returns: Pointer to the method specific identity or %NULL if default + * identity is to be used + * + * This function is an optional handler that only EAP methods + * that use method specific identity need to implement. + */ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); }; +/** + * struct eap_sm - EAP state machine data + */ struct eap_sm { enum { EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED, @@ -76,7 +243,7 @@ struct eap_sm { u8 *eapKeyData; /* peer to lower layer */ size_t eapKeyDataLen; /* peer to lower layer */ const struct eap_method *m; /* selected EAP method */ - /* not defined in draft-ietf-eap-statemachine-02 */ + /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx; struct eapol_callbacks *eapol_cb; @@ -101,6 +268,13 @@ struct eap_sm { u8 *peer_challenge, *auth_challenge; int num_rounds; + int force_disabled; }; +const u8 * eap_hdr_validate(EapType eap_type, const u8 *msg, size_t msglen, + size_t *plen); +void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob); +const struct wpa_config_blob * +eap_get_config_blob(struct eap_sm *sm, const char *name); + #endif /* EAP_I_H */ diff --git a/contrib/wpa_supplicant/eap_leap.c b/contrib/wpa_supplicant/eap_leap.c index 6409a68..d0e5aab 100644 --- a/contrib/wpa_supplicant/eap_leap.c +++ b/contrib/wpa_supplicant/eap_leap.c @@ -21,7 +21,7 @@ #include "wpa_supplicant.h" #include "config_ssid.h" #include "ms_funcs.h" -#include "md5.h" +#include "crypto.h" #define LEAP_VERSION 1 #define LEAP_CHALLENGE_LEN 8 @@ -68,18 +68,20 @@ static void eap_leap_deinit(struct eap_sm *sm, void *priv) static u8 * eap_leap_process_request(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_leap_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *req, *resp; - u8 *pos, *challenge, challenge_len; + const struct eap_hdr *req; + struct eap_hdr *resp; + const u8 *pos, *challenge; + u8 challenge_len, *rpos; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); + req = (const struct eap_hdr *) reqData; + pos = (const u8 *) (req + 1); if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_LEAP) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); ret->ignore = TRUE; @@ -121,17 +123,18 @@ static u8 * eap_leap_process_request(struct eap_sm *sm, void *priv, resp->code = EAP_CODE_RESPONSE; resp->identifier = req->identifier; resp->length = host_to_be16(*respDataLen); - pos = (u8 *) (resp + 1); - *pos++ = EAP_TYPE_LEAP; - *pos++ = LEAP_VERSION; - *pos++ = 0; /* unused */ - *pos++ = LEAP_RESPONSE_LEN; + rpos = (u8 *) (resp + 1); + *rpos++ = EAP_TYPE_LEAP; + *rpos++ = LEAP_VERSION; + *rpos++ = 0; /* unused */ + *rpos++ = LEAP_RESPONSE_LEN; nt_challenge_response(challenge, - config->password, config->password_len, pos); - memcpy(data->peer_response, pos, LEAP_RESPONSE_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", pos, LEAP_RESPONSE_LEN); - pos += LEAP_RESPONSE_LEN; - memcpy(pos, config->identity, config->identity_len); + config->password, config->password_len, rpos); + memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", + rpos, LEAP_RESPONSE_LEN); + rpos += LEAP_RESPONSE_LEN; + memcpy(rpos, config->identity, config->identity_len); data->state = LEAP_WAIT_SUCCESS; @@ -141,12 +144,13 @@ static u8 * eap_leap_process_request(struct eap_sm *sm, void *priv, static u8 * eap_leap_process_success(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_leap_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *req, *resp; + const struct eap_hdr *req; + struct eap_hdr *resp; u8 *pos; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); @@ -158,7 +162,7 @@ static u8 * eap_leap_process_success(struct eap_sm *sm, void *priv, return NULL; } - req = (struct eap_hdr *) reqData; + req = (const struct eap_hdr *) reqData; *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_CHALLENGE_LEN + config->identity_len; @@ -194,19 +198,20 @@ static u8 * eap_leap_process_success(struct eap_sm *sm, void *priv, static u8 * eap_leap_process_response(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_leap_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *resp; - u8 *pos, response_len, pw_hash[16], pw_hash_hash[16], + const struct eap_hdr *resp; + const u8 *pos; + u8 response_len, pw_hash[16], pw_hash_hash[16], expected[LEAP_RESPONSE_LEN]; wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); - resp = (struct eap_hdr *) reqData; - pos = (u8 *) (resp + 1); + resp = (const struct eap_hdr *) reqData; + pos = (const u8 *) (resp + 1); if (reqDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_LEAP) { wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); ret->ignore = TRUE; @@ -270,11 +275,11 @@ static u8 * eap_leap_process_response(struct eap_sm *sm, void *priv, static u8 * eap_leap_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *eap; + const struct eap_hdr *eap; size_t len; if (config == NULL || config->password == NULL) { @@ -284,7 +289,7 @@ static u8 * eap_leap_process(struct eap_sm *sm, void *priv, return NULL; } - eap = (struct eap_hdr *) reqData; + eap = (const struct eap_hdr *) reqData; if (reqDataLen < sizeof(*eap) || (len = be_to_host16(eap->length)) > reqDataLen) { @@ -295,7 +300,7 @@ static u8 * eap_leap_process(struct eap_sm *sm, void *priv, ret->ignore = FALSE; ret->allowNotifications = TRUE; - ret->methodState = METHOD_CONT; + ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; sm->leap_done = FALSE; @@ -331,7 +336,8 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) struct eap_leap_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); u8 *key, pw_hash_hash[16], pw_hash[16]; - MD5_CTX context; + const u8 *addr[5]; + size_t elen[5]; if (data->state != LEAP_DONE) return NULL; @@ -353,13 +359,17 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response", data->ap_response, LEAP_RESPONSE_LEN); - MD5Init(&context); - MD5Update(&context, pw_hash_hash, 16); - MD5Update(&context, data->ap_challenge, LEAP_CHALLENGE_LEN); - MD5Update(&context, data->ap_response, LEAP_RESPONSE_LEN); - MD5Update(&context, data->peer_challenge, LEAP_CHALLENGE_LEN); - MD5Update(&context, data->peer_response, LEAP_RESPONSE_LEN); - MD5Final(key, &context); + addr[0] = pw_hash_hash; + elen[0] = 16; + addr[1] = data->ap_challenge; + elen[1] = LEAP_CHALLENGE_LEN; + addr[2] = data->ap_response; + elen[2] = LEAP_RESPONSE_LEN; + addr[3] = data->peer_challenge; + elen[3] = LEAP_CHALLENGE_LEN; + addr[4] = data->peer_response; + elen[4] = LEAP_RESPONSE_LEN; + md5_vector(5, addr, elen, key); wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); *len = LEAP_KEY_LEN; diff --git a/contrib/wpa_supplicant/eap_md5.c b/contrib/wpa_supplicant/eap_md5.c index bcb5558..46a5f55 100644 --- a/contrib/wpa_supplicant/eap_md5.c +++ b/contrib/wpa_supplicant/eap_md5.c @@ -20,6 +20,7 @@ #include "wpa_supplicant.h" #include "config_ssid.h" #include "md5.h" +#include "crypto.h" static void * eap_md5_init(struct eap_sm *sm) @@ -35,15 +36,18 @@ static void eap_md5_deinit(struct eap_sm *sm, void *priv) static u8 * eap_md5_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *req, *resp; - u8 *pos, *challenge; + const struct eap_hdr *req; + struct eap_hdr *resp; + const u8 *pos, *challenge; + u8 *rpos; int challenge_len; - MD5_CTX context; size_t len; + const u8 *addr[3]; + size_t elen[3]; if (config == NULL || config->password == NULL) { wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); @@ -52,18 +56,15 @@ static u8 * eap_md5_process(struct eap_sm *sm, void *priv, return NULL; } - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_MD5 || - (len = be_to_host16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); + pos = eap_hdr_validate(EAP_TYPE_MD5, reqData, reqDataLen, &len); + if (pos == NULL) { ret->ignore = TRUE; return NULL; } - pos++; + req = (const struct eap_hdr *) reqData; challenge_len = *pos++; if (challenge_len == 0 || - challenge_len > len - sizeof(*req) - 2) { + challenge_len > len - 1) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge " "(challenge_len=%d len=%lu", challenge_len, (unsigned long) len); @@ -87,16 +88,18 @@ static u8 * eap_md5_process(struct eap_sm *sm, void *priv, resp->code = EAP_CODE_RESPONSE; resp->identifier = req->identifier; resp->length = host_to_be16(*respDataLen); - pos = (u8 *) (resp + 1); - *pos++ = EAP_TYPE_MD5; - *pos++ = MD5_MAC_LEN; /* Value-Size */ - - MD5Init(&context); - MD5Update(&context, &resp->identifier, 1); - MD5Update(&context, config->password, config->password_len); - MD5Update(&context, challenge, challenge_len); - MD5Final(pos, &context); - wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN); + rpos = (u8 *) (resp + 1); + *rpos++ = EAP_TYPE_MD5; + *rpos++ = MD5_MAC_LEN; /* Value-Size */ + + addr[0] = &resp->identifier; + elen[0] = 1; + addr[1] = config->password; + elen[1] = config->password_len; + addr[2] = challenge; + elen[2] = challenge_len; + md5_vector(3, addr, elen, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, MD5_MAC_LEN); return (u8 *) resp; } diff --git a/contrib/wpa_supplicant/eap_mschapv2.c b/contrib/wpa_supplicant/eap_mschapv2.c index 35c391c..9fcd480 100644 --- a/contrib/wpa_supplicant/eap_mschapv2.c +++ b/contrib/wpa_supplicant/eap_mschapv2.c @@ -21,6 +21,7 @@ #include "wpa_supplicant.h" #include "config_ssid.h" #include "ms_funcs.h" +#include "wpa_ctrl.h" struct eap_mschapv2_hdr { @@ -71,6 +72,9 @@ struct eap_mschapv2_data { u8 master_key[16]; int master_key_valid; int success; + + u8 *prev_challenge; + size_t prev_challenge_len; }; @@ -114,6 +118,7 @@ static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) struct eap_mschapv2_data *data = priv; free(data->peer_challenge); free(data->auth_challenge); + free(data->prev_challenge); free(data); } @@ -121,7 +126,7 @@ static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) static u8 * eap_mschapv2_challenge(struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, - struct eap_mschapv2_hdr *req, + const struct eap_mschapv2_hdr *req, size_t *respDataLen) { struct wpa_ssid *config = eap_get_config(sm); @@ -131,6 +136,9 @@ static u8 * eap_mschapv2_challenge(struct eap_sm *sm, struct eap_mschapv2_hdr *resp; u8 password_hash[16], password_hash_hash[16]; + if (config == NULL) + return NULL; + /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ @@ -150,26 +158,31 @@ static u8 * eap_mschapv2_challenge(struct eap_sm *sm, challenge_len = *pos++; if (challenge_len != 16) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " - "%d", challenge_len); + "%lu", (unsigned long) challenge_len); ret->ignore = TRUE; return NULL; } if (len < 10 || len - 10 < challenge_len) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" - " packet: len=%lu challenge_len=%d", - (unsigned long) len, challenge_len); + " packet: len=%lu challenge_len=%lu", + (unsigned long) len, (unsigned long) challenge_len); ret->ignore = TRUE; return NULL; } - challenge = pos; + if (data->passwd_change_challenge_valid) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the " + "failure message"); + challenge = data->passwd_change_challenge; + } else + challenge = pos; pos += challenge_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", pos, len - challenge_len - 10); ret->ignore = FALSE; - ret->methodState = METHOD_CONT; + ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; @@ -187,9 +200,17 @@ static u8 * eap_mschapv2_challenge(struct eap_sm *sm, resp->type = EAP_TYPE_MSCHAPV2; resp->op_code = MSCHAPV2_OP_RESPONSE; resp->mschapv2_id = req->mschapv2_id; + if (data->prev_error) { + /* + * TODO: this does not seem to be enough when processing two + * or more failure messages. IAS did not increment mschapv2_id + * in its own packets, but it seemed to expect the peer to + * increment this for all packets(?). + */ + resp->mschapv2_id++; + } ms_len = *respDataLen - 5; - resp->ms_length[0] = ms_len >> 8; - resp->ms_length[1] = ms_len & 0xff; + WPA_PUT_BE16(resp->ms_length, ms_len); pos = (u8 *) (resp + 1); *pos++ = MSCHAPV2_RESP_LEN; /* Value-Size */ @@ -243,6 +264,8 @@ static u8 * eap_mschapv2_challenge(struct eap_sm *sm, pos++; /* Flag / reserved, must be zero */ memcpy(pos, config->identity, config->identity_len); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " + "(response)", resp->identifier, resp->mschapv2_id); return (u8 *) resp; } @@ -250,16 +273,17 @@ static u8 * eap_mschapv2_challenge(struct eap_sm *sm, static u8 * eap_mschapv2_success(struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, - struct eap_mschapv2_hdr *req, + const struct eap_mschapv2_hdr *req, size_t *respDataLen) { struct eap_mschapv2_hdr *resp; - u8 *pos, recv_response[20]; + const u8 *pos; + u8 recv_response[20]; int len, left; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); len = be_to_host16(req->length); - pos = (u8 *) (req + 1); + pos = (const u8 *) (req + 1); if (!data->auth_response_valid || len < sizeof(*req) + 42 || pos[0] != 'S' || pos[1] != '=' || hexstr2bin((char *) (pos + 2), recv_response, 20) || @@ -299,6 +323,21 @@ static u8 * eap_mschapv2_success(struct eap_sm *sm, ret->allowNotifications = FALSE; data->success = 1; + if (data->prev_error == ERROR_PASSWD_EXPIRED) { + struct wpa_ssid *config = eap_get_config(sm); + if (config && config->new_password) { + wpa_msg(sm->msg_ctx, MSG_INFO, + WPA_EVENT_PASSWORD_CHANGED + "EAP-MSCHAPV2: Password changed successfully"); + data->prev_error = 0; + free(config->password); + config->password = config->new_password; + config->new_password = NULL; + config->password_len = config->new_password_len; + config->new_password_len = 0; + } + } + return (u8 *) resp; } @@ -382,27 +421,152 @@ static int eap_mschapv2_failure_txt(struct eap_sm *sm, "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " "%d)", msg, retry == 1 ? "" : "not ", data->prev_error); - if (retry == 1 && config) { + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3 && config) { + if (config->new_password == NULL) { + wpa_msg(sm->msg_ctx, MSG_INFO, + "EAP-MSCHAPV2: Password expired - password " + "change required"); + eap_sm_request_new_password(sm, config); + } + } else if (retry == 1 && config) { /* TODO: could prevent the current password from being used * again at least for some period of time */ - eap_sm_request_identity(sm, config); + if (!config->mschapv2_retry) + eap_sm_request_identity(sm, config); eap_sm_request_password(sm, config); + config->mschapv2_retry = 1; } else if (config) { /* TODO: prevent retries using same username/password */ + config->mschapv2_retry = 0; } return retry == 1; } +static u8 * eap_mschapv2_change_password(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + const struct eap_mschapv2_hdr *req, + size_t *respDataLen) +{ + struct eap_mschapv2_hdr *resp; + int ms_len, i; + u8 *peer_challenge, *username, *pos; + size_t username_len; + struct wpa_ssid *config = eap_get_config(sm); + + if (config == NULL || config->identity == NULL || + config->new_password == NULL || config->password == NULL) + return NULL; + + /* + * MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). + */ + username = config->identity; + username_len = config->identity_len; + for (i = 0; i < username_len; i++) { + if (username[i] == '\\') { + username_len -= i + 1; + username += i + 1; + break; + } + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = TRUE; + + *respDataLen = 591; + resp = malloc(*respDataLen); + if (resp == NULL) { + return NULL; + } + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16((u16) *respDataLen); + resp->type = EAP_TYPE_MSCHAPV2; + resp->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; + resp->mschapv2_id = req->mschapv2_id + 1; + ms_len = *respDataLen - 5; + WPA_PUT_BE16(resp->ms_length, ms_len); + pos = (u8 *) (resp + 1); + + /* Encrypted-Password */ + new_password_encrypted_with_old_nt_password_hash( + config->new_password, config->new_password_len, + config->password, config->password_len, pos); + pos += 516; + + /* Encrypted-Hash */ + old_nt_password_hash_encrypted_with_new_nt_password_hash( + config->new_password, config->new_password_len, + config->password, config->password_len, pos); + pos += 16; + + /* Peer-Challenge */ + peer_challenge = pos; + if (hostapd_get_rand(peer_challenge, 16)) { + free(resp); + return NULL; + } + pos += 16; + + /* Reserved, must be zero */ + memset(pos, 0, 8); + pos += 8; + + /* NT-Response */ + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", + data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", + peer_challenge, 16); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", + username, username_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", + config->new_password, config->new_password_len); + generate_nt_response(data->passwd_change_challenge, peer_challenge, + username, username_len, + config->new_password, config->new_password_len, + pos); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", pos, 24); + + /* Authenticator response is not really needed yet, but calculate it + * here so that challenges need not be saved. */ + generate_authenticator_response(config->new_password, + config->new_password_len, + peer_challenge, + data->passwd_change_challenge, + username, username_len, pos, + data->auth_response); + data->auth_response_valid = 1; + + pos += 24; + + /* Flags */ + *pos++ = 0; + *pos++ = 0; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " + "(change pw)", resp->identifier, resp->mschapv2_id); + + return (u8 *) resp; +} + + static u8 * eap_mschapv2_failure(struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, - struct eap_mschapv2_hdr *req, + const struct eap_mschapv2_hdr *req, size_t *respDataLen) { struct eap_mschapv2_hdr *resp; - u8 *msdata = (u8 *) (req + 1); + const u8 *msdata = (const u8 *) (req + 1); char *buf; int len = be_to_host16(req->length) - sizeof(*req); int retry = 0; @@ -423,10 +587,19 @@ static u8 * eap_mschapv2_failure(struct eap_sm *sm, ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; - if (retry) { + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3) { + struct wpa_ssid *config = eap_get_config(sm); + if (config && config->new_password) + return eap_mschapv2_change_password(sm, data, ret, req, + respDataLen); + if (config && config->pending_req_new_password) + return NULL; + } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { /* TODO: could try to retry authentication, e.g, after having * changed the username/password. In this case, EAP MS-CHAP-v2 * Failure Response would not be sent here. */ + return NULL; } *respDataLen = 6; @@ -447,13 +620,15 @@ static u8 * eap_mschapv2_failure(struct eap_sm *sm, static u8 * eap_mschapv2_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_mschapv2_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); - struct eap_mschapv2_hdr *req; - int ms_len, len; + const struct eap_mschapv2_hdr *req; + int ms_len, using_prev_challenge = 0; + const u8 *pos; + size_t len; if (config == NULL || config->identity == NULL) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); @@ -469,19 +644,28 @@ static u8 * eap_mschapv2_process(struct eap_sm *sm, void *priv, return NULL; } - req = (struct eap_mschapv2_hdr *) reqData; - len = be_to_host16(req->length); - if (len < sizeof(*req) + 2 || req->type != EAP_TYPE_MSCHAPV2 || - len > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); + if (config->mschapv2_retry && data->prev_challenge && + data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet " + "with the previous challenge"); + + reqData = data->prev_challenge; + reqDataLen = data->prev_challenge_len; + using_prev_challenge = 1; + config->mschapv2_retry = 0; + } + + pos = eap_hdr_validate(EAP_TYPE_MSCHAPV2, reqData, reqDataLen, &len); + if (pos == NULL || len < 5) { ret->ignore = TRUE; return NULL; } - - ms_len = ((int) req->ms_length[0] << 8) | req->ms_length[1]; + req = (const struct eap_mschapv2_hdr *) reqData; + len = be_to_host16(req->length); + ms_len = WPA_GET_BE16(req->ms_length); if (ms_len != len - 5) { - wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%d " - "ms_len=%d", len, ms_len); + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu " + "ms_len=%d", (unsigned long) len, ms_len); if (sm->workaround) { /* Some authentication servers use invalid ms_len, * ignore it for interoperability. */ @@ -493,8 +677,19 @@ static u8 * eap_mschapv2_process(struct eap_sm *sm, void *priv, } } + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d", + req->identifier, req->mschapv2_id); + switch (req->op_code) { case MSCHAPV2_OP_CHALLENGE: + if (!using_prev_challenge) { + free(data->prev_challenge); + data->prev_challenge = malloc(len); + if (data->prev_challenge) { + data->prev_challenge_len = len; + memcpy(data->prev_challenge, reqData, len); + } + } return eap_mschapv2_challenge(sm, data, ret, req, respDataLen); case MSCHAPV2_OP_SUCCESS: return eap_mschapv2_success(sm, data, ret, req, respDataLen); diff --git a/contrib/wpa_supplicant/eap_otp.c b/contrib/wpa_supplicant/eap_otp.c index e50de63..e7ec44c 100644 --- a/contrib/wpa_supplicant/eap_otp.c +++ b/contrib/wpa_supplicant/eap_otp.c @@ -35,31 +35,29 @@ static void eap_otp_deinit(struct eap_sm *sm, void *priv) static u8 * eap_otp_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *req, *resp; - u8 *pos, *password; + const struct eap_hdr *req; + struct eap_hdr *resp; + const u8 *pos, *password; + u8 *rpos; size_t password_len, len; - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_OTP || - (len = be_to_host16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-OTP: Invalid frame"); + pos = eap_hdr_validate(EAP_TYPE_OTP, reqData, reqDataLen, &len); + if (pos == NULL) { ret->ignore = TRUE; return NULL; } - pos++; + req = (const struct eap_hdr *) reqData; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message", - pos, len - sizeof(*req) - 1); + pos, len); if (config == NULL || (config->password == NULL && config->otp == NULL)) { wpa_printf(MSG_INFO, "EAP-OTP: Password not configured"); - eap_sm_request_otp(sm, config, (char *) pos, - len - sizeof(*req) - 1); + eap_sm_request_otp(sm, config, (const char *) pos, len); ret->ignore = TRUE; return NULL; } @@ -85,9 +83,9 @@ static u8 * eap_otp_process(struct eap_sm *sm, void *priv, resp->code = EAP_CODE_RESPONSE; resp->identifier = req->identifier; resp->length = host_to_be16(*respDataLen); - pos = (u8 *) (resp + 1); - *pos++ = EAP_TYPE_OTP; - memcpy(pos, password, password_len); + rpos = (u8 *) (resp + 1); + *rpos++ = EAP_TYPE_OTP; + memcpy(rpos, password, password_len); wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response", password, password_len); diff --git a/contrib/wpa_supplicant/eap_pax.c b/contrib/wpa_supplicant/eap_pax.c new file mode 100644 index 0000000..b590b90 --- /dev/null +++ b/contrib/wpa_supplicant/eap_pax.c @@ -0,0 +1,510 @@ +/* + * WPA Supplicant / EAP-PAX (draft-clancy-eap-pax-04.txt) + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "eap_pax_common.h" +#include "sha1.h" +#include "crypto.h" + +/* + * Note: only PAX_STD subprotocol is currently supported + */ + +struct eap_pax_data { + enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state; + u8 mac_id, dh_group_id, public_key_id; + union { + u8 e[2 * EAP_PAX_RAND_LEN]; + struct { + u8 x[EAP_PAX_RAND_LEN]; /* server rand */ + u8 y[EAP_PAX_RAND_LEN]; /* client rand */ + } r; + } rand; + char *cid; + size_t cid_len; + u8 ak[EAP_PAX_AK_LEN]; + u8 mk[EAP_PAX_MK_LEN]; + u8 ck[EAP_PAX_CK_LEN]; + u8 ick[EAP_PAX_ICK_LEN]; +}; + + +static void eap_pax_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_pax_init(struct eap_sm *sm) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_pax_data *data; + + if (config == NULL || !config->nai || + (!config->eappsk && !config->password)) { + wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key " + "(eappsk/password) not configured"); + return NULL; + } + + if (config->eappsk && config->eappsk_len != EAP_PAX_AK_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: incorrect key length (eappsk); " + "expected %d", EAP_PAX_AK_LEN); + return NULL; + } + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + data->state = PAX_INIT; + + data->cid = malloc(config->nai_len); + if (data->cid == NULL) { + eap_pax_deinit(sm, data); + return NULL; + } + memcpy(data->cid, config->nai, config->nai_len); + data->cid_len = config->nai_len; + + if (config->eappsk) { + memcpy(data->ak, config->eappsk, EAP_PAX_AK_LEN); + } else { + u8 hash[SHA1_MAC_LEN]; + const unsigned char *addr[1]; + size_t len[1]; + addr[0] = config->password; + len[0] = config->password_len; + sha1_vector(1, addr, len, hash); + memcpy(data->ak, hash, EAP_PAX_AK_LEN); + } + + return data; +} + + +static void eap_pax_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + free(data->cid); + free(data); +} + + +static struct eap_pax_hdr * eap_pax_alloc_resp(const struct eap_pax_hdr *req, + u16 resp_len, u8 op_code) +{ + struct eap_pax_hdr *resp; + + resp = malloc(resp_len); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(resp_len); + resp->type = EAP_TYPE_PAX; + resp->op_code = op_code; + resp->flags = 0; + resp->mac_id = req->mac_id; + resp->dh_group_id = req->dh_group_id; + resp->public_key_id = req->public_key_id; + return resp; +} + + +static u8 * eap_pax_process_std_1(struct eap_sm *sm, struct eap_pax_data *data, + struct eap_method_ret *ret, + const u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + const struct eap_pax_hdr *req; + struct eap_pax_hdr *resp; + const u8 *pos; + u8 *rpos; + size_t left; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)"); + req = (const struct eap_pax_hdr *) reqData; + + if (data->state != PAX_INIT) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - " + "ignored"); + ret->ignore = TRUE; + return NULL; + } + + left = reqDataLen - sizeof(*req); + + if (left < 2 + EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short " + "payload"); + ret->ignore = TRUE; + return NULL; + } + + pos = (const u8 *) (req + 1); + if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A " + "length %d (expected %d)", + WPA_GET_BE16(pos), EAP_PAX_RAND_LEN); + ret->ignore = TRUE; + return NULL; + } + + pos += 2; + left -= 2; + memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)", + data->rand.r.x, EAP_PAX_RAND_LEN); + pos += EAP_PAX_RAND_LEN; + left -= EAP_PAX_RAND_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + if (hostapd_get_rand(data->rand.r.y, EAP_PAX_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); + ret->ignore = TRUE; + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", + data->rand.r.y, EAP_PAX_RAND_LEN); + + if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, + data->mk, data->ck, data->ick) < 0) + { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)"); + + *respDataLen = sizeof(*resp) + 2 + EAP_PAX_RAND_LEN + + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN; + resp = eap_pax_alloc_resp(req, *respDataLen, EAP_PAX_OP_STD_2); + if (resp == NULL) + return NULL; + + rpos = (u8 *) (resp + 1); + *rpos++ = 0; + *rpos++ = EAP_PAX_RAND_LEN; + memcpy(rpos, data->rand.r.y, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)", + rpos, EAP_PAX_RAND_LEN); + rpos += EAP_PAX_RAND_LEN; + + WPA_PUT_BE16(rpos, data->cid_len); + rpos += 2; + memcpy(rpos, data->cid, data->cid_len); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", rpos, data->cid_len); + rpos += data->cid_len; + + *rpos++ = 0; + *rpos++ = EAP_PAX_MAC_LEN; + eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", + rpos, EAP_PAX_MAC_LEN); + rpos += EAP_PAX_MAC_LEN; + + eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, + (u8 *) resp, *respDataLen - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); + rpos += EAP_PAX_ICV_LEN; + + data->state = PAX_STD_2_SENT; + data->mac_id = req->mac_id; + data->dh_group_id = req->dh_group_id; + data->public_key_id = req->public_key_id; + + return (u8 *) resp; +} + + +static u8 * eap_pax_process_std_3(struct eap_sm *sm, struct eap_pax_data *data, + struct eap_method_ret *ret, + const u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + const struct eap_pax_hdr *req; + struct eap_pax_hdr *resp; + u8 *rpos, mac[EAP_PAX_MAC_LEN]; + const u8 *pos; + size_t left; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)"); + req = (const struct eap_pax_hdr *) reqData; + + if (data->state != PAX_STD_2_SENT) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - " + "ignored"); + ret->ignore = TRUE; + return NULL; + } + + left = reqDataLen - sizeof(*req); + + if (left < 2 + EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short " + "payload"); + ret->ignore = TRUE; + return NULL; + } + + pos = (const u8 *) (req + 1); + if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect " + "MAC_CK length %d (expected %d)", + WPA_GET_BE16(pos), EAP_PAX_MAC_LEN); + ret->ignore = TRUE; + return NULL; + } + pos += 2; + left -= 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", + pos, EAP_PAX_MAC_LEN); + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, NULL, 0, mac); + if (memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " + "received"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)", + mac, EAP_PAX_MAC_LEN); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + + pos += EAP_PAX_MAC_LEN; + left -= EAP_PAX_MAC_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)"); + + *respDataLen = sizeof(*resp) + EAP_PAX_ICV_LEN; + resp = eap_pax_alloc_resp(req, *respDataLen, EAP_PAX_OP_ACK); + if (resp == NULL) + return NULL; + + rpos = (u8 *) (resp + 1); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + (u8 *) resp, *respDataLen - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); + + data->state = PAX_DONE; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + + return (u8 *) resp; +} + + +static u8 * eap_pax_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_pax_data *data = priv; + const struct eap_pax_hdr *req; + u8 *resp, icvbuf[EAP_PAX_ICV_LEN]; + const u8 *icv, *pos; + size_t len; + u16 flen; + + pos = eap_hdr_validate(EAP_TYPE_PAX, reqData, reqDataLen, &len); + if (pos == NULL || len < EAP_PAX_ICV_LEN) { + ret->ignore = TRUE; + return NULL; + } + req = (const struct eap_pax_hdr *) reqData; + flen = be_to_host16(req->length) - EAP_PAX_ICV_LEN; + + wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " + "flags 0x%x mac_id 0x%x dh_group_id 0x%x " + "public_key_id 0x%x", + req->op_code, req->flags, req->mac_id, req->dh_group_id, + req->public_key_id); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", + pos, len - EAP_PAX_ICV_LEN); + + if (data->state != PAX_INIT && data->mac_id != req->mac_id) { + wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->mac_id, req->mac_id); + ret->ignore = TRUE; + return NULL; + } + + if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) { + wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->dh_group_id, req->dh_group_id); + ret->ignore = TRUE; + return NULL; + } + + if (data->state != PAX_INIT && + data->public_key_id != req->public_key_id) { + wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->public_key_id, req->public_key_id); + ret->ignore = TRUE; + return NULL; + } + + /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x", + req->mac_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x", + req->dh_group_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x", + req->public_key_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_MF) { + /* TODO: add support for reassembling fragments */ + wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - " + "ignored packet"); + ret->ignore = TRUE; + return NULL; + } + + icv = pos + len - EAP_PAX_ICV_LEN; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); + if (req->op_code == EAP_PAX_OP_STD_1) { + eap_pax_mac(req->mac_id, (u8 *) "", 0, + reqData, flen, NULL, 0, NULL, 0, icvbuf); + } else { + eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, + reqData, flen, NULL, 0, NULL, 0, icvbuf); + } + if (memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the " + "message"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (req->op_code) { + case EAP_PAX_OP_STD_1: + resp = eap_pax_process_std_1(sm, data, ret, reqData, flen, + respDataLen); + break; + case EAP_PAX_OP_STD_3: + resp = eap_pax_process_std_3(sm, data, ret, reqData, flen, + respDataLen); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown " + "op_code %d", req->op_code); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return resp; +} + + +static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == PAX_DONE; +} + + +static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != PAX_DONE) + return NULL; + + key = malloc(EAP_PAX_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_PAX_MSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_PAX_MSK_LEN, key); + + return key; +} + + +const struct eap_method eap_method_pax = +{ + .method = EAP_TYPE_PAX, + .name = "PAX", + .init = eap_pax_init, + .deinit = eap_pax_deinit, + .process = eap_pax_process, + .isKeyAvailable = eap_pax_isKeyAvailable, + .getKey = eap_pax_getKey, +}; diff --git a/contrib/wpa_supplicant/eap_pax_common.c b/contrib/wpa_supplicant/eap_pax_common.c new file mode 100644 index 0000000..d8f4016 --- /dev/null +++ b/contrib/wpa_supplicant/eap_pax_common.c @@ -0,0 +1,152 @@ +/* + * WPA Supplicant / EAP-PAX shared routines + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "sha1.h" +#include "eap_pax_common.h" + + +/** + * eap_pax_kdf - PAX Key Derivation Function + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @key: Secret key (X) + * @key_len: Length of the secret key in bytes + * @identifier: Public identifier for the key (Y) + * @entropy: Exchanged entropy to seed the KDF (Z) + * @entropy_len: Length of the entropy in bytes + * @output_len: Output len in bytes (W) + * @output: Buffer for the derived key + * Returns: 0 on success, -1 failed + * + * draft-clancy-eap-pax-04.txt, chap. 2.5: PAX-KDF-W(X, Y, Z) + */ +int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, + const char *identifier, + const u8 *entropy, size_t entropy_len, + size_t output_len, u8 *output) +{ + u8 mac[SHA1_MAC_LEN]; + u8 counter, *pos; + const u8 *addr[3]; + size_t len[3]; + size_t num_blocks, left; + + num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN; + if (identifier == NULL || num_blocks >= 255) + return -1; + + /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) + return -1; + + addr[0] = (const u8 *) identifier; + len[0] = strlen(identifier); + addr[1] = entropy; + len[1] = entropy_len; + addr[2] = &counter; + len[2] = 1; + + pos = output; + left = output_len; + for (counter = 1; counter <= (u8) num_blocks; counter++) { + size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; + hmac_sha1_vector(key, key_len, 3, addr, len, mac); + memcpy(pos, mac, clen); + pos += clen; + left -= clen; + } + + return 0; +} + + +/** + * eap_pax_mac - EAP-PAX MAC + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @key: Secret key + * @key_len: Length of the secret key in bytes + * @data1: Optional data, first block; %NULL if not used + * @data1_len: Length of data1 in bytes + * @data2: Optional data, second block; %NULL if not used + * @data2_len: Length of data2 in bytes + * @data3: Optional data, third block; %NULL if not used + * @data3_len: Length of data3 in bytes + * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes) + * Returns: 0 on success, -1 on failure + * + * Wrapper function to calculate EAP-PAX MAC. + */ +int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, + const u8 *data1, size_t data1_len, + const u8 *data2, size_t data2_len, + const u8 *data3, size_t data3_len, + u8 *mac) +{ + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[3]; + size_t len[3]; + size_t count; + + /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) + return -1; + + addr[0] = data1; + len[0] = data1_len; + addr[1] = data2; + len[1] = data2_len; + addr[2] = data3; + len[2] = data3_len; + + count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); + hmac_sha1_vector(key, key_len, count, addr, len, hash); + memcpy(mac, hash, EAP_PAX_MAC_LEN); + + return 0; +} + + +/** + * eap_pax_initial_key_derivation - EAP-PAX initial key derivation + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @ak: Authentication Key + * @e: Entropy + * @mk: Buffer for the derived Master Key + * @ck: Buffer for the derived Confirmation Key + * @ick: Buffer for the derived Integrity Check Key + * Returns: 0 on success, -1 on failure + */ +int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, + u8 *mk, u8 *ck, u8 *ick) +{ + wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation"); + if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick)) + return -1; + + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN); + + return 0; +} diff --git a/contrib/wpa_supplicant/eap_pax_common.h b/contrib/wpa_supplicant/eap_pax_common.h new file mode 100644 index 0000000..b5ad6af --- /dev/null +++ b/contrib/wpa_supplicant/eap_pax_common.h @@ -0,0 +1,84 @@ +/* + * WPA Supplicant / EAP-PAX shared routines + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_PAX_COMMON_H +#define EAP_PAX_COMMON_H + +struct eap_pax_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PAX */ + u8 op_code; + u8 flags; + u8 mac_id; + u8 dh_group_id; + u8 public_key_id; + /* Followed by variable length payload and ICV */ +} __attribute__ ((packed)); + + +/* op_code: */ +enum { + EAP_PAX_OP_STD_1 = 0x01, + EAP_PAX_OP_STD_2 = 0x02, + EAP_PAX_OP_STD_3 = 0x03, + EAP_PAX_OP_SEC_1 = 0x11, + EAP_PAX_OP_SEC_2 = 0x12, + EAP_PAX_OP_SEC_3 = 0x13, + EAP_PAX_OP_SEC_4 = 0x14, + EAP_PAX_OP_SEC_5 = 0x15, + EAP_PAX_OP_ACK = 0x21 +}; + +/* flags: */ +#define EAP_PAX_FLAGS_MF 0x01 +#define EAP_PAX_FLAGS_CE 0x02 + +/* mac_id: */ +#define EAP_PAX_MAC_HMAC_SHA1_128 0x01 +#define EAP_PAX_MAC_AES_CBC_MAC_128 0x02 + +/* dh_group_id: */ +#define EAP_PAX_DH_GROUP_NONE 0x00 +#define EAP_PAX_DH_GROUP_3072_MODP 0x01 + +/* public_key_id: */ +#define EAP_PAX_PUBLIC_KEY_NONE 0x00 +#define EAP_PAX_PUBLIC_KEY_RSA_OAEP_2048 0x01 + + +#define EAP_PAX_RAND_LEN 32 +#define EAP_PAX_MSK_LEN 64 +#define EAP_PAX_MAC_LEN 16 +#define EAP_PAX_ICV_LEN 16 +#define EAP_PAX_AK_LEN 16 +#define EAP_PAX_MK_LEN 16 +#define EAP_PAX_CK_LEN 16 +#define EAP_PAX_ICK_LEN 16 + + +int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, + const char *identifier, + const u8 *entropy, size_t entropy_len, + size_t output_len, u8 *output); +int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, + const u8 *data1, size_t data1_len, + const u8 *data2, size_t data2_len, + const u8 *data3, size_t data3_len, + u8 *mac); +int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, + u8 *mk, u8 *ck, u8 *ick); + +#endif /* EAP_PAX_COMMON_H */ diff --git a/contrib/wpa_supplicant/eap_peap.c b/contrib/wpa_supplicant/eap_peap.c index 8ca8ab2..efbb867 100644 --- a/contrib/wpa_supplicant/eap_peap.c +++ b/contrib/wpa_supplicant/eap_peap.c @@ -193,7 +193,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) static int eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data, - int id, u8 *plain, size_t plain_len, + int id, const u8 *plain, size_t plain_len, u8 **out_data, size_t *out_len) { int res; @@ -263,7 +263,7 @@ static int eap_peap_phase2_nak(struct eap_sm *sm, static int eap_peap_phase2_request(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, + const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { @@ -348,7 +348,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm, if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp)) { + config->pending_req_otp || config->pending_req_new_password)) { free(data->pending_phase2_req); data->pending_phase2_req = malloc(len); if (data->pending_phase2_req) { @@ -361,18 +361,20 @@ static int eap_peap_phase2_request(struct eap_sm *sm, } -static int eap_peap_decrypt(struct eap_sm *sm, - struct eap_peap_data *data, +static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, - u8 *in_data, size_t in_len, + const struct eap_hdr *req, + const u8 *in_data, size_t in_len, u8 **out_data, size_t *out_len) { u8 *in_decrypted; - int buf_len, len_decrypted, len, skip_change = 0, res; + int buf_len, len_decrypted, len, skip_change = 0; struct eap_hdr *hdr, *rhdr; u8 *resp = NULL; size_t resp_len; + const u8 *msg; + size_t msg_len; + int need_more_input; wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" " Phase 2", (unsigned long) in_len); @@ -393,9 +395,10 @@ static int eap_peap_decrypt(struct eap_sm *sm, goto continue_req; } - res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); - if (res < 0 || res == 1) - return res; + msg = eap_tls_data_reassemble(sm, &data->ssl, in_data, in_len, + &msg_len, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; if (in_len == 0 && sm->workaround && data->phase2_success) { /* @@ -424,7 +427,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, } len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, + msg, msg_len, in_decrypted, buf_len); free(data->ssl.tls_in); data->ssl.tls_in = NULL; @@ -613,62 +616,22 @@ continue_req: static u8 * eap_peap_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { - struct eap_hdr *req; - int left, res; - unsigned int tls_msg_len; - u8 flags, *pos, *resp, id; + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, *resp, id; + const u8 *pos; struct eap_peap_data *data = priv; - if (tls_get_errors(sm->ssl_ctx)) { - wpa_printf(MSG_INFO, "EAP-PEAP: TLS errors detected"); - ret->ignore = TRUE; + pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, + reqData, reqDataLen, &left, &flags); + if (pos == NULL) return NULL; - } - - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_PEAP || - (left = be_to_host16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); - ret->ignore = TRUE; - return NULL; - } - left -= sizeof(struct eap_hdr); + req = (const struct eap_hdr *) reqData; id = req->identifier; - pos++; - flags = *pos++; - left -= 2; - wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) reqDataLen, flags); - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS " - "length"); - ret->ignore = TRUE; - return NULL; - } - tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | - pos[3]; - wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } - - ret->ignore = FALSE; - ret->methodState = METHOD_CONT; - ret->decision = DECISION_FAIL; - ret->allowNotifications = TRUE; if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " @@ -733,12 +696,12 @@ static u8 * eap_peap_process(struct eap_sm *sm, void *priv, "derive key"); } - if (sm->workaround && data->peap_version == 1 && - data->resuming) { + if (sm->workaround && data->resuming) { /* - * At least one RADIUS server (Aegis v1.1.6; - * but not v1.1.4) seems to be terminating - * PEAPv1 session resumption with outer + * At least few RADIUS servers (Aegis v1.1.6; + * but not v1.1.4; and Cisco ACS) seem to be + * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco + * ACS) session resumption with outer * EAP-Success. This does not seem to follow * draft-josefsson-pppext-eap-tls-eap-05.txt * section 4.2, so only allow this if EAP @@ -746,7 +709,7 @@ static u8 * eap_peap_process(struct eap_sm *sm, void *priv, */ wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - " "allow outer EAP-Success to " - "terminate PEAPv1 resumption"); + "terminate PEAP resumption"); ret->decision = DECISION_COND_SUCC; data->phase2_success = 1; } diff --git a/contrib/wpa_supplicant/eap_psk.c b/contrib/wpa_supplicant/eap_psk.c index 3b325b5..853c79a 100644 --- a/contrib/wpa_supplicant/eap_psk.c +++ b/contrib/wpa_supplicant/eap_psk.c @@ -1,5 +1,5 @@ /* - * WPA Supplicant / EAP-PSK (draft-bersani-eap-psk-05.txt) + * WPA Supplicant / EAP-PSK (draft-bersani-eap-psk-09.txt) * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -10,6 +10,9 @@ * license. * * See README and COPYING for more details. + * + * Note: EAP-PSK is an EAP authentication method and as such, completely + * different from WPA-PSK. This file is not needed for WPA-PSK functionality. */ #include <stdlib.h> @@ -22,128 +25,19 @@ #include "config_ssid.h" #include "md5.h" #include "aes_wrap.h" - - -/* draft-bersani-eap-psk-03.txt mode. This is retained for interop testing and - * will be removed once an AS that supports draft5 becomes available. */ -#define EAP_PSK_DRAFT3 - -#define EAP_PSK_RAND_LEN 16 -#define EAP_PSK_MAC_LEN 16 -#define EAP_PSK_TEK_LEN 16 -#define EAP_PSK_MSK_LEN 64 - -#define EAP_PSK_R_FLAG_CONT 1 -#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 -#define EAP_PSK_R_FLAG_DONE_FAILURE 3 - -/* EAP-PSK First Message (AS -> Supplicant) */ -struct eap_psk_hdr_1 { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ -#ifndef EAP_PSK_DRAFT3 - u8 flags; -#endif /* EAP_PSK_DRAFT3 */ - u8 rand_s[EAP_PSK_RAND_LEN]; -#ifndef EAP_PSK_DRAFT3 - /* Followed by variable length ID_S */ -#endif /* EAP_PSK_DRAFT3 */ -} __attribute__ ((packed)); - -/* EAP-PSK Second Message (Supplicant -> AS) */ -struct eap_psk_hdr_2 { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ -#ifndef EAP_PSK_DRAFT3 - u8 flags; - u8 rand_s[EAP_PSK_RAND_LEN]; -#endif /* EAP_PSK_DRAFT3 */ - u8 rand_p[EAP_PSK_RAND_LEN]; - u8 mac_p[EAP_PSK_MAC_LEN]; - /* Followed by variable length ID_P */ -} __attribute__ ((packed)); - -/* EAP-PSK Third Message (AS -> Supplicant) */ -struct eap_psk_hdr_3 { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ -#ifndef EAP_PSK_DRAFT3 - u8 flags; - u8 rand_s[EAP_PSK_RAND_LEN]; -#endif /* EAP_PSK_DRAFT3 */ - u8 mac_s[EAP_PSK_MAC_LEN]; - /* Followed by variable length PCHANNEL */ -} __attribute__ ((packed)); - -/* EAP-PSK Fourth Message (Supplicant -> AS) */ -struct eap_psk_hdr_4 { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ -#ifndef EAP_PSK_DRAFT3 - u8 flags; - u8 rand_s[EAP_PSK_RAND_LEN]; -#endif /* EAP_PSK_DRAFT3 */ - /* Followed by variable length PCHANNEL */ -} __attribute__ ((packed)); - +#include "eap_psk_common.h" struct eap_psk_data { enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; - u8 rand_s[EAP_PSK_RAND_LEN]; u8 rand_p[EAP_PSK_RAND_LEN]; - u8 ak[16], kdk[16], tek[EAP_PSK_TEK_LEN]; + u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; u8 *id_s, *id_p; size_t id_s_len, id_p_len; u8 key_data[EAP_PSK_MSK_LEN]; }; -#define aes_block_size 16 - - -static void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) -{ - memset(ak, 0, aes_block_size); - aes_128_encrypt_block(psk, ak, ak); - memcpy(kdk, ak, aes_block_size); - ak[aes_block_size - 1] ^= 0x01; - kdk[aes_block_size - 1] ^= 0x02; - aes_128_encrypt_block(psk, ak, ak); - aes_128_encrypt_block(psk, kdk, kdk); -} - - -static void eap_psk_derive_keys(const u8 *kdk, const u8 *rb, u8 *tek, u8 *msk) -{ - u8 hash[aes_block_size]; - u8 counter = 1; - int i; - - aes_128_encrypt_block(kdk, rb, hash); - - hash[aes_block_size - 1] ^= counter; - aes_128_encrypt_block(kdk, hash, tek); - hash[aes_block_size - 1] ^= counter; - counter++; - - for (i = 0; i < EAP_PSK_MSK_LEN / aes_block_size; i++) { - hash[aes_block_size - 1] ^= counter; - aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]); - hash[aes_block_size - 1] ^= counter; - counter++; - } -} - - static void * eap_psk_init(struct eap_sm *sm) { struct wpa_ssid *config = eap_get_config(sm); @@ -159,8 +53,8 @@ static void * eap_psk_init(struct eap_sm *sm) return NULL; memset(data, 0, sizeof(*data)); eap_psk_key_setup(config->eappsk, data->ak, data->kdk); - wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, 16); - wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, 16); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); data->state = PSK_INIT; if (config->nai) { @@ -175,22 +69,6 @@ static void * eap_psk_init(struct eap_sm *sm) return NULL; } -#ifdef EAP_PSK_DRAFT3 - if (config->server_nai) { - data->id_s = malloc(config->server_nai_len); - if (data->id_s) - memcpy(data->id_s, config->server_nai, - config->server_nai_len); - data->id_s_len = config->server_nai_len; - } - if (data->id_s == NULL) { - wpa_printf(MSG_INFO, "EAP-PSK: could not get server identity"); - free(data->id_p); - free(data); - return NULL; - } -#endif /* EAP_PSK_DRAFT3 */ - return data; } @@ -206,17 +84,17 @@ static void eap_psk_deinit(struct eap_sm *sm, void *priv) static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { - struct eap_psk_hdr_1 *hdr1; + const struct eap_psk_hdr_1 *hdr1; struct eap_psk_hdr_2 *hdr2; u8 *resp, *buf, *pos; size_t buflen; wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); - hdr1 = (struct eap_psk_hdr_1 *) reqData; + hdr1 = (const struct eap_psk_hdr_1 *) reqData; if (reqDataLen < sizeof(*hdr1) || be_to_host16(hdr1->length) < sizeof(*hdr1) || be_to_host16(hdr1->length) > reqDataLen) { @@ -228,7 +106,6 @@ static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data, ret->ignore = TRUE; return NULL; } -#ifndef EAP_PSK_DRAFT3 wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); if ((hdr1->flags & 0x03) != 0) { wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", @@ -237,24 +114,20 @@ static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data, ret->decision = DECISION_FAIL; return NULL; } -#endif /* EAP_PSK_DRAFT3 */ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, EAP_PSK_RAND_LEN); - memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); -#ifndef EAP_PSK_DRAFT3 free(data->id_s); data->id_s_len = be_to_host16(hdr1->length) - sizeof(*hdr1); data->id_s = malloc(data->id_s_len); if (data->id_s == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " - "ID_S (len=%d)", data->id_s_len); + "ID_S (len=%lu)", (unsigned long) data->id_s_len); ret->ignore = TRUE; return NULL; } memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", data->id_s, data->id_s_len); -#endif /* EAP_PSK_DRAFT3 */ if (hostapd_get_rand(data->rand_p, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); @@ -271,10 +144,8 @@ static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data, hdr2->identifier = hdr1->identifier; hdr2->length = host_to_be16(*respDataLen); hdr2->type = EAP_TYPE_PSK; -#ifndef EAP_PSK_DRAFT3 hdr2->flags = 1; /* T=1 */ memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); -#endif /* EAP_PSK_DRAFT3 */ memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); memcpy((u8 *) (hdr2 + 1), data->id_p, data->id_p_len); /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ @@ -288,7 +159,7 @@ static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data, pos = buf + data->id_p_len; memcpy(pos, data->id_s, data->id_s_len); pos += data->id_s_len; - memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); + memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN); pos += EAP_PSK_RAND_LEN; memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p); @@ -307,19 +178,20 @@ static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data, static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { - struct eap_psk_hdr_3 *hdr3; + const struct eap_psk_hdr_3 *hdr3; struct eap_psk_hdr_4 *hdr4; - u8 *resp, *buf, *pchannel, *tag, *msg, nonce[16]; + u8 *resp, *buf, *rpchannel, nonce[16], *decrypted; + const u8 *pchannel, *tag, *msg; u8 mac[EAP_PSK_MAC_LEN]; - size_t buflen, left; + size_t buflen, left, data_len; int failed = 0; wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); - hdr3 = (struct eap_psk_hdr_3 *) reqData; + hdr3 = (const struct eap_psk_hdr_3 *) reqData; left = be_to_host16(hdr3->length); if (left < sizeof(*hdr3) || reqDataLen < left) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " @@ -331,8 +203,7 @@ static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data, return NULL; } left -= sizeof(*hdr3); - pchannel = (u8 *) (hdr3 + 1); -#ifndef EAP_PSK_DRAFT3 + pchannel = (const u8 *) (hdr3 + 1); wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); if ((hdr3->flags & 0x03) != 2) { wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", @@ -343,16 +214,6 @@ static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data, } wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, EAP_PSK_RAND_LEN); - /* TODO: would not need to store RAND_S since it is available in this - * message. For now, since we store this anyway, verify that it matches - * with whatever the server is sending. */ - if (memcmp(hdr3->rand_s, data->rand_s, EAP_PSK_RAND_LEN) != 0) { - wpa_printf(MSG_ERROR, "EAP-PSK: RAND_S did not match"); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - return NULL; - } -#endif /* EAP_PSK_DRAFT3 */ wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); @@ -404,25 +265,29 @@ static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data, wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", reqData, 5); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); -#ifdef EAP_PSK_DRAFT3 - if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), - reqData, 5, msg, left, tag)) -#else /* EAP_PSK_DRAFT3 */ + decrypted = malloc(left); + if (decrypted == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + memcpy(decrypted, msg, left); + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), - reqData, 22, msg, left, tag)) -#endif /* EAP_PSK_DRAFT3 */ - { + reqData, 22, decrypted, left, tag)) { wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + free(decrypted); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", - msg, left); + decrypted, left); /* Verify R flag */ - switch (msg[0] >> 6) { + switch (decrypted[0] >> 6) { case EAP_PSK_R_FLAG_CONT: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); - return NULL; + failed = 1; + break; case EAP_PSK_R_FLAG_DONE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); break; @@ -435,37 +300,48 @@ static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data, } *respDataLen = sizeof(*hdr4) + 4 + 16 + 1; - resp = malloc(*respDataLen); - if (resp == NULL) + resp = malloc(*respDataLen + 1); + if (resp == NULL) { + free(decrypted); return NULL; + } hdr4 = (struct eap_psk_hdr_4 *) resp; hdr4->code = EAP_CODE_RESPONSE; hdr4->identifier = hdr3->identifier; hdr4->length = host_to_be16(*respDataLen); hdr4->type = EAP_TYPE_PSK; -#ifndef EAP_PSK_DRAFT3 hdr4->flags = 3; /* T=3 */ memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); -#endif /* EAP_PSK_DRAFT3 */ - pchannel = (u8 *) (hdr4 + 1); + rpchannel = (u8 *) (hdr4 + 1); /* nonce++ */ inc_byte_array(nonce, sizeof(nonce)); - memcpy(pchannel, nonce + 12, 4); + memcpy(rpchannel, nonce + 12, 4); - pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + data_len = 1; + if (decrypted[0] & EAP_PSK_E_FLAG) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag"); + failed = 1; + rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) | + EAP_PSK_E_FLAG; + if (left > 1) { + /* Add empty EXT_Payload with same EXT_Type */ + (*respDataLen)++; + hdr4->length = host_to_be16(*respDataLen); + rpchannel[4 + 16 + 1] = decrypted[1]; + data_len++; + } + } else if (failed) + rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6; + else + rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", - pchannel + 4 + 16, 1); -#ifdef EAP_PSK_DRAFT3 - aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), resp, 5, - pchannel + 4 + 16, 1, pchannel + 4); -#else /* EAP_PSK_DRAFT3 */ + rpchannel + 4 + 16, data_len); aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), resp, 22, - pchannel + 4 + 16, 1, pchannel + 4); -#endif /* EAP_PSK_DRAFT3 */ + rpchannel + 4 + 16, data_len, rpchannel + 4); wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", - pchannel, 4 + 16 + 1); + rpchannel, 4 + 16 + data_len); wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", failed ? "un" : ""); @@ -473,31 +349,31 @@ static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data, ret->methodState = METHOD_DONE; ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; + free(decrypted); + return resp; } static u8 * eap_psk_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_psk_data *data = priv; - struct eap_hdr *req; - u8 *pos, *resp = NULL; + const u8 *pos; + u8 *resp = NULL; size_t len; - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_PSK || - (len = be_to_host16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + pos = eap_hdr_validate(EAP_TYPE_PSK, reqData, reqDataLen, &len); + if (pos == NULL) { ret->ignore = TRUE; return NULL; } + len += sizeof(struct eap_hdr) + 1; ret->ignore = FALSE; - ret->methodState = METHOD_CONT; + ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; diff --git a/contrib/wpa_supplicant/eap_psk_common.c b/contrib/wpa_supplicant/eap_psk_common.c new file mode 100644 index 0000000..24de66c --- /dev/null +++ b/contrib/wpa_supplicant/eap_psk_common.c @@ -0,0 +1,57 @@ +/* + * WPA Supplicant / EAP-PSK shared routines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "aes_wrap.h" +#include "eap_psk_common.h" + +#define aes_block_size 16 + + +void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) +{ + memset(ak, 0, aes_block_size); + aes_128_encrypt_block(psk, ak, ak); + memcpy(kdk, ak, aes_block_size); + ak[aes_block_size - 1] ^= 0x01; + kdk[aes_block_size - 1] ^= 0x02; + aes_128_encrypt_block(psk, ak, ak); + aes_128_encrypt_block(psk, kdk, kdk); +} + + +void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk) +{ + u8 hash[aes_block_size]; + u8 counter = 1; + int i; + + aes_128_encrypt_block(kdk, rand_p, hash); + + hash[aes_block_size - 1] ^= counter; + aes_128_encrypt_block(kdk, hash, tek); + hash[aes_block_size - 1] ^= counter; + counter++; + + for (i = 0; i < EAP_PSK_MSK_LEN / aes_block_size; i++) { + hash[aes_block_size - 1] ^= counter; + aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]); + hash[aes_block_size - 1] ^= counter; + counter++; + } +} diff --git a/contrib/wpa_supplicant/eap_psk_common.h b/contrib/wpa_supplicant/eap_psk_common.h new file mode 100644 index 0000000..5dd3a10 --- /dev/null +++ b/contrib/wpa_supplicant/eap_psk_common.h @@ -0,0 +1,92 @@ +/* + * WPA Supplicant / EAP-PSK shared routines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_PSK_COMMON_H +#define EAP_PSK_COMMON_H + + +#define EAP_PSK_RAND_LEN 16 +#define EAP_PSK_MAC_LEN 16 +#define EAP_PSK_TEK_LEN 16 +#define EAP_PSK_MSK_LEN 64 +#define EAP_PSK_PSK_LEN 16 +#define EAP_PSK_AK_LEN 16 +#define EAP_PSK_KDK_LEN 16 + +#define EAP_PSK_R_FLAG_CONT 1 +#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 +#define EAP_PSK_R_FLAG_DONE_FAILURE 3 +#define EAP_PSK_E_FLAG 0x20 + +/* Shared prefix for all EAP-PSK frames */ +struct eap_psk_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; +} __attribute__ ((packed)); + +/* EAP-PSK First Message (AS -> Supplicant) */ +struct eap_psk_hdr_1 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length ID_S */ +} __attribute__ ((packed)); + +/* EAP-PSK Second Message (Supplicant -> AS) */ +struct eap_psk_hdr_2 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 mac_p[EAP_PSK_MAC_LEN]; + /* Followed by variable length ID_P */ +} __attribute__ ((packed)); + +/* EAP-PSK Third Message (AS -> Supplicant) */ +struct eap_psk_hdr_3 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 mac_s[EAP_PSK_MAC_LEN]; + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + +/* EAP-PSK Fourth Message (Supplicant -> AS) */ +struct eap_psk_hdr_4 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + + +void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk); +void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk); + +#endif /* EAP_PSK_COMMON_H */ diff --git a/contrib/wpa_supplicant/eap_sim.c b/contrib/wpa_supplicant/eap_sim.c index f7ce191..ca9d737 100644 --- a/contrib/wpa_supplicant/eap_sim.c +++ b/contrib/wpa_supplicant/eap_sim.c @@ -20,7 +20,7 @@ #include "eap_i.h" #include "wpa_supplicant.h" #include "config_ssid.h" -#include "sha1.h" +#include "crypto.h" #include "pcsc_funcs.h" #include "eap_sim_common.h" @@ -198,8 +198,7 @@ static void eap_sim_derive_mk(struct eap_sim_data *data, addr[4] = sel_ver; len[4] = 2; - sel_ver[0] = data->selected_version >> 8; - sel_ver[1] = data->selected_version & 0xff; + WPA_PUT_BE16(sel_ver, data->selected_version); /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ sha1_vector(5, addr, len, data->mk); @@ -277,7 +276,7 @@ static int eap_sim_learn_ids(struct eap_sim_data *data, static u8 * eap_sim_client_error(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen, int err) { struct eap_sim_msg *msg; @@ -295,7 +294,7 @@ static u8 * eap_sim_client_error(struct eap_sm *sm, struct eap_sim_data *data, static u8 * eap_sim_response_start(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen, enum eap_sim_id_req id_req) { @@ -324,7 +323,7 @@ static u8 * eap_sim_response_start(struct eap_sm *sm, eap_sim_clear_identities(data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", - req->identifier); + req->identifier); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT", @@ -349,13 +348,13 @@ static u8 * eap_sim_response_start(struct eap_sm *sm, static u8 * eap_sim_response_challenge(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen) { struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", - req->identifier); + req->identifier); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_MAC"); @@ -368,7 +367,7 @@ static u8 * eap_sim_response_challenge(struct eap_sm *sm, static u8 * eap_sim_response_reauth(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen, int counter_too_small) { struct eap_sim_msg *msg; @@ -408,7 +407,7 @@ static u8 * eap_sim_response_reauth(struct eap_sm *sm, static u8 * eap_sim_response_notification(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t *respDataLen, u16 notification) { @@ -446,7 +445,7 @@ static u8 * eap_sim_response_notification(struct eap_sm *sm, static u8 * eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, size_t reqDataLen, + const struct eap_hdr *req, size_t reqDataLen, size_t *respDataLen, struct eap_sim_attrs *attr) { @@ -524,7 +523,8 @@ static u8 * eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data, static u8 * eap_sim_process_challenge(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, size_t reqDataLen, + const struct eap_hdr *req, + size_t reqDataLen, size_t *respDataLen, struct eap_sim_attrs *attr) { @@ -595,8 +595,9 @@ static u8 * eap_sim_process_challenge(struct eap_sm *sm, "derivation", identity, identity_len); eap_sim_derive_mk(data, identity, identity_len); eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); - if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, - data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + if (eap_sim_verify_mac(data->k_aut, (const u8 *) req, reqDataLen, + attr->mac, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " "used invalid AT_MAC"); return eap_sim_client_error(sm, data, req, respDataLen, @@ -610,14 +611,17 @@ static u8 * eap_sim_process_challenge(struct eap_sm *sm, CLEAR_EAP_ID); if (attr->encr_data) { - if (eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, - 0)) { + u8 *decrypted; + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, + &eattr, 0); + if (decrypted == NULL) { return eap_sim_client_error( sm, data, req, respDataLen, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } eap_sim_learn_ids(data, &eattr); + free(decrypted); } if (data->state != FAILURE) @@ -634,11 +638,12 @@ static u8 * eap_sim_process_challenge(struct eap_sm *sm, static int eap_sim_process_notification_reauth(struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t reqDataLen, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; + u8 *decrypted; if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after " @@ -646,8 +651,10 @@ static int eap_sim_process_notification_reauth(struct eap_sim_data *data, return -1; } - if (eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, 0)) { + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " "data from notification message"); return -1; @@ -657,15 +664,17 @@ static int eap_sim_process_notification_reauth(struct eap_sim_data *data, wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification " "message does not match with counter in reauth " "message"); + free(decrypted); return -1; } + free(decrypted); return 0; } static int eap_sim_process_notification_auth(struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t reqDataLen, struct eap_sim_attrs *attr) { @@ -695,7 +704,7 @@ static int eap_sim_process_notification_auth(struct eap_sim_data *data, static u8 * eap_sim_process_notification(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t reqDataLen, size_t *respDataLen, struct eap_sim_attrs *attr) @@ -732,12 +741,13 @@ static u8 * eap_sim_process_notification(struct eap_sm *sm, static u8 * eap_sim_process_reauthentication(struct eap_sm *sm, struct eap_sim_data *data, - struct eap_hdr *req, + const struct eap_hdr *req, size_t reqDataLen, size_t *respDataLen, struct eap_sim_attrs *attr) { struct eap_sim_attrs eattr; + u8 *decrypted; wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication"); @@ -749,7 +759,7 @@ static u8 * eap_sim_process_reauthentication(struct eap_sm *sm, } data->reauth = 1; - if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, + if (eap_sim_verify_mac(data->k_aut, (const u8 *) req, reqDataLen, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " "did not have valid AT_MAC"); @@ -764,8 +774,10 @@ static u8 * eap_sim_process_reauthentication(struct eap_sm *sm, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - if (eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, 0)) { + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " "data from reauthentication message"); return eap_sim_client_error(sm, data, req, respDataLen, @@ -776,6 +788,7 @@ static u8 * eap_sim_process_reauthentication(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet", !eattr.nonce_s ? " AT_NONCE_S" : "", eattr.counter < 0 ? " AT_COUNTER" : ""); + free(decrypted); return eap_sim_client_error(sm, data, req, respDataLen, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } @@ -794,6 +807,7 @@ static u8 * eap_sim_process_reauthentication(struct eap_sm *sm, data->last_eap_identity_len = data->reauth_id_len; data->reauth_id = NULL; data->reauth_id_len = 0; + free(decrypted); return eap_sim_response_reauth(sm, data, req, respDataLen, 1); } data->counter = eattr.counter; @@ -818,19 +832,21 @@ static u8 * eap_sim_process_reauthentication(struct eap_sm *sm, "fast reauths performed - force fullauth"); eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); } + free(decrypted); return eap_sim_response_reauth(sm, data, req, respDataLen, 0); } static u8 * eap_sim_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { struct eap_sim_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); - struct eap_hdr *req; - u8 *pos, subtype, *res; + const struct eap_hdr *req; + u8 subtype, *res; + const u8 *pos; struct eap_sim_attrs attr; size_t len; @@ -842,21 +858,19 @@ static u8 * eap_sim_process(struct eap_sm *sm, void *priv, return NULL; } - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_SIM || - (len = be_to_host16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); + pos = eap_hdr_validate(EAP_TYPE_SIM, reqData, reqDataLen, &len); + if (pos == NULL || len < 1) { ret->ignore = TRUE; return NULL; } + req = (const struct eap_hdr *) reqData; + len = be_to_host16(req->length); ret->ignore = FALSE; - ret->methodState = METHOD_CONT; + ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = TRUE; - pos++; subtype = *pos++; wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype); pos += 2; /* Reserved */ @@ -901,7 +915,7 @@ done: ret->decision = DECISION_FAIL; ret->methodState = METHOD_DONE; } else if (data->state == SUCCESS) { - ret->decision = DECISION_UNCOND_SUCC; + ret->decision = DECISION_COND_SUCC; ret->methodState = METHOD_DONE; } diff --git a/contrib/wpa_supplicant/eap_sim_common.c b/contrib/wpa_supplicant/eap_sim_common.c index 98f4fb7..75947b7 100644 --- a/contrib/wpa_supplicant/eap_sim_common.c +++ b/contrib/wpa_supplicant/eap_sim_common.c @@ -19,14 +19,11 @@ #include "common.h" #include "eap_i.h" #include "sha1.h" +#include "crypto.h" #include "aes_wrap.h" #include "eap_sim_common.h" -#define MSK_LEN 8 -#define EMSK_LEN 8 - - static void eap_sim_prf(const u8 *key, u8 *x, size_t xlen) { u8 xkey[64]; @@ -86,22 +83,17 @@ void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk) memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); pos += EAP_SIM_K_AUT_LEN; memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); - pos += MSK_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", k_encr, EAP_SIM_K_ENCR_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", k_aut, EAP_SIM_K_ENCR_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MSK", - msk, MSK_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Ext. MSK", - msk + MSK_LEN, EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material", - msk, EAP_SIM_KEYING_DATA_LEN); + msk, EAP_SIM_KEYING_DATA_LEN); } -void eap_sim_derive_keys_reauth(unsigned int _counter, +void eap_sim_derive_keys_reauth(u16 _counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, const u8 *mk, u8 *msk) { @@ -119,8 +111,7 @@ void eap_sim_derive_keys_reauth(unsigned int _counter, addr[3] = mk; len[3] = EAP_SIM_MK_LEN; - counter[0] = _counter >> 8; - counter[1] = _counter & 0xff; + WPA_PUT_BE16(counter, _counter); wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", @@ -135,34 +126,37 @@ void eap_sim_derive_keys_reauth(unsigned int _counter, wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); eap_sim_prf(xkey, msk, EAP_SIM_KEYING_DATA_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: MSK", msk, MSK_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: Ext. MSK", msk + MSK_LEN, EMSK_LEN); wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material", msk, EAP_SIM_KEYING_DATA_LEN); } -int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac, - u8 *extra, size_t extra_len) +int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, + const u8 *mac, const u8 *extra, size_t extra_len) { unsigned char hmac[SHA1_MAC_LEN]; const u8 *addr[2]; size_t len[2]; - u8 rx_mac[EAP_SIM_MAC_LEN]; + u8 *tmp; + + if (mac == NULL || req_len < EAP_SIM_MAC_LEN || mac < req || + mac > req + req_len - EAP_SIM_MAC_LEN) + return -1; - if (mac == NULL) + tmp = malloc(req_len); + if (tmp == NULL) return -1; - addr[0] = req; + addr[0] = tmp; len[0] = req_len; addr[1] = extra; len[1] = extra_len; /* HMAC-SHA1-128 */ - memcpy(rx_mac, mac, EAP_SIM_MAC_LEN); - memset(mac, 0, EAP_SIM_MAC_LEN); + memcpy(tmp, req, req_len); + memset(tmp + (mac - req), 0, EAP_SIM_MAC_LEN); hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); - memcpy(mac, rx_mac, EAP_SIM_MAC_LEN); + free(tmp); return (memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; } @@ -187,10 +181,10 @@ void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, } -int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, int aka, - int encr) +int eap_sim_parse_attr(const u8 *start, const u8 *end, + struct eap_sim_attrs *attr, int aka, int encr) { - u8 *pos = start, *apos; + const u8 *pos = start, *apos; size_t alen, plen; int list_len, i; @@ -473,25 +467,35 @@ int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, int aka, } -int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len, - const u8 *iv, struct eap_sim_attrs *attr, int aka) +u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, + size_t encr_data_len, const u8 *iv, + struct eap_sim_attrs *attr, int aka) { + u8 *decrypted; + if (!iv) { wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); - return -1; + return NULL; } - aes_128_cbc_decrypt(k_encr, iv, encr_data, encr_data_len); + + decrypted = malloc(encr_data_len); + if (decrypted == NULL) + return NULL; + memcpy(decrypted, encr_data, encr_data_len); + + aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", - encr_data, encr_data_len); + decrypted, encr_data_len); - if (eap_sim_parse_attr(encr_data, encr_data + encr_data_len, attr, + if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr, aka, 1)) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " "decrypted AT_ENCR_DATA"); - return -1; + free(decrypted); + return NULL; } - return 0; + return decrypted; } @@ -628,8 +632,8 @@ u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, start = pos = msg->buf + msg->used; *pos++ = attr; *pos++ = attr_len / 4; - *pos++ = value >> 8; - *pos++ = value & 0xff; + WPA_PUT_BE16(pos, value); + pos += 2; if (data) memcpy(pos, data, len); if (pad_len) { @@ -709,7 +713,9 @@ int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) void eap_sim_report_notification(void *msg_ctx, int notification, int aka) { +#ifndef CONFIG_NO_STDOUT_DEBUG const char *type = aka ? "AKA" : "SIM"; +#endif /* CONFIG_NO_STDOUT_DEBUG */ switch (notification) { case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: diff --git a/contrib/wpa_supplicant/eap_sim_common.h b/contrib/wpa_supplicant/eap_sim_common.h index c89e04e..6715c36 100644 --- a/contrib/wpa_supplicant/eap_sim_common.h +++ b/contrib/wpa_supplicant/eap_sim_common.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / EAP-SIM/AKA shared routines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_SIM_COMMON_H #define EAP_SIM_COMMON_H @@ -16,11 +30,11 @@ #define AKA_AUTN_LEN 16 void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk); -void eap_sim_derive_keys_reauth(unsigned int _counter, +void eap_sim_derive_keys_reauth(u16 _counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, const u8 *mk, u8 *msk); -int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac, - u8 *extra, size_t extra_len); +int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, + const u8 *mac, const u8 *extra, size_t extra_len); void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len); @@ -65,19 +79,20 @@ enum eap_sim_id_req { struct eap_sim_attrs { - u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; - u8 *next_pseudonym, *next_reauth_id; - u8 *nonce_mt, *identity; + const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; + const u8 *next_pseudonym, *next_reauth_id; + const u8 *nonce_mt, *identity; size_t num_chal, version_list_len, encr_data_len; size_t next_pseudonym_len, next_reauth_id_len, identity_len; enum eap_sim_id_req id_req; int notification, counter, selected_version, client_error_code; }; -int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, - int aka, int encr); -int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len, - const u8 *iv, struct eap_sim_attrs *attr, int aka); +int eap_sim_parse_attr(const u8 *start, const u8 *end, + struct eap_sim_attrs *attr, int aka, int encr); +u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, + size_t encr_data_len, const u8 *iv, + struct eap_sim_attrs *attr, int aka); struct eap_sim_msg; diff --git a/contrib/wpa_supplicant/eap_testing.txt b/contrib/wpa_supplicant/eap_testing.txt index 03ef285..a4d1e0a 100644 --- a/contrib/wpa_supplicant/eap_testing.txt +++ b/contrib/wpa_supplicant/eap_testing.txt @@ -35,48 +35,50 @@ F) failed -) server did not support ?) not tested -hostapd --------------------------------------------------------. -Cisco Aironet 1200 AP (local RADIUS server) ----------------. | -Corriente Elektron -------------------------------------. | | -Lucent NavisRadiator -------------------------------. | | | -Interlink RAD-Series ---------------------------. | | | | -Radiator -----------------------------------. | | | | | -Meetinghouse Aegis ---------------------. | | | | | | -Funk Steel-Belted ------------------. | | | | | | | -Funk Odyssey -------------------. | | | | | | | | -Microsoft IAS --------------. | | | | | | | | | -FreeRADIUS -------------. | | | | | | | | | | - | | | | | | | | | | | +Cisco ACS ----------------------------------------------------------. +hostapd --------------------------------------------------------. | +Cisco Aironet 1200 AP (local RADIUS server) ----------------. | | +Corriente 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-PEAPv1/MSCHAPv2 - - + + + +1 + +5 +8 - + -EAP-PEAPv1/GTC - - + + + +1 + +5 - - + -EAP-PEAPv1/OTP - - - - - +1 - - - - - -EAP-PEAPv1/MD5 - - - + + +1 + +5 - - + -EAP-PEAPv1/TLS - - - + + +1 F +5 - - - -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-SIM +3 - - ? - + - ? - - + -EAP-AKA - - - - - + - - - - - -EAP-PSK +7 - - - - - - - - - - -EAP-FAST - - - - - - - - - + - -LEAP + - + + + + F +6 - + - +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-PEAPv1/MSCHAPv2 - - + + + +1 + +5 +8 - + + +EAP-PEAPv1/GTC - - + + + +1 + +5 - - + + +EAP-PEAPv1/OTP - - - - - +1 - - - - - - +EAP-PEAPv1/MD5 - - - + + +1 + +5 - - + - +EAP-PEAPv1/TLS - - - + + +1 F +5 - - - - +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-SIM +3 - - ? - + - ? - - + - +EAP-AKA - - - - - + - - - - - - +EAP-PSK +7 - - - - - - - - - + - +EAP-PAX - - - - - - - - - - + - +EAP-FAST - - - - - - - - - + - + +LEAP + - + + + + F +6 - + - + 1) PEAPv1 required new label, "client PEAP encryption" instead of "client EAP encryption", during key derivation (requires phase1="peaplabel=1" in the @@ -309,7 +311,13 @@ hostapd v0.3.3 - EAP-TTLS / EAP-GTC - EAP-TTLS / EAP-MSCHAPv2 - EAP-SIM +- EAP-PAX +Cisco Secure ACS 3.3(1) for Windows Server +- PEAPv1/GTC worked, but PEAPv0/GTC failed in the end after password was + sent successfully; ACS is replying with empty PEAP packet (TLS ACK); + wpa_supplicant tries to decrypt this.. Replying with TLS ACK and and + marking the connection completed was enough to make this work. PEAPv1: diff --git a/contrib/wpa_supplicant/eap_tls.c b/contrib/wpa_supplicant/eap_tls.c index 4b02cca..d1452df 100644 --- a/contrib/wpa_supplicant/eap_tls.c +++ b/contrib/wpa_supplicant/eap_tls.c @@ -38,8 +38,8 @@ static void * eap_tls_init(struct eap_sm *sm) struct eap_tls_data *data; struct wpa_ssid *config = eap_get_config(sm); if (config == NULL || - (sm->init_phase2 ? config->private_key2 : config->private_key) - == NULL) { + ((sm->init_phase2 ? config->private_key2 : config->private_key) + == NULL && config->engine == 0)) { wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured"); return NULL; } @@ -52,6 +52,18 @@ static void * eap_tls_init(struct eap_sm *sm) if (eap_tls_ssl_init(sm, &data->ssl, config)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_deinit(sm, data); + if (config->engine) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard " + "PIN"); + eap_sm_request_pin(sm, config); + sm->ignore = TRUE; + } else if (config->private_key && !config->private_key_passwd) + { + wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private " + "key passphrase"); + eap_sm_request_passphrase(sm, config); + sm->ignore = TRUE; + } return NULL; } @@ -72,61 +84,23 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv) static u8 * eap_tls_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { - struct eap_hdr *req; - int left, res; - unsigned int tls_msg_len; - u8 flags, *pos, *resp, id; + struct wpa_ssid *config = eap_get_config(sm); + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, *resp, id; + const u8 *pos; struct eap_tls_data *data = priv; - if (tls_get_errors(sm->ssl_ctx)) { - wpa_printf(MSG_INFO, "EAP-TLS: TLS errors detected"); - ret->ignore = TRUE; - return NULL; - } - - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_TLS) { - wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); - ret->ignore = TRUE; + pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret, + reqData, reqDataLen, &left, &flags); + if (pos == NULL) return NULL; - } + req = (const struct eap_hdr *) reqData; id = req->identifier; - pos++; - flags = *pos++; - left = be_to_host16(req->length) - sizeof(struct eap_hdr) - 2; - wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) reqDataLen, flags); - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS " - "length"); - ret->ignore = TRUE; - return NULL; - } - tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | - pos[3]; - wpa_printf(MSG_DEBUG, "EAP-TLS: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } - - ret->ignore = FALSE; - - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - ret->allowNotifications = TRUE; if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-TLS: Start"); @@ -142,6 +116,11 @@ static u8 * eap_tls_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed"); ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; + if (resp) { + /* This is likely an alert message, so send it instead + * of just ACKing the error. */ + return resp; + } return eap_tls_build_ack(&data->ssl, respDataLen, id, EAP_TYPE_TLS, 0); } @@ -166,6 +145,16 @@ static u8 * eap_tls_process(struct eap_sm *sm, void *priv, return eap_tls_build_ack(&data->ssl, respDataLen, id, EAP_TYPE_TLS, 0); } + + if (res == -1) { + /* The TLS handshake failed. So better forget the old PIN. + * It may be wrong, we can't be sure but trying the wrong one + * again might block it on the card - so better ask the user + * again */ + free(config->pin); + config->pin = NULL; + } + return resp; } diff --git a/contrib/wpa_supplicant/eap_tls_common.c b/contrib/wpa_supplicant/eap_tls_common.c index a56538c..cb3fb28 100644 --- a/contrib/wpa_supplicant/eap_tls_common.c +++ b/contrib/wpa_supplicant/eap_tls_common.c @@ -24,69 +24,108 @@ #include "md5.h" #include "sha1.h" #include "tls.h" +#include "config.h" + + +static int eap_tls_check_blob(struct eap_sm *sm, const char **name, + const u8 **data, size_t *data_len) +{ + const struct wpa_config_blob *blob; + + if (*name == NULL || strncmp(*name, "blob://", 7) != 0) + return 0; + + blob = eap_get_config_blob(sm, *name + 7); + if (blob == NULL) { + wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not " + "found", __func__, *name + 7); + return -1; + } + + *name = NULL; + *data = blob->data; + *data_len = blob->len; + + return 0; +} int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, struct wpa_ssid *config) { - int ret = -1; - char *ca_cert, *client_cert, *private_key, *private_key_passwd, - *dh_file, *subject_match; + int ret = -1, res; + struct tls_connection_params params; data->eap = sm; data->phase2 = sm->init_phase2; + memset(¶ms, 0, sizeof(params)); + params.engine = config->engine; if (config == NULL) { - ca_cert = NULL; - client_cert = NULL; - private_key = NULL; - private_key_passwd = NULL; - dh_file = NULL; - subject_match = NULL; } else if (data->phase2) { - ca_cert = (char *) config->ca_cert2; - client_cert = (char *) config->client_cert2; - private_key = (char *) config->private_key2; - private_key_passwd = (char *) config->private_key2_passwd; - dh_file = (char *) config->dh_file2; - subject_match = (char *) config->subject_match2; + params.ca_cert = (char *) config->ca_cert2; + params.ca_path = (char *) config->ca_path2; + params.client_cert = (char *) config->client_cert2; + params.private_key = (char *) config->private_key2; + params.private_key_passwd = + (char *) config->private_key2_passwd; + params.dh_file = (char *) config->dh_file2; + params.subject_match = (char *) config->subject_match2; + params.altsubject_match = (char *) config->altsubject_match2; } else { - ca_cert = (char *) config->ca_cert; - client_cert = (char *) config->client_cert; - private_key = (char *) config->private_key; - private_key_passwd = (char *) config->private_key_passwd; - dh_file = (char *) config->dh_file; - subject_match = (char *) config->subject_match; - } - data->conn = tls_connection_init(sm->ssl_ctx); - if (data->conn == NULL) { - wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " - "connection"); - goto done; + params.ca_cert = (char *) config->ca_cert; + params.ca_path = (char *) config->ca_path; + params.client_cert = (char *) config->client_cert; + params.private_key = (char *) config->private_key; + params.private_key_passwd = + (char *) config->private_key_passwd; + params.dh_file = (char *) config->dh_file; + params.subject_match = (char *) config->subject_match; + params.altsubject_match = (char *) config->altsubject_match; + params.engine_id = config->engine_id; + params.pin = config->pin; + params.key_id = config->key_id; } - if (tls_connection_ca_cert(sm->ssl_ctx, data->conn, ca_cert, - subject_match)) { - wpa_printf(MSG_INFO, "TLS: Failed to load root certificate " - "'%s'", ca_cert); + if (eap_tls_check_blob(sm, ¶ms.ca_cert, ¶ms.ca_cert_blob, + ¶ms.ca_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms.client_cert, + ¶ms.client_cert_blob, + ¶ms.client_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms.private_key, + ¶ms.private_key_blob, + ¶ms.private_key_blob_len) || + eap_tls_check_blob(sm, ¶ms.dh_file, ¶ms.dh_blob, + ¶ms.dh_blob_len)) { + wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs"); goto done; } - if (tls_connection_client_cert(sm->ssl_ctx, data->conn, client_cert)) { - wpa_printf(MSG_INFO, "TLS: Failed to load client certificate " - "'%s'", client_cert); + data->conn = tls_connection_init(sm->ssl_ctx); + if (data->conn == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " + "connection"); goto done; } - if (tls_connection_private_key(sm->ssl_ctx, data->conn, private_key, - private_key_passwd)) { - wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", - private_key); + res = tls_connection_set_params(sm->ssl_ctx, data->conn, ¶ms); + if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + /* At this point with the pkcs11 engine the PIN might be wrong. + * We reset the PIN in the configuration to be sure to not use + * it again and the calling function must request a new one */ + free(config->pin); + config->pin = NULL; + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { + wpa_printf(MSG_INFO,"TLS: Failed to load private key"); + /* We don't know exactly but maybe the PIN was wrong, + * so ask for a new one. */ + free(config->pin); + config->pin = NULL; + eap_sm_request_pin(sm, config); + sm->ignore = TRUE; goto done; - } - - if (dh_file && tls_connection_dh(sm->ssl_ctx, data->conn, dh_file)) { - wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", - dh_file); + } else if (res) { + wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " + "parameters"); goto done; } @@ -126,93 +165,131 @@ u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len) { struct tls_keys keys; - u8 *random; + u8 *rnd; u8 *out; if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) return NULL; + + if (keys.eap_tls_prf && strcmp(label, "client EAP encryption") == 0) { + if (len > keys.eap_tls_prf_len) + return NULL; + out = malloc(len); + if (out == NULL) + return NULL; + memcpy(out, keys.eap_tls_prf, len); + return out; + } + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + return NULL; + out = malloc(len); - random = malloc(keys.client_random_len + keys.server_random_len); - if (out == NULL || random == NULL) { + rnd = malloc(keys.client_random_len + keys.server_random_len); + if (out == NULL || rnd == NULL) { free(out); - free(random); + free(rnd); return NULL; } - memcpy(random, keys.client_random, keys.client_random_len); - memcpy(random + keys.client_random_len, keys.server_random, + memcpy(rnd, keys.client_random, keys.client_random_len); + memcpy(rnd + keys.client_random_len, keys.server_random, keys.server_random_len); if (tls_prf(keys.master_key, keys.master_key_len, - label, random, keys.client_random_len + + label, rnd, keys.client_random_len + keys.server_random_len, out, len)) { - free(random); + free(rnd); free(out); return NULL; } - free(random); + free(rnd); return out; } -int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, - u8 **in_data, size_t *in_len) +/** + * eap_tls_data_reassemble - Reassemble TLS data + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @data: Data for TLS processing + * @in_data: Next incoming TLS segment + * @in_len: Length of in_data + * @out_len: Variable for returning output data length + * @need_more_input: Variable for returning whether more input data is needed + * to reassemble this TLS packet + * Returns: Pointer to output data or %NULL on error + * + * This function reassembles TLS fragments. + */ +const u8 * eap_tls_data_reassemble( + struct eap_sm *sm, struct eap_ssl_data *data, const u8 *in_data, + size_t in_len, size_t *out_len, int *need_more_input) { u8 *buf; - if (data->tls_in_left > *in_len || data->tls_in) { - if (data->tls_in_len + *in_len == 0) { + *need_more_input = 0; + + if (data->tls_in_left > in_len || data->tls_in) { + if (data->tls_in_len + in_len == 0) { free(data->tls_in); data->tls_in = NULL; data->tls_in_len = 0; wpa_printf(MSG_WARNING, "SSL: Invalid reassembly " - "state: tls_in_left=%d tls_in_len=%d " - "*in_len=%d", - data->tls_in_left, data->tls_in_len, - *in_len); - return -1; + "state: tls_in_left=%lu tls_in_len=%lu " + "in_len=%lu", + (unsigned long) data->tls_in_left, + (unsigned long) data->tls_in_len, + (unsigned long) in_len); + return NULL; } - buf = realloc(data->tls_in, data->tls_in_len + *in_len); + buf = realloc(data->tls_in, data->tls_in_len + in_len); if (buf == NULL) { free(data->tls_in); data->tls_in = NULL; data->tls_in_len = 0; wpa_printf(MSG_INFO, "SSL: Could not allocate memory " "for TLS data"); - return -1; + return NULL; } - memcpy(buf + data->tls_in_len, *in_data, *in_len); + memcpy(buf + data->tls_in_len, in_data, in_len); data->tls_in = buf; - data->tls_in_len += *in_len; - if (*in_len > data->tls_in_left) { + data->tls_in_len += in_len; + if (in_len > data->tls_in_left) { wpa_printf(MSG_INFO, "SSL: more data than TLS message " "length indicated"); data->tls_in_left = 0; - return -1; + return NULL; } - data->tls_in_left -= *in_len; + data->tls_in_left -= in_len; if (data->tls_in_left > 0) { wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " "data", (unsigned long) data->tls_in_left); - return 1; + *need_more_input = 1; + return NULL; } - - *in_data = data->tls_in; - *in_len = data->tls_in_len; - } else + } else { data->tls_in_left = 0; + data->tls_in = malloc(in_len); + if (data->tls_in == NULL) + return NULL; + memcpy(data->tls_in, in_data, in_len); + data->tls_in_len = in_len; + } - return 0; + *out_len = data->tls_in_len; + return data->tls_in; } int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, int eap_type, int peap_version, - u8 id, u8 *in_data, size_t in_len, + u8 id, const u8 *in_data, size_t in_len, u8 **out_data, size_t *out_len) { size_t len; u8 *pos, *flags; struct eap_hdr *resp; + int ret = 0; WPA_ASSERT(data->tls_out_len == 0 || in_len == 0); *out_len = 0; @@ -220,9 +297,15 @@ int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, if (data->tls_out_len == 0) { /* No more data to send out - expect to receive more data from * the AS. */ - int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len); - if (res < 0 || res == 1) - return res; + const u8 *msg; + size_t msg_len; + int need_more_input; + + msg = eap_tls_data_reassemble(sm, data, in_data, in_len, + &msg_len, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; + /* Full TLS message reassembled - continue handshake processing */ if (data->tls_out) { @@ -235,7 +318,7 @@ int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, } data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn, - in_data, in_len, + msg, msg_len, &data->tls_out_len); /* Clear reassembled input data (if the buffer was needed). */ @@ -248,6 +331,13 @@ int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, data->tls_out_len = 0; return -1; } + if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " + "report error"); + ret = -1; + /* TODO: clean pin if engine used? */ + } + if (data->tls_out_len == 0) { /* TLS negotiation should now be complete since all other cases * needing more that should have been catched above based on @@ -303,7 +393,7 @@ int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, data->tls_out = NULL; } - return 0; + return ret; } @@ -330,6 +420,13 @@ u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id, int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) { + free(data->tls_in); + data->tls_in = NULL; + data->tls_in_len = data->tls_in_left = data->tls_in_total = 0; + free(data->tls_out); + data->tls_out = NULL; + data->tls_out_len = data->tls_out_pos = 0; + return tls_connection_shutdown(sm->ssl_ctx, data->conn); } @@ -347,3 +444,61 @@ int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, return len; } + + +const u8 * eap_tls_process_init(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, struct eap_method_ret *ret, + const u8 *reqData, size_t reqDataLen, + size_t *len, u8 *flags) +{ + const struct eap_hdr *req; + const u8 *pos; + size_t left; + unsigned int tls_msg_len; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "SSL: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(eap_type, reqData, reqDataLen, &left); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + req = (const struct eap_hdr *) reqData; + *flags = *pos++; + left--; + wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) reqDataLen, *flags); + if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "SSL: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", + tls_msg_len); + if (data->tls_in_left == 0) { + data->tls_in_total = tls_msg_len; + data->tls_in_left = tls_msg_len; + free(data->tls_in); + data->tls_in = NULL; + data->tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + *len = (size_t) left; + return pos; +} diff --git a/contrib/wpa_supplicant/eap_tls_common.h b/contrib/wpa_supplicant/eap_tls_common.h index 499cae4..d1dfff5 100644 --- a/contrib/wpa_supplicant/eap_tls_common.h +++ b/contrib/wpa_supplicant/eap_tls_common.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_TLS_COMMON_H #define EAP_TLS_COMMON_H @@ -36,16 +50,21 @@ int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len); -int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, - u8 **in_data, size_t *in_len); +const u8 * eap_tls_data_reassemble( + struct eap_sm *sm, struct eap_ssl_data *data, const u8 *in_data, + size_t in_len, size_t *out_len, int *need_more_input); int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, int eap_type, int peap_version, - u8 id, u8 *in_data, size_t in_len, + u8 id, const u8 *in_data, size_t in_len, u8 **out_data, size_t *out_len); u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id, int eap_type, int peap_version); int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data); int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, size_t buflen, int verbose); +const u8 * eap_tls_process_init(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, struct eap_method_ret *ret, + const u8 *reqData, size_t reqDataLen, + size_t *len, u8 *flags); #endif /* EAP_TLS_COMMON_H */ diff --git a/contrib/wpa_supplicant/eap_tlv.c b/contrib/wpa_supplicant/eap_tlv.c index 5571d8b..4070c6f 100644 --- a/contrib/wpa_supplicant/eap_tlv.c +++ b/contrib/wpa_supplicant/eap_tlv.c @@ -22,7 +22,7 @@ #include "eap_tlv.h" -u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len) +u8 * eap_tlv_build_nak(int id, u16 nak_type, size_t *resp_len) { struct eap_hdr *hdr; u8 *pos; @@ -48,14 +48,13 @@ u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len) *pos++ = 0; *pos++ = 0; /* NAK-Type */ - *pos++ = nak_type >> 8; - *pos++ = nak_type & 0xff; + WPA_PUT_BE16(pos, nak_type); return (u8 *) hdr; } -u8 * eap_tlv_build_result(int id, int status, size_t *resp_len) +u8 * eap_tlv_build_result(int id, u16 status, size_t *resp_len) { struct eap_hdr *hdr; u8 *pos; @@ -76,33 +75,32 @@ u8 * eap_tlv_build_result(int id, int status, size_t *resp_len) *pos++ = 0; *pos++ = 2; /* Status */ - *pos++ = status >> 8; - *pos++ = status & 0xff; + WPA_PUT_BE16(pos, status); return (u8 *) hdr; } int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, - struct eap_hdr *hdr, u8 **resp, size_t *resp_len) + const struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { size_t left; - u8 *pos; - u8 *result_tlv = NULL; + const u8 *pos; + const u8 *result_tlv = NULL; size_t result_tlv_len = 0; int tlv_type, mandatory, tlv_len; /* Parse TLVs */ left = be_to_host16(hdr->length) - sizeof(struct eap_hdr) - 1; - pos = (u8 *) (hdr + 1); + pos = (const u8 *) (hdr + 1); pos++; wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); while (left >= 4) { mandatory = !!(pos[0] & 0x80); - tlv_type = pos[0] & 0x3f; - tlv_type = (tlv_type << 8) | pos[1]; - tlv_len = ((int) pos[2] << 8) | pos[3]; - pos += 4; + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + tlv_len = WPA_GET_BE16(pos); + pos += 2; left -= 4; if (tlv_len > left) { wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " @@ -150,7 +148,7 @@ int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, (unsigned long) result_tlv_len); return -1; } - status = ((int) result_tlv[0] << 8) | result_tlv[1]; + status = WPA_GET_BE16(result_tlv); if (status == EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " "- EAP-TLV/Phase2 Completed"); diff --git a/contrib/wpa_supplicant/eap_tlv.h b/contrib/wpa_supplicant/eap_tlv.h index 4391552..6a5afe0 100644 --- a/contrib/wpa_supplicant/eap_tlv.h +++ b/contrib/wpa_supplicant/eap_tlv.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_TLV_H #define EAP_TLV_H @@ -65,9 +79,9 @@ struct eap_tlv_pac_ack_tlv { #define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 -u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len); -u8 * eap_tlv_build_result(int id, int status, size_t *resp_len); +u8 * eap_tlv_build_nak(int id, u16 nak_type, size_t *resp_len); +u8 * eap_tlv_build_result(int id, u16 status, size_t *resp_len); int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, - struct eap_hdr *hdr, u8 **resp, size_t *resp_len); + const struct eap_hdr *hdr, u8 **resp, size_t *resp_len); #endif /* EAP_TLV_H */ diff --git a/contrib/wpa_supplicant/eap_ttls.c b/contrib/wpa_supplicant/eap_ttls.c index 733f136..0b1ff8f 100644 --- a/contrib/wpa_supplicant/eap_ttls.c +++ b/contrib/wpa_supplicant/eap_ttls.c @@ -22,7 +22,7 @@ #include "wpa_supplicant.h" #include "config_ssid.h" #include "ms_funcs.h" -#include "md5.h" +#include "crypto.h" #include "tls.h" #include "eap_ttls.h" @@ -183,7 +183,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) static int eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data, - int id, u8 *plain, size_t plain_len, + int id, const u8 *plain, size_t plain_len, u8 **out_data, size_t *out_len) { int res; @@ -312,7 +312,7 @@ static int eap_ttls_phase2_nak(struct eap_sm *sm, static int eap_ttls_phase2_request_eap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, + const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { @@ -400,7 +400,7 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm, static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, + const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { @@ -506,7 +506,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, + const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { @@ -576,7 +576,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, static int eap_ttls_phase2_request_pap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, + const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { @@ -624,13 +624,14 @@ static int eap_ttls_phase2_request_pap(struct eap_sm *sm, static int eap_ttls_phase2_request_chap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, + const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { struct wpa_ssid *config = eap_get_config(sm); u8 *buf, *pos, *challenge; - MD5_CTX context; + const u8 *addr[3]; + size_t len[3]; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); @@ -665,11 +666,13 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, *pos++ = data->ident; /* MD5(Ident + Password + Challenge) */ - MD5Init(&context); - MD5Update(&context, &data->ident, 1); - MD5Update(&context, config->password, config->password_len); - MD5Update(&context, challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); - MD5Final(pos, &context); + addr[0] = &data->ident; + len[0] = 1; + addr[1] = config->password; + len[1] = config->password_len; + addr[2] = challenge; + len[2] = EAP_TTLS_CHAP_CHALLENGE_LEN; + md5_vector(3, addr, len, pos); wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", config->identity, config->identity_len); @@ -698,7 +701,7 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, static int eap_ttls_phase2_request(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, - struct eap_hdr *req, + const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { @@ -763,12 +766,13 @@ static int eap_ttls_phase2_request(struct eap_sm *sm, static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, - struct eap_method_ret *ret, struct eap_hdr *req, - u8 *in_data, size_t in_len, + struct eap_method_ret *ret, + const struct eap_hdr *req, + const u8 *in_data, size_t in_len, u8 **out_data, size_t *out_len) { u8 *in_decrypted = NULL, *pos; - int buf_len, len_decrypted = 0, len, left, retval = 0, res; + int buf_len, len_decrypted = 0, len, left, retval = 0; struct eap_hdr *hdr = NULL; u8 *resp = NULL, *mschapv2 = NULL, *eapdata = NULL; size_t resp_len, eap_len = 0; @@ -776,6 +780,9 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, u8 recv_response[20]; int mschapv2_error = 0; struct wpa_ssid *config = eap_get_config(sm); + const u8 *msg; + size_t msg_len; + int need_more_input; wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" " Phase 2", (unsigned long) in_len); @@ -839,11 +846,10 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, goto process_eap; } - res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); - if (res < 0 || res == 1) { - retval = res; - goto done; - } + msg = eap_tls_data_reassemble(sm, &data->ssl, in_data, in_len, + &msg_len, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; buf_len = in_len; if (data->ssl.tls_in_total > buf_len) @@ -860,7 +866,7 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, } len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, + msg, msg_len, in_decrypted, buf_len); free(data->ssl.tls_in); data->ssl.tls_in = NULL; @@ -1117,7 +1123,8 @@ continue_req: free(resp); } else if (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp) { + config->pending_req_otp || + config->pending_req_new_password) { free(data->pending_phase2_req); data->pending_phase2_req = malloc(len_decrypted); if (data->pending_phase2_req) { @@ -1142,62 +1149,22 @@ done: static u8 * eap_ttls_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, - u8 *reqData, size_t reqDataLen, + const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { - struct eap_hdr *req; - int left, res; - unsigned int tls_msg_len; - u8 flags, *pos, *resp, id; + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, *resp, id; + const u8 *pos; struct eap_ttls_data *data = priv; - if (tls_get_errors(sm->ssl_ctx)) { - wpa_printf(MSG_INFO, "EAP-TTLS: TLS errors detected"); - ret->ignore = TRUE; - return NULL; - } - - req = (struct eap_hdr *) reqData; - pos = (u8 *) (req + 1); - if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_TTLS || - (left = be_to_host16(req->length)) > reqDataLen) { - wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); - ret->ignore = TRUE; + pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, + reqData, reqDataLen, &left, &flags); + if (pos == NULL) return NULL; - } - left -= sizeof(struct eap_hdr); + req = (const struct eap_hdr *) reqData; id = req->identifier; - pos++; - flags = *pos++; - left -= 2; - wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) reqDataLen, flags); - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS " - "length"); - ret->ignore = TRUE; - return NULL; - } - tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | - pos[3]; - wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } - - ret->ignore = FALSE; - ret->methodState = METHOD_CONT; - ret->decision = DECISION_FAIL; - ret->allowNotifications = TRUE; if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Start"); diff --git a/contrib/wpa_supplicant/eap_ttls.h b/contrib/wpa_supplicant/eap_ttls.h index a187db4..f35f5a9 100644 --- a/contrib/wpa_supplicant/eap_ttls.h +++ b/contrib/wpa_supplicant/eap_ttls.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAP_TTLS_H #define EAP_TTLS_H diff --git a/contrib/wpa_supplicant/eapol_sm.c b/contrib/wpa_supplicant/eapol_sm.c index 1424b7d..95a6031 100644 --- a/contrib/wpa_supplicant/eapol_sm.c +++ b/contrib/wpa_supplicant/eapol_sm.c @@ -20,15 +20,17 @@ #include "eapol_sm.h" #include "eap.h" #include "eloop.h" -#include "wpa_supplicant.h" #include "l2_packet.h" #include "wpa.h" #include "md5.h" #include "rc4.h" -/* IEEE 802.1aa/D6.1 - Supplicant */ +/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */ +/** + * struct eapol_sm - Internal data for EAPOL state machines + */ struct eapol_sm { /* Timers */ unsigned int authWhile; @@ -118,7 +120,7 @@ struct eapol_sm { unsigned int dot1xSuppLastEapolFrameVersion; unsigned char dot1xSuppLastEapolFrameSource[6]; - /* Miscellaneous variables (not defined in IEEE 802.1aa/D6.1) */ + /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */ Boolean changed; struct eap_sm *eap; struct wpa_ssid *config; @@ -141,6 +143,38 @@ struct eapol_sm { }; +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +struct ieee802_1x_eapol_key { + u8 type; + /* Note: key_length is unaligned */ + u8 key_length[2]; + /* does not repeat within the life of the keying material used to + * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ + u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; + u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as + * the key */ + u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} __attribute__ ((packed)); + + static void eapol_sm_txLogoff(struct eapol_sm *sm); static void eapol_sm_txStart(struct eapol_sm *sm); static void eapol_sm_processKey(struct eapol_sm *sm); @@ -150,8 +184,6 @@ static void eapol_sm_abortSupp(struct eapol_sm *sm); static void eapol_sm_abort_cached(struct eapol_sm *sm); static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); -static struct eapol_callbacks eapol_cb; - /* Definitions for clarifying state machine implementation */ #define SM_STATE(machine, state) \ @@ -182,17 +214,26 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) { struct eapol_sm *sm = timeout_ctx; - if (sm->authWhile > 0) + if (sm->authWhile > 0) { sm->authWhile--; - if (sm->heldWhile > 0) + if (sm->authWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); + } + if (sm->heldWhile > 0) { sm->heldWhile--; - if (sm->startWhen > 0) + if (sm->heldWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); + } + if (sm->startWhen > 0) { sm->startWhen--; - if (sm->idleWhile > 0) + if (sm->startWhen == 0) + wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); + } + if (sm->idleWhile > 0) { sm->idleWhile--; - wpa_printf(MSG_MSGDUMP, "EAPOL: Port Timers tick - authWhile=%d " - "heldWhile=%d startWhen=%d idleWhile=%d", - sm->authWhile, sm->heldWhile, sm->startWhen, sm->idleWhile); + if (sm->idleWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); + } eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm); eapol_sm_step(sm); @@ -224,11 +265,24 @@ SM_STATE(SUPP_PAE, DISCONNECTED) SM_STATE(SUPP_PAE, CONNECTING) { + int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; SM_ENTRY(SUPP_PAE, CONNECTING); - sm->startWhen = sm->startPeriod; - sm->startCount++; + if (send_start) { + sm->startWhen = sm->startPeriod; + sm->startCount++; + } else { + /* + * Do not send EAPOL-Start immediately since in most cases, + * Authenticator is going to start authentication immediately + * after association and an extra EAPOL-Start is just going to + * delay authentication. Use a short timeout to send the first + * EAPOL-Start if Authenticator does not start authentication. + */ + sm->startWhen = 3; + } sm->eapolEap = FALSE; - eapol_sm_txStart(sm); + if (send_start) + eapol_sm_txStart(sm); } @@ -532,8 +586,8 @@ SM_STEP(SUPP_BE) static void eapol_sm_txLogoff(struct eapol_sm *sm) { wpa_printf(MSG_DEBUG, "EAPOL: txLogoff"); - sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_LOGOFF, - (u8 *) "", 0); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0); sm->dot1xSuppEapolLogoffFramesTx++; sm->dot1xSuppEapolFramesTx++; } @@ -542,8 +596,8 @@ static void eapol_sm_txLogoff(struct eapol_sm *sm) static void eapol_sm_txStart(struct eapol_sm *sm) { wpa_printf(MSG_DEBUG, "EAPOL: txStart"); - sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_START, - (u8 *) "", 0); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0); sm->dot1xSuppEapolStartFramesTx++; sm->dot1xSuppEapolFramesTx++; } @@ -566,6 +620,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; int key_len, res, sign_key_len, encr_key_len; + u16 rx_key_length; wpa_printf(MSG_DEBUG, "EAPOL: processKey"); if (sm->last_rx_key == NULL) @@ -584,11 +639,13 @@ static void eapol_sm_processKey(struct eapol_sm *sm) wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); return; } + rx_key_length = WPA_GET_BE16(key->key_length); wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d " "EAPOL-Key: type=%d key_length=%d key_index=0x%x", hdr->version, hdr->type, be_to_host16(hdr->length), - key->type, be_to_host16(key->key_length), key->key_index); + key->type, rx_key_length, key->key_index); + eapol_sm_notify_lower_layer_success(sm); sign_key_len = IEEE8021X_SIGN_KEY_LEN; encr_key_len = IEEE8021X_ENCR_KEY_LEN; res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata)); @@ -645,12 +702,12 @@ static void eapol_sm_processKey(struct eapol_sm *sm) wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); key_len = be_to_host16(hdr->length) - sizeof(*key); - if (key_len > 32 || be_to_host16(key->key_length) > 32) { + if (key_len > 32 || rx_key_length > 32) { wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", - key_len ? key_len : be_to_host16(key->key_length)); + key_len ? key_len : rx_key_length); return; } - if (key_len == be_to_host16(key->key_length)) { + if (key_len == rx_key_length) { memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, encr_key_len); @@ -660,22 +717,23 @@ static void eapol_sm_processKey(struct eapol_sm *sm) wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", datakey, key_len); } else if (key_len == 0) { - /* IEEE 802.1X-REV specifies that least significant Key Length + /* + * IEEE 802.1X-2004 specifies that least significant Key Length * octets from MS-MPPE-Send-Key are used as the key if the key * data is not present. This seems to be meaning the beginning * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator. * Anyway, taking the beginning of the keying material from EAP - * seems to interoperate with Authenticators. */ - key_len = be_to_host16(key->key_length); + * seems to interoperate with Authenticators. + */ + key_len = rx_key_length; memcpy(datakey, keydata.encr_key, key_len); wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying " "material data encryption key", datakey, key_len); } else { wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d " - "(key_length=%d)", key_len, - be_to_host16(key->key_length)); + "(key_length=%d)", key_len, rx_key_length); return; } @@ -741,8 +799,8 @@ static void eapol_sm_txSuppRsp(struct eapol_sm *sm) } /* Send EAP-Packet from the EAP layer to the Authenticator */ - sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAP_PACKET, - resp, resp_len); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAP_PACKET, resp, resp_len); /* eapRespData is not used anymore, so free it here */ free(resp); @@ -768,63 +826,20 @@ static void eapol_sm_abortSupp(struct eapol_sm *sm) } -struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) -{ - struct eapol_sm *sm; - sm = malloc(sizeof(*sm)); - if (sm == NULL) - return NULL; - memset(sm, 0, sizeof(*sm)); - sm->ctx = ctx; - - sm->portControl = Auto; - - /* Supplicant PAE state machine */ - sm->heldPeriod = 60; - sm->startPeriod = 30; - sm->maxStart = 3; - - /* Supplicant Backend state machine */ - sm->authPeriod = 30; - - sm->eap = eap_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx); - if (sm->eap == NULL) { - free(sm); - return NULL; - } - - /* Initialize EAPOL state machines */ - sm->initialize = TRUE; - eapol_sm_step(sm); - sm->initialize = FALSE; - eapol_sm_step(sm); - - eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); - - return sm; -} - - -void eapol_sm_deinit(struct eapol_sm *sm) -{ - if (sm == NULL) - return; - eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); - eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); - eap_sm_deinit(sm->eap); - free(sm->last_rx_key); - free(sm->eapReqData); - free(sm->ctx); - free(sm); -} - - static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) { eapol_sm_step(timeout_ctx); } +/** + * eapol_sm_step - EAPOL state machine step function + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * This function is called to notify the state machine about changed external + * variables. It will step through the EAPOL state machines in loop to process + * all triggered state changes. + */ void eapol_sm_step(struct eapol_sm *sm) { int i; @@ -929,6 +944,17 @@ static const char * eapol_port_control(PortControl ctrl) } +/** + * eapol_sm_configure - Set EAPOL variables + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @heldPeriod: dot1xSuppHeldPeriod + * @authPeriod: dot1xSuppAuthPeriod + * @startPeriod: dot1xSuppStartPeriod + * @maxStart: dot1xSuppMaxStart + * + * Set configurable EAPOL state machine variables. Each variable can be set to + * the given value or ignored if set to -1 (to set only some of the variables). + */ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, int startPeriod, int maxStart) { @@ -945,6 +971,19 @@ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, } +/** + * eapol_sm_get_status - Get EAPOL state machine status + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query EAPOL state machine for status information. This function fills in a + * text area with current status information from the EAPOL state machine. If + * the buffer (buf) is not large enough, status information will be truncated + * to fit the buffer. + */ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, int verbose) { @@ -960,10 +999,10 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, if (verbose) { len += snprintf(buf + len, buflen - len, - "heldPeriod=%d\n" - "authPeriod=%d\n" - "startPeriod=%d\n" - "maxStart=%d\n" + "heldPeriod=%u\n" + "authPeriod=%u\n" + "startPeriod=%u\n" + "maxStart=%u\n" "portControl=%s\n" "Supplicant Backend state=%s\n", sm->heldPeriod, @@ -980,6 +1019,18 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, } +/** + * eapol_sm_get_mib - Get EAPOL state machine MIBs + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @buf: Buffer for MIB information + * @buflen: Maximum buffer length + * Returns: Number of bytes written to buf. + * + * Query EAPOL state machine for MIB information. This function fills in a + * text area with current MIB information from the EAPOL state machine. If + * the buffer (buf) is not large enough, MIB information will be truncated to + * fit the buffer. + */ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) { int len; @@ -987,22 +1038,22 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) return 0; len = snprintf(buf, buflen, "dot1xSuppPaeState=%d\n" - "dot1xSuppHeldPeriod=%d\n" - "dot1xSuppAuthPeriod=%d\n" - "dot1xSuppStartPeriod=%d\n" - "dot1xSuppMaxStart=%d\n" + "dot1xSuppHeldPeriod=%u\n" + "dot1xSuppAuthPeriod=%u\n" + "dot1xSuppStartPeriod=%u\n" + "dot1xSuppMaxStart=%u\n" "dot1xSuppSuppControlledPortStatus=%s\n" "dot1xSuppBackendPaeState=%d\n" - "dot1xSuppEapolFramesRx=%d\n" - "dot1xSuppEapolFramesTx=%d\n" - "dot1xSuppEapolStartFramesTx=%d\n" - "dot1xSuppEapolLogoffFramesTx=%d\n" - "dot1xSuppEapolRespFramesTx=%d\n" - "dot1xSuppEapolReqIdFramesRx=%d\n" - "dot1xSuppEapolReqFramesRx=%d\n" - "dot1xSuppInvalidEapolFramesRx=%d\n" - "dot1xSuppEapLengthErrorFramesRx=%d\n" - "dot1xSuppLastEapolFrameVersion=%d\n" + "dot1xSuppEapolFramesRx=%u\n" + "dot1xSuppEapolFramesTx=%u\n" + "dot1xSuppEapolStartFramesTx=%u\n" + "dot1xSuppEapolLogoffFramesTx=%u\n" + "dot1xSuppEapolRespFramesTx=%u\n" + "dot1xSuppEapolReqIdFramesRx=%u\n" + "dot1xSuppEapolReqFramesRx=%u\n" + "dot1xSuppInvalidEapolFramesRx=%u\n" + "dot1xSuppEapLengthErrorFramesRx=%u\n" + "dot1xSuppLastEapolFrameVersion=%u\n" "dot1xSuppLastEapolFrameSource=" MACSTR "\n", sm->SUPP_PAE_state, sm->heldPeriod, @@ -1027,20 +1078,31 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) } -void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len) +/** + * eapol_sm_rx_eapol - Process received EAPOL frames + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @src: Source MAC address of the EAPOL packet + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) + * @len: Length of the EAPOL frame + * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine, + * -1 failure + */ +int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, + size_t len) { - struct ieee802_1x_hdr *hdr; - struct ieee802_1x_eapol_key *key; + const struct ieee802_1x_hdr *hdr; + const struct ieee802_1x_eapol_key *key; int plen, data_len; + int res = 1; if (sm == NULL) - return; + return 0; sm->dot1xSuppEapolFramesRx++; if (len < sizeof(*hdr)) { sm->dot1xSuppInvalidEapolFramesRx++; - return; + return 0; } - hdr = (struct ieee802_1x_hdr *) buf; + hdr = (const struct ieee802_1x_hdr *) buf; sm->dot1xSuppLastEapolFrameVersion = hdr->version; memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); if (hdr->version < EAPOL_VERSION) { @@ -1049,7 +1111,7 @@ void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len) plen = be_to_host16(hdr->length); if (plen > len - sizeof(*hdr)) { sm->dot1xSuppEapLengthErrorFramesRx++; - return; + return 0; } data_len = plen + sizeof(*hdr); @@ -1080,12 +1142,13 @@ void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len) "frame received"); break; } - key = (struct ieee802_1x_eapol_key *) (hdr + 1); + key = (const struct ieee802_1x_eapol_key *) (hdr + 1); if (key->type == EAPOL_KEY_TYPE_WPA || key->type == EAPOL_KEY_TYPE_RSN) { /* WPA Supplicant takes care of this frame. */ wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " "frame in EAPOL state machines"); + res = 0; break; } if (key->type != EAPOL_KEY_TYPE_RC4) { @@ -1110,9 +1173,18 @@ void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len) sm->dot1xSuppInvalidEapolFramesRx++; break; } + + return res; } +/** + * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL station machine about transmitted EAPOL packet from an external + * component, e.g., WPA. This will update the statistics. + */ void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) { if (sm) @@ -1120,6 +1192,13 @@ void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) } +/** + * eapol_sm_notify_portEnabled - Notification about portEnabled change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @enabled: New portEnabled value + * + * Notify EAPOL station machine about new portEnabled value. + */ void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) { if (sm == NULL) @@ -1131,6 +1210,13 @@ void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) } +/** + * eapol_sm_notify_portValid - Notification about portValid change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @valid: New portValid value + * + * Notify EAPOL station machine about new portValid value. + */ void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) { if (sm == NULL) @@ -1142,6 +1228,17 @@ void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) } +/** + * eapol_sm_notify_eap_success - Notification of external EAP success trigger + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @success: %TRUE = set success, %FALSE = clear success + * + * Notify EAPOL station machine that external event has forced EAP state to + * success (success = %TRUE). This can be cleared by setting success = %FALSE. + * + * This function is called to update EAP state when WPA-PSK key handshake has + * been completed successfully since WPA-PSK does not use EAP state machine. + */ void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) { if (sm == NULL) @@ -1156,6 +1253,14 @@ void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) } +/** + * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @fail: %TRUE = set failure, %FALSE = clear failure + * + * Notify EAPOL station machine that external event has forced EAP state to + * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE. + */ void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) { if (sm == NULL) @@ -1168,8 +1273,20 @@ void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) } +/** + * eapol_sm_notify_config - Notification of EAPOL configuration change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @config: Pointer to current network configuration + * @conf: Pointer to EAPOL configuration data + * + * Notify EAPOL station machine that configuration has changed. config will be + * stored as a backpointer to network configuration. This can be %NULL to clear + * the stored pointed. conf will be copied to local EAPOL/EAP configuration + * data. If conf is %NULL, this part of the configuration change will be + * skipped. + */ void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config, - struct eapol_config *conf) + const struct eapol_config *conf) { if (sm == NULL) return; @@ -1185,13 +1302,25 @@ void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config, if (sm->eap) { eap_set_fast_reauth(sm->eap, conf->fast_reauth); eap_set_workaround(sm->eap, conf->workaround); + eap_set_force_disabled(sm->eap, conf->eap_disabled); } } +/** + * eapol_sm_get_key - Get master session key (MSK) from EAP + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @key: Pointer for key buffer + * @len: Number of bytes to copy to key + * Returns: 0 on success (len of key available), maximum available key len + * (>0) if key is available but it is shorter than len, or -1 on failure. + * + * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key + * is available only after a successful authentication. + */ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) { - u8 *eap_key; + const u8 *eap_key; size_t eap_len; if (sm == NULL || !eap_key_available(sm->eap)) @@ -1206,6 +1335,13 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) } +/** + * eapol_sm_notify_logoff - Notification of logon/logoff commands + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @logoff: Whether command was logoff + * + * Notify EAPOL state machines that user requested logon/logoff. + */ void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) { if (sm) { @@ -1215,6 +1351,13 @@ void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) } +/** + * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that PMKSA caching was successful. This is used + * to move EAPOL and EAP state machines into authenticated/successful state. + */ void eapol_sm_notify_cached(struct eapol_sm *sm) { if (sm == NULL) @@ -1225,6 +1368,13 @@ void eapol_sm_notify_cached(struct eapol_sm *sm) } +/** + * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @attempt: Whether PMKSA caching is tried + * + * Notify EAPOL state machines whether PMKSA caching is used. + */ void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) { if (sm == NULL) @@ -1252,6 +1402,14 @@ static void eapol_sm_abort_cached(struct eapol_sm *sm) } +/** + * eapol_sm_register_scard_ctx - Notification of smart card context + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @ctx: Context data for smart card operations + * + * Notify EAPOL state machines of context data for smart card operations. This + * context data will be used as a parameter for scard_*() functions. + */ void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx) { if (sm) { @@ -1261,6 +1419,13 @@ void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx) } +/** + * eapol_sm_notify_portControl - Notification of portControl changes + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @portControl: New value for portControl variable + * + * Notify EAPOL state machines that portControl variable has changed. + */ void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) { if (sm == NULL) @@ -1272,6 +1437,13 @@ void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) } +/** + * eapol_sm_notify_ctrl_attached - Notification of attached monitor + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that a monitor was attached to the control + * interface to trigger re-sending of pending requests for user input. + */ void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) { if (sm == NULL) @@ -1280,6 +1452,13 @@ void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) } +/** + * eapol_sm_notify_ctrl_response - Notification of received user input + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that a control response, i.e., user + * input, was received in order to trigger retrying of a pending EAP request. + */ void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) { if (sm == NULL) @@ -1295,6 +1474,37 @@ void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) } +/** + * eapol_sm_request_reauth - Request reauthentication + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * This function can be used to request EAPOL reauthentication, e.g., when the + * current PMKSA entry is nearing expiration. + */ +void eapol_sm_request_reauth(struct eapol_sm *sm) +{ + if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED) + return; + eapol_sm_txStart(sm); +} + + +/** + * eapol_sm_notify_lower_layer_success - Notification of lower layer success + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL (and EAP) state machines that a lower layer has detected a + * successful authentication. This is used to recover from dropped EAP-Success + * messages. + */ +void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eap_notify_lower_layer_success(sm->eap); +} + + static struct wpa_ssid * eapol_sm_get_config(void *ctx) { struct eapol_sm *sm = ctx; @@ -1409,6 +1619,25 @@ static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, } +static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob) +{ + struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->set_config_blob) + sm->ctx->set_config_blob(sm->ctx->ctx, blob); +} + + +static const struct wpa_config_blob * +eapol_sm_get_config_blob(void *ctx, const char *name) +{ + struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->get_config_blob) + return sm->ctx->get_config_blob(sm->ctx->ctx, name); + else + return NULL; +} + + static struct eapol_callbacks eapol_cb = { .get_config = eapol_sm_get_config, @@ -1417,4 +1646,77 @@ static struct eapol_callbacks eapol_cb = .get_int = eapol_sm_get_int, .set_int = eapol_sm_set_int, .get_eapReqData = eapol_sm_get_eapReqData, + .set_config_blob = eapol_sm_set_config_blob, + .get_config_blob = eapol_sm_get_config_blob, }; + + +/** + * eapol_sm_init - Initialize EAPOL state machine + * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer + * and EAPOL state machine will free it in eapol_sm_deinit() + * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure + * + * Allocate and initialize an EAPOL state machine. + */ +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) +{ + struct eapol_sm *sm; + struct eap_config conf; + sm = malloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + memset(sm, 0, sizeof(*sm)); + sm->ctx = ctx; + + sm->portControl = Auto; + + /* Supplicant PAE state machine */ + sm->heldPeriod = 60; + sm->startPeriod = 30; + sm->maxStart = 3; + + /* Supplicant Backend state machine */ + sm->authPeriod = 30; + + memset(&conf, 0, sizeof(conf)); + conf.opensc_engine_path = ctx->opensc_engine_path; + conf.pkcs11_engine_path = ctx->pkcs11_engine_path; + conf.pkcs11_module_path = ctx->pkcs11_module_path; + + sm->eap = eap_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); + if (sm->eap == NULL) { + free(sm); + return NULL; + } + + /* Initialize EAPOL state machines */ + sm->initialize = TRUE; + eapol_sm_step(sm); + sm->initialize = FALSE; + eapol_sm_step(sm); + + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + + return sm; +} + + +/** + * eapol_sm_deinit - Deinitialize EAPOL state machine + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Deinitialize and free EAPOL state machine. + */ +void eapol_sm_deinit(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eap_sm_deinit(sm->eap); + free(sm->last_rx_key); + free(sm->eapReqData); + free(sm->ctx); + free(sm); +} diff --git a/contrib/wpa_supplicant/eapol_sm.h b/contrib/wpa_supplicant/eapol_sm.h index b941203..1d44a54 100644 --- a/contrib/wpa_supplicant/eapol_sm.h +++ b/contrib/wpa_supplicant/eapol_sm.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / EAPOL state machines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 EAPOL_SM_H #define EAPOL_SM_H @@ -6,28 +20,177 @@ typedef enum { Unauthorized, Authorized } PortStatus; typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; +/** + * struct eapol_config - Per network configuration for EAPOL state machines + */ struct eapol_config { + /** + * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames + * + * This variable should be set to 1 when using EAPOL state machines + * with non-WPA security policy to generate dynamic WEP keys. When + * using WPA, this should be set to 0 so that WPA state machine can + * process the EAPOL-Key frames. + */ int accept_802_1x_keys; + #define EAPOL_REQUIRE_KEY_UNICAST BIT(0) #define EAPOL_REQUIRE_KEY_BROADCAST BIT(1) - int required_keys; /* which EAPOL-Key packets are required before - * marking connection authenticated */ - int fast_reauth; /* whether fast EAP reauthentication is enabled */ - int workaround; /* whether EAP workarounds are enabled */ + /** + * required_keys - Which EAPOL-Key packets are required + * + * This variable determines which EAPOL-Key packets are required before + * marking connection authenticated. This is a bit field of + * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags. + */ + int required_keys; + + /** + * fast_reauth - Whether fast EAP reauthentication is enabled + */ + int fast_reauth; + + /** + * workaround - Whether EAP workarounds are enabled + */ + unsigned int workaround; + + /** + * eap_disabled - Whether EAP is disabled + */ + int eap_disabled; }; struct eapol_sm; +struct wpa_config_blob; +/** + * struct eapol_ctx - Global (for all networks) EAPOL state machine context + */ struct eapol_ctx { - void *ctx; /* pointer to arbitrary upper level context */ - int preauth; /* This EAPOL state machine is used for IEEE 802.11i/RSN - * pre-authentication */ + /** + * ctx - Pointer to arbitrary upper level context + */ + void *ctx; + + /** + * preauth - IEEE 802.11i/RSN pre-authentication + * + * This EAPOL state machine is used for IEEE 802.11i/RSN + * pre-authentication + */ + int preauth; + + /** + * cb - Function to be called when EAPOL negotiation has been completed + * @eapol: Pointer to EAPOL state machine data + * @success: Whether the authentication was completed successfully + * @ctx: Pointer to context data (cb_ctx) + * + * This optional callback function will be called when the EAPOL + * authentication has been completed. This allows the owner of the + * EAPOL state machine to process the key and terminate the EAPOL state + * machine. Currently, this is used only in RSN pre-authentication. + */ void (*cb)(struct eapol_sm *eapol, int success, void *ctx); - void *cb_ctx, *msg_ctx, *scard_ctx; + + /** + * cb_ctx - Callback context for cb() + */ + void *cb_ctx; + + /** + * msg_ctx - Callback context for wpa_msg() calls + */ + void *msg_ctx; + + /** + * scard_ctx - Callback context for PC/SC scard_*() function calls + * + * This context can be updated with eapol_sm_register_scard_ctx(). + */ + void *scard_ctx; + + /** + * eapol_send_ctx - Callback context for eapol_send() calls + */ + void *eapol_send_ctx; + + /** + * eapol_done_cb - Function to be called at successful completion + * @ctx: Callback context (ctx) + * + * This function is called at the successful completion of EAPOL + * authentication. If dynamic WEP keys are used, this is called only + * after all the expected keys have been received. + */ void (*eapol_done_cb)(void *ctx); - int (*eapol_send)(void *ctx, int type, u8 *buf, size_t len); + + /** + * eapol_send - Send EAPOL packets + * @ctx: Callback context (eapol_send_ctx) + * @type: EAPOL type (IEEE802_1X_TYPE_*) + * @buf: Pointer to EAPOL payload + * @len: Length of the EAPOL payload + * Returns: 0 on success, -1 on failure + */ + int (*eapol_send)(void *ctx, int type, const u8 *buf, size_t len); + + /** + * set_wep_key - Configure WEP keys + * @ctx: Callback context (ctx) + * @unicast: Non-zero = unicast, 0 = multicast/broadcast key + * @keyidx: Key index (0..3) + * @key: WEP key + * @keylen: Length of the WEP key + * Returns: 0 on success, -1 on failure + */ int (*set_wep_key)(void *ctx, int unicast, int keyidx, - u8 *key, size_t keylen); + const u8 *key, size_t keylen); + + /** + * set_config_blob - Set or add a named configuration blob + * @ctx: Callback context (ctx) + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an + * existing blob. + */ + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + + /** + * get_config_blob - Get a named configuration blob + * @ctx: Callback context (ctx) + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + + /** + * opensc_engine_path - Path to the OpenSSL engine for opensc + * + * This is an OpenSSL specific configuration option for loading OpenSC + * engine (engine_opensc.so); if %NULL, this engine is not loaded. + */ + const char *opensc_engine_path; + + /** + * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11 + * + * This is an OpenSSL specific configuration option for loading PKCS#11 + * engine (engine_pkcs11.so); if %NULL, this engine is not loaded. + */ + const char *pkcs11_engine_path; + + /** + * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module + * + * This is an OpenSSL specific configuration option for configuring + * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this + * module is not loaded. + */ + const char *pkcs11_module_path; }; @@ -42,14 +205,15 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen); void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, int startPeriod, int maxStart); -void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len); +int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, + size_t len); void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm); void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled); void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid); void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success); void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail); void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config, - struct eapol_config *conf); + const struct eapol_config *conf); int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len); void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff); void eapol_sm_notify_cached(struct eapol_sm *sm); @@ -58,9 +222,12 @@ void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx); void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl); void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm); void eapol_sm_notify_ctrl_response(struct eapol_sm *sm); +void eapol_sm_request_reauth(struct eapol_sm *sm); +void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { + free(ctx); return (struct eapol_sm *) 1; } static inline void eapol_sm_deinit(struct eapol_sm *sm) @@ -84,9 +251,10 @@ static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int maxStart) { } -static inline void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, - size_t len) +static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, + const u8 *buf, size_t len) { + return 0; } static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) { @@ -133,6 +301,12 @@ static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) { } +static inline void eapol_sm_request_reauth(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm) +{ +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SM_H */ diff --git a/contrib/wpa_supplicant/eapol_test.c b/contrib/wpa_supplicant/eapol_test.c index 061ae89..1bc34a3 100644 --- a/contrib/wpa_supplicant/eapol_test.c +++ b/contrib/wpa_supplicant/eapol_test.c @@ -1,6 +1,15 @@ /* * WPA Supplicant - test code - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2003-2006, Jouni Malinen <jkmaline@cc.hut.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. @@ -13,9 +22,11 @@ #include <ctype.h> #include <string.h> #include <signal.h> +#ifndef CONFIG_NATIVE_WINDOWS #include <netinet/in.h> -#include <assert.h> #include <arpa/inet.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <assert.h> #include "common.h" #include "config.h" @@ -34,139 +45,147 @@ extern int wpa_debug_level; extern int wpa_debug_show_keys; -static int eapol_test_num_reauths = 0; -static int no_mppe_keys = 0; -static int num_mppe_ok = 0, num_mppe_mismatch = 0; -static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx); +struct wpa_driver_ops *wpa_supplicant_drivers[] = { }; -void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...) -{ - va_list ap; - char *buf; - const int buflen = 2048; - int len; - - buf = malloc(buflen); - if (buf == NULL) { - printf("Failed to allocate message buffer for:\n"); - va_start(ap, fmt); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); - return; - } - va_start(ap, fmt); - len = vsnprintf(buf, buflen, fmt, ap); - va_end(ap); - wpa_printf(level, "%s", buf); - wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len); - free(buf); -} +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; -void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, - union wpa_event_data *data) -{ -} + 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; -int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst) -{ - return -1; -} + 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; +}; -void rsn_preauth_deinit(struct wpa_supplicant *wpa_s) -{ -} +static struct eapol_test_data eapol_test; -int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len) -{ - return 0; -} +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx); -int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +void hostapd_logger(void *ctx, u8 *addr, unsigned int module, int level, + char *fmt, ...) { - return 0; -} + char *format; + int maxlen; + va_list ap; + maxlen = strlen(fmt) + 100; + format = malloc(maxlen); + if (!format) + return; -void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) -{ -} + va_start(ap, fmt); -const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len) -{ - return NULL; + if (addr) + snprintf(format, maxlen, "STA " MACSTR ": %s", + MAC2STR(addr), fmt); + else + snprintf(format, maxlen, "%s", fmt); + + vprintf(format, ap); + printf("\n"); + + free(format); + + va_end(ap); } -int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen) { - return -1; + if (buflen == 0 || addr == NULL) + return NULL; + + if (addr->af == AF_INET) { + snprintf(buf, buflen, "%s", inet_ntoa(addr->u.v4)); + } else { + buf[0] = '\0'; + } +#ifdef CONFIG_IPV6 + if (addr->af == AF_INET6) { + if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL) + buf[0] = '\0'; + } +#endif /* CONFIG_IPV6 */ + + return buf; } -static void ieee802_1x_encapsulate_radius(struct wpa_supplicant *wpa_s, - u8 *eap, size_t len) +static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, + const u8 *eap, size_t len) { struct radius_msg *msg; char buf[128]; - struct eap_hdr *hdr; - u8 *pos; + const struct eap_hdr *hdr; + const u8 *pos; wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " "packet"); - wpa_s->radius_identifier = radius_client_get_id(wpa_s); + e->radius_identifier = radius_client_get_id(e->radius); msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, - wpa_s->radius_identifier); + e->radius_identifier); if (msg == NULL) { printf("Could not create net RADIUS packet\n"); return; } - radius_msg_make_authenticator(msg, (u8 *) wpa_s, sizeof(*wpa_s)); + radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e)); - hdr = (struct eap_hdr *) eap; - pos = (u8 *) (hdr + 1); + 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++; - free(wpa_s->eap_identity); - wpa_s->eap_identity_len = len - sizeof(*hdr) - 1; - wpa_s->eap_identity = malloc(wpa_s->eap_identity_len); - if (wpa_s->eap_identity) { - memcpy(wpa_s->eap_identity, pos, - wpa_s->eap_identity_len); + free(e->eap_identity); + e->eap_identity_len = len - sizeof(*hdr) - 1; + e->eap_identity = malloc(e->eap_identity_len); + if (e->eap_identity) { + memcpy(e->eap_identity, pos, e->eap_identity_len); wpa_hexdump(MSG_DEBUG, "Learned identity from " "EAP-Response-Identity", - wpa_s->eap_identity, - wpa_s->eap_identity_len); + e->eap_identity, e->eap_identity_len); } } - if (wpa_s->eap_identity && + if (e->eap_identity && !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, - wpa_s->eap_identity, - wpa_s->eap_identity_len)) { + e->eap_identity, e->eap_identity_len)) { printf("Could not add User-Name\n"); goto fail; } if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &wpa_s->own_ip_addr, 4)) { + (u8 *) &e->own_ip_addr, 4)) { printf("Could not add NAS-IP-Address\n"); goto fail; } snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(wpa_s->own_addr)); + MAC2STR(e->wpa_s->own_addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, (u8 *) buf, strlen(buf))) { printf("Could not add Calling-Station-Id\n"); @@ -201,9 +220,9 @@ static void ieee802_1x_encapsulate_radius(struct wpa_supplicant *wpa_s, /* State attribute must be copied if and only if this packet is * Access-Request reply to the previous Access-Challenge */ - if (wpa_s->last_recv_radius && wpa_s->last_recv_radius->hdr->code == + if (e->last_recv_radius && e->last_recv_radius->hdr->code == RADIUS_CODE_ACCESS_CHALLENGE) { - int res = radius_msg_copy_attr(msg, wpa_s->last_recv_radius, + 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 " @@ -216,7 +235,7 @@ static void ieee802_1x_encapsulate_radius(struct wpa_supplicant *wpa_s, } } - radius_client_send(wpa_s, msg, RADIUS_AUTH, wpa_s->own_addr); + radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr); return; fail: @@ -225,18 +244,35 @@ static void ieee802_1x_encapsulate_radius(struct wpa_supplicant *wpa_s, } -static int eapol_test_eapol_send(void *ctx, int type, u8 *buf, size_t len) +static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf, + size_t len) { - struct wpa_supplicant *wpa_s = ctx; - printf("WPA: wpa_eapol_send(type=%d len=%d)\n", type, len); + /* struct wpa_supplicant *wpa_s = ctx; */ + printf("WPA: eapol_test_eapol_send(type=%d len=%d)\n", type, len); if (type == IEEE802_1X_TYPE_EAP_PACKET) { wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len); - ieee802_1x_encapsulate_radius(wpa_s, 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"); @@ -245,40 +281,40 @@ static void eapol_test_eapol_done_cb(void *ctx) static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx) { - struct wpa_supplicant *wpa_s = eloop_ctx; + struct eapol_test_data *e = eloop_ctx; printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n"); - wpa_s->radius_access_accept_received = 0; - send_eap_request_identity(eloop_ctx, timeout_ctx); + e->radius_access_accept_received = 0; + send_eap_request_identity(e->wpa_s, NULL); } -static int eapol_test_compare_pmk(struct wpa_supplicant *wpa_s) +static int eapol_test_compare_pmk(struct eapol_test_data *e) { u8 pmk[PMK_LEN]; int ret = 1; - if (eapol_sm_get_key(wpa_s->eapol, pmk, PMK_LEN) == 0) { + if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN); - if (memcmp(pmk, wpa_s->authenticator_pmk, PMK_LEN) != 0) + if (memcmp(pmk, e->authenticator_pmk, PMK_LEN) != 0) printf("WARNING: PMK mismatch\n"); - else if (wpa_s->radius_access_accept_received) + else if (e->radius_access_accept_received) ret = 0; - } else if (wpa_s->authenticator_pmk_len == 16 && - eapol_sm_get_key(wpa_s->eapol, pmk, 16) == 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 (memcmp(pmk, wpa_s->authenticator_pmk, 16) != 0) + if (memcmp(pmk, e->authenticator_pmk, 16) != 0) printf("WARNING: PMK mismatch\n"); - else if (wpa_s->radius_access_accept_received) + else if (e->radius_access_accept_received) ret = 0; - } else if (wpa_s->radius_access_accept_received && no_mppe_keys) { + } else if (e->radius_access_accept_received && e->no_mppe_keys) { /* No keying material expected */ ret = 0; } if (ret) - num_mppe_mismatch++; - else if (!no_mppe_keys) - num_mppe_ok++; + e->num_mppe_mismatch++; + else if (!e->no_mppe_keys) + e->num_mppe_ok++; return ret; } @@ -286,20 +322,20 @@ static int eapol_test_compare_pmk(struct wpa_supplicant *wpa_s) static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) { - struct wpa_supplicant *wpa_s = ctx; + struct eapol_test_data *e = ctx; printf("eapol_sm_cb: success=%d\n", success); - eapol_test_num_reauths--; - if (eapol_test_num_reauths < 0) + e->eapol_test_num_reauths--; + if (e->eapol_test_num_reauths < 0) eloop_terminate(); else { - eapol_test_compare_pmk(wpa_s); - eloop_register_timeout(0, 100000, eapol_sm_reauth, - wpa_s, NULL); + eapol_test_compare_pmk(e); + eloop_register_timeout(0, 100000, eapol_sm_reauth, e, NULL); } } -static int test_eapol(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +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; @@ -314,10 +350,16 @@ static int test_eapol(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) ctx->msg_ctx = wpa_s; ctx->scard_ctx = wpa_s->scard; ctx->cb = eapol_sm_cb; - ctx->cb_ctx = wpa_s; + 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) { @@ -344,22 +386,25 @@ static int test_eapol(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) } -static void test_eapol_clean(struct wpa_supplicant *wpa_s) +static void test_eapol_clean(struct eapol_test_data *e, + struct wpa_supplicant *wpa_s) { - radius_client_deinit(wpa_s); - free(wpa_s->last_eap_radius); - if (wpa_s->last_recv_radius) { - radius_msg_free(wpa_s->last_recv_radius); - free(wpa_s->last_recv_radius); - } - free(wpa_s->eap_identity); - wpa_s->eap_identity = NULL; + radius_client_deinit(e->radius); + free(e->last_eap_radius); + if (e->last_recv_radius) { + radius_msg_free(e->last_recv_radius); + free(e->last_recv_radius); + } + free(e->eap_identity); + e->eap_identity = NULL; eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; - if (wpa_s->auth_server) { - free(wpa_s->auth_server->shared_secret); - free(wpa_s->auth_server); + if (e->radius_conf && e->radius_conf->auth_server) { + free(e->radius_conf->auth_server->shared_secret); + free(e->radius_conf->auth_server); } + free(e->radius_conf); + e->radius_conf = NULL; scard_deinit(wpa_s->scard); wpa_supplicant_ctrl_iface_deinit(wpa_s); wpa_config_free(wpa_s->conf); @@ -393,9 +438,9 @@ static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx) static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx) { - struct wpa_supplicant *wpa_s = eloop_ctx; + struct eapol_test_data *e = eloop_ctx; printf("EAPOL test timed out\n"); - wpa_s->auth_timed_out = 1; + e->auth_timed_out = 1; eloop_terminate(); } @@ -418,7 +463,7 @@ static char *eap_type_text(u8 type) } -static void ieee802_1x_decapsulate_radius(struct wpa_supplicant *wpa_s) +static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) { u8 *eap; size_t len; @@ -427,10 +472,10 @@ static void ieee802_1x_decapsulate_radius(struct wpa_supplicant *wpa_s) char buf[64]; struct radius_msg *msg; - if (wpa_s->last_recv_radius == NULL) + if (e->last_recv_radius == NULL) return; - msg = wpa_s->last_recv_radius; + msg = e->last_recv_radius; eap = radius_msg_get_eap(msg, &len); if (eap == NULL) { @@ -439,9 +484,9 @@ static void ieee802_1x_decapsulate_radius(struct wpa_supplicant *wpa_s) * attribute */ wpa_printf(MSG_DEBUG, "could not extract " "EAP-Message from RADIUS message"); - free(wpa_s->last_eap_radius); - wpa_s->last_eap_radius = NULL; - wpa_s->last_eap_radius_len = 0; + free(e->last_eap_radius); + e->last_eap_radius = NULL; + e->last_eap_radius_len = 0; return; } @@ -487,10 +532,9 @@ static void ieee802_1x_decapsulate_radius(struct wpa_supplicant *wpa_s) /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */ - if (wpa_s->last_eap_radius) - free(wpa_s->last_eap_radius); - wpa_s->last_eap_radius = eap; - wpa_s->last_eap_radius_len = len; + free(e->last_eap_radius); + e->last_eap_radius = eap; + e->last_eap_radius_len = len; { struct ieee802_1x_hdr *hdr; @@ -500,14 +544,14 @@ static void ieee802_1x_decapsulate_radius(struct wpa_supplicant *wpa_s) hdr->type = IEEE802_1X_TYPE_EAP_PACKET; hdr->length = htons(len); memcpy((u8 *) (hdr + 1), eap, len); - eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, + eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid, (u8 *) hdr, sizeof(*hdr) + len); free(hdr); } } -static void ieee802_1x_get_keys(struct wpa_supplicant *wpa_s, +static void ieee802_1x_get_keys(struct eapol_test_data *e, struct radius_msg *msg, struct radius_msg *req, u8 *shared_secret, size_t shared_secret_len) { @@ -529,11 +573,11 @@ static void ieee802_1x_get_keys(struct wpa_supplicant *wpa_s, if (keys->recv) { wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)", keys->recv, keys->recv_len); - wpa_s->authenticator_pmk_len = + e->authenticator_pmk_len = keys->recv_len > PMK_LEN ? PMK_LEN : keys->recv_len; - memcpy(wpa_s->authenticator_pmk, keys->recv, - wpa_s->authenticator_pmk_len); + memcpy(e->authenticator_pmk, keys->recv, + e->authenticator_pmk_len); } free(keys->send); @@ -545,11 +589,12 @@ static void ieee802_1x_get_keys(struct wpa_supplicant *wpa_s, /* Process the RADIUS frames from Authentication Server */ static RadiusRxResult -ieee802_1x_receive_auth(struct wpa_supplicant *wpa_s, - struct radius_msg *msg, struct radius_msg *req, +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, u8 *shared_secret, size_t shared_secret_len, void *data) { + struct eapol_test_data *e = data; + /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be * present when packet contains an EAP-Message attribute */ if (msg->hdr->code == RADIUS_CODE_ACCESS_REJECT && @@ -560,7 +605,7 @@ ieee802_1x_receive_auth(struct wpa_supplicant *wpa_s, "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)) { + req, 1)) { printf("Incoming RADIUS packet did not have correct " "Message-Authenticator - dropped\n"); return RADIUS_RX_UNKNOWN; @@ -573,31 +618,31 @@ ieee802_1x_receive_auth(struct wpa_supplicant *wpa_s, return RADIUS_RX_UNKNOWN; } - wpa_s->radius_identifier = -1; + e->radius_identifier = -1; wpa_printf(MSG_DEBUG, "RADIUS packet matching with station"); - if (wpa_s->last_recv_radius) { - radius_msg_free(wpa_s->last_recv_radius); - free(wpa_s->last_recv_radius); + if (e->last_recv_radius) { + radius_msg_free(e->last_recv_radius); + free(e->last_recv_radius); } - wpa_s->last_recv_radius = msg; + e->last_recv_radius = msg; switch (msg->hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: - wpa_s->radius_access_accept_received = 1; - ieee802_1x_get_keys(wpa_s, msg, req, shared_secret, + e->radius_access_accept_received = 1; + ieee802_1x_get_keys(e, msg, req, shared_secret, shared_secret_len); break; case RADIUS_CODE_ACCESS_REJECT: - wpa_s->radius_access_reject_received = 1; + e->radius_access_reject_received = 1; break; } - ieee802_1x_decapsulate_radius(wpa_s); + ieee802_1x_decapsulate_radius(e); if ((msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && - eapol_test_num_reauths < 0) || + e->eapol_test_num_reauths < 0) || msg->hdr->code == RADIUS_CODE_ACCESS_REJECT) { eloop_terminate(); } @@ -606,77 +651,8 @@ ieee802_1x_receive_auth(struct wpa_supplicant *wpa_s, } -static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - int aka = 0; - u8 *pos = ssid->eap_methods; - - while (pos && *pos != EAP_TYPE_NONE) { - if (*pos == EAP_TYPE_AKA) { - aka = 1; - break; - } - pos++; - } - - if (ssid->identity == NULL && wpa_s->imsi) { - ssid->identity = malloc(1 + wpa_s->imsi_len); - if (ssid->identity) { - ssid->identity[0] = aka ? '0' : '1'; - memcpy(ssid->identity + 1, wpa_s->imsi, - wpa_s->imsi_len); - ssid->identity_len = 1 + wpa_s->imsi_len; - wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " - "IMSI", ssid->identity, - ssid->identity_len); - } - } -} - - -static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - char buf[100]; - size_t len; - - if (ssid->pcsc == NULL) - return; - if (wpa_s->scard != NULL) { - wpa_supplicant_imsi_identity(wpa_s, ssid); - return; - } - wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - " - "initialize PCSC"); - wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin); - if (wpa_s->scard == NULL) { - wpa_printf(MSG_WARNING, "Failed to initialize SIM " - "(pcsc-lite)"); - /* TODO: what to do here? */ - return; - } - eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); - - len = sizeof(buf); - if (scard_get_imsi(wpa_s->scard, buf, &len)) { - wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); - /* TODO: what to do here? */ - return; - } - - wpa_hexdump(MSG_DEBUG, "IMSI", (u8 *) buf, len); - free(wpa_s->imsi); - wpa_s->imsi = malloc(len); - if (wpa_s->imsi) { - memcpy(wpa_s->imsi, buf, len); - wpa_s->imsi_len = len; - wpa_supplicant_imsi_identity(wpa_s, ssid); - } -} - - -static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *authsrv, +static void wpa_init_conf(struct eapol_test_data *e, + struct wpa_supplicant *wpa_s, const char *authsrv, int port, const char *secret) { struct hostapd_radius_server *as; @@ -684,24 +660,43 @@ static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *authsrv, wpa_s->bssid[5] = 1; wpa_s->own_addr[5] = 2; - wpa_s->own_ip_addr.s_addr = htonl((127 << 24) | 1); + e->own_ip_addr.s_addr = htonl((127 << 24) | 1); strncpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname)); - wpa_s->num_auth_servers = 1; + e->radius_conf = malloc(sizeof(struct hostapd_radius_servers)); + assert(e->radius_conf != NULL); + memset(e->radius_conf, 0, sizeof(struct hostapd_radius_servers)); + e->radius_conf->num_auth_servers = 1; as = malloc(sizeof(struct hostapd_radius_server)); assert(as != NULL); - inet_aton(authsrv, &as->addr); + memset(as, 0, sizeof(*as)); +#ifdef CONFIG_NATIVE_WINDOWS + { + 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 */ + inet_aton(authsrv, &as->addr.u.v4); +#endif /* CONFIG_NATIVE_WINDOWS */ + as->addr.af = AF_INET; as->port = port; as->shared_secret = (u8 *) strdup(secret); as->shared_secret_len = strlen(secret); - wpa_s->auth_server = wpa_s->auth_servers = as; + e->radius_conf->auth_server = as; + e->radius_conf->auth_servers = as; + e->radius_conf->msg_dumps = 1; + e->radius = radius_client_init(wpa_s, e->radius_conf); + assert(e->radius != NULL); - res = radius_client_init(wpa_s); - assert(res == 0); - - res = radius_client_register(wpa_s, RADIUS_AUTH, - ieee802_1x_receive_auth, NULL); + res = radius_client_register(e->radius, RADIUS_AUTH, + ieee802_1x_receive_auth, e); assert(res == 0); } @@ -736,9 +731,14 @@ static int scard_test(void) unsigned char aka_ik[IK_LEN]; unsigned char aka_ck[CK_LEN]; - scard = scard_init(SCARD_TRY_BOTH, "1234"); + 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)) @@ -827,11 +827,16 @@ static int scard_get_triplets(int argc, char *argv[]) wpa_debug_level = 99; } - scard = scard_init(SCARD_GSM_SIM_ONLY, argv[0]); + 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)) { @@ -877,7 +882,7 @@ static void eapol_test_terminate(int sig, void *eloop_ctx, static void usage(void) { printf("usage:\n" - "eapol_test [-n] -c<conf> [-a<AS IP>] [-p<AS port>] " + "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] " "[-s<AS secret>] [-r<count>]\n" "eapol_test scard\n" "eapol_test sim <PIN> <num triplets> [debug]\n" @@ -891,6 +896,8 @@ static void usage(void) " -s<AS secret> = shared secret with the authentication " "server, default 'radius'\n" " -r<count> = number of re-authentications\n" + " -W = wait for a control interface monitor before starting\n" + " -S = save configuration after authentiation\n" " -n = no MPPE keys expected\n"); } @@ -898,17 +905,27 @@ static void usage(void) int main(int argc, char *argv[]) { struct wpa_supplicant wpa_s; - int c, ret = 1; + 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 *conf = NULL; +#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 */ + + memset(&eapol_test, 0, sizeof(eapol_test)); + wpa_debug_level = 0; wpa_debug_show_keys = 1; for (;;) { - c = getopt(argc, argv, "a:c:np:r:s:"); + c = getopt(argc, argv, "a:c:np:r:s:SW"); if (c < 0) break; switch (c) { @@ -919,17 +936,23 @@ int main(int argc, char *argv[]) conf = optarg; break; case 'n': - no_mppe_keys++; + eapol_test.no_mppe_keys++; break; case 'p': as_port = atoi(optarg); break; case 'r': - eapol_test_num_reauths = atoi(optarg); + eapol_test.eapol_test_num_reauths = atoi(optarg); break; case 's': as_secret = optarg; break; + case 'S': + save_config++; + break; + case 'W': + wait_for_monitor++; + break; default: usage(); return -1; @@ -954,6 +977,7 @@ int main(int argc, char *argv[]) eloop_init(&wpa_s); 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); @@ -964,7 +988,7 @@ int main(int argc, char *argv[]) return -1; } - wpa_init_conf(&wpa_s, as_addr, as_port, as_secret); + wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret); if (wpa_supplicant_ctrl_iface_init(&wpa_s)) { printf("Failed to initialize control interface '%s'.\n" "You may have another eapol_test process already " @@ -976,37 +1000,50 @@ int main(int argc, char *argv[]) wpa_s.conf->ctrl_interface); return -1; } - wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid); + if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) + return -1; - if (test_eapol(&wpa_s, wpa_s.conf->ssid)) + if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid)) return -1; - eloop_register_timeout(30, 0, eapol_test_timeout, &wpa_s, NULL); + if (wait_for_monitor) + wpa_supplicant_ctrl_iface_wait(&wpa_s); + + eloop_register_timeout(30, 0, eapol_test_timeout, &eapol_test, NULL); eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL); eloop_register_signal(SIGINT, eapol_test_terminate, NULL); eloop_register_signal(SIGTERM, eapol_test_terminate, NULL); +#ifndef CONFIG_NATIVE_WINDOWS eloop_register_signal(SIGHUP, eapol_test_terminate, NULL); +#endif /* CONFIG_NATIVE_WINDOWS */ eloop_run(); - if (eapol_test_compare_pmk(&wpa_s) == 0) + if (eapol_test_compare_pmk(&eapol_test) == 0) ret = 0; - if (wpa_s.auth_timed_out) + if (eapol_test.auth_timed_out) ret = -2; - if (wpa_s.radius_access_reject_received) + if (eapol_test.radius_access_reject_received) ret = -3; - test_eapol_clean(&wpa_s); + if (save_config) + wpa_config_write(conf, wpa_s.conf); + + test_eapol_clean(&eapol_test, &wpa_s); eloop_destroy(); printf("MPPE keys OK: %d mismatch: %d\n", - num_mppe_ok, num_mppe_mismatch); - if (num_mppe_mismatch) + 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"); +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + return ret; } diff --git a/contrib/wpa_supplicant/eloop.c b/contrib/wpa_supplicant/eloop.c index 6071508..39fccb8 100644 --- a/contrib/wpa_supplicant/eloop.c +++ b/contrib/wpa_supplicant/eloop.c @@ -1,5 +1,5 @@ /* - * Event loop + * Event loop based on select() loop * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -294,10 +294,16 @@ int eloop_register_signal(int sig, void eloop_run(void) { - fd_set rfds; + fd_set *rfds; int i, res; struct timeval tv, now; + rfds = malloc(sizeof(*rfds)); + if (rfds == NULL) { + printf("eloop_run - malloc failed\n"); + return; + } + while (!eloop.terminate && (eloop.timeout || eloop.reader_count > 0)) { if (eloop.timeout) { @@ -312,13 +318,14 @@ void eloop_run(void) #endif } - FD_ZERO(&rfds); + FD_ZERO(rfds); for (i = 0; i < eloop.reader_count; i++) - FD_SET(eloop.readers[i].sock, &rfds); - res = select(eloop.max_sock + 1, &rfds, NULL, NULL, + FD_SET(eloop.readers[i].sock, rfds); + res = select(eloop.max_sock + 1, rfds, NULL, NULL, eloop.timeout ? &tv : NULL); if (res < 0 && errno != EINTR) { perror("select"); + free(rfds); return; } eloop_process_pending_signals(); @@ -342,7 +349,7 @@ void eloop_run(void) continue; for (i = 0; i < eloop.reader_count; i++) { - if (FD_ISSET(eloop.readers[i].sock, &rfds)) { + if (FD_ISSET(eloop.readers[i].sock, rfds)) { eloop.readers[i].handler( eloop.readers[i].sock, eloop.readers[i].eloop_data, @@ -350,6 +357,8 @@ void eloop_run(void) } } } + + free(rfds); } diff --git a/contrib/wpa_supplicant/eloop.h b/contrib/wpa_supplicant/eloop.h index f5b8847..c57e682 100644 --- a/contrib/wpa_supplicant/eloop.h +++ b/contrib/wpa_supplicant/eloop.h @@ -1,53 +1,152 @@ +/* + * Event loop + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.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 defines an event loop interface that supports processing events + * from registered timeouts (i.e., do something after N seconds), sockets + * (e.g., a new packet available for reading), and signals. eloop.c is an + * implementation of this interface using select() and sockets. This is + * suitable for most UNIX/POSIX systems. When porting to other operating + * systems, it may be necessary to replace that implementation with OS specific + * mechanisms. + */ + #ifndef ELOOP_H #define ELOOP_H /* Magic number for eloop_cancel_timeout() */ #define ELOOP_ALL_CTX (void *) -1 -/* Initialize global event loop data - must be called before any other eloop_* - * function. user_data is a pointer to global data structure and will be passed - * as eloop_ctx to signal handlers. */ +/** + * eloop_init() - Initialize global event loop data + * @user_data: Pointer to global data passed as eloop_ctx to signal handlers + * + * This function must be called before any other eloop_* function. user_data + * can be used to configure a global (to the process) pointer that will be + * passed as eloop_ctx parameter to signal handlers. + */ void eloop_init(void *user_data); -/* Register handler for read event */ +/** + * eloop_register_read_sock - Register handler for read events + * @sock: File descriptor number for the socket + * @handler: Callback function to be called when data is available for reading + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a read socket notifier for the given file descriptor. The handler + * function will be called whenever data is available for reading from the + * socket. + */ int eloop_register_read_sock(int sock, void (*handler)(int sock, void *eloop_ctx, void *sock_ctx), void *eloop_data, void *user_data); + +/** + * eloop_unregister_read_sock - Unregister handler for read events + * @sock: File descriptor number for the socket + * + * Unregister a read socket notifier that was previously registered with + * eloop_register_read_sock(). + */ void eloop_unregister_read_sock(int sock); -/* Register timeout */ +/** + * eloop_register_timeout - Register timeout + * @secs: Number of seconds to the timeout + * @usecs: Number of microseconds to the timeout + * @handler: Callback function to be called when timeout occurs + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a timeout that will cause the handler function to be called after + * given time. + */ int eloop_register_timeout(unsigned int secs, unsigned int usecs, void (*handler)(void *eloop_ctx, void *timeout_ctx), void *eloop_data, void *user_data); -/* Cancel timeouts matching <handler,eloop_data,user_data>. - * ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts - * regardless of eloop_data/user_data. */ +/** + * eloop_cancel_timeout - Cancel timeouts + * @handler: Matching callback function + * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all + * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all + * Returns: Number of cancelled timeouts + * + * Cancel matching <handler,eloop_data,user_data> timeouts registered with + * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for + * cancelling all timeouts regardless of eloop_data/user_data. + */ int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), void *eloop_data, void *user_data); -/* Register handler for signal. - * Note: signals are 'global' events and there is no local eloop_data pointer - * like with other handlers. The (global) pointer given to eloop_init() will be - * used as eloop_ctx for signal handlers. */ -int eloop_register_signal(int sock, +/** + * eloop_register_signal - Register handler for signals + * @sig: Signal number (e.g., SIGHUP) + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a signal is received. + * The calback function is actually called only after the system signal handler + * has returned. This means that the normal limits for sighandlers (i.e., only + * "safe functions" allowed) do not apply for the registered callback. + * + * Signals are 'global' events and there is no local eloop_data pointer like + * with other handlers. The global user_data pointer registered with + * eloop_init() will be used as eloop_ctx for signal handlers. + */ +int eloop_register_signal(int sig, void (*handler)(int sig, void *eloop_ctx, void *signal_ctx), void *user_data); -/* Start event loop and continue running as long as there are any registered - * event handlers. */ +/** + * eloop_run - Start the event loop + * + * Start the event loop and continue running as long as there are any + * registered event handlers. This function is run after event loop has been + * initialized with event_init() and one or more events have been registered. + */ void eloop_run(void); -/* Terminate event loop even if there are registered events. */ +/** + * eloop_terminate - Terminate event loop + * + * Terminate event loop even if there are registered events. This can be used + * to request the program to be terminated cleanly. + */ void eloop_terminate(void); -/* Free any reserved resources. After calling eloop_destoy(), other eloop_* - * functions must not be called before re-running eloop_init(). */ +/** + * eloop_destroy - Free any resources allocated for the event loop + * + * After calling eloop_destoy(), other eloop_* functions must not be called + * before re-running eloop_init(). + */ void eloop_destroy(void); -/* Check whether event loop has been terminated. */ +/** + * eloop_terminated - Check whether event loop has been terminated + * Returns: 1 = event loop terminate, 0 = event loop still running + * + * This function can be used to check whether eloop_terminate() has been called + * to request termination of the event loop. This is normally used to abort + * operations that may still be queued to be run when eloop_terminate() was + * called. + */ int eloop_terminated(void); #endif /* ELOOP_H */ diff --git a/contrib/wpa_supplicant/events.c b/contrib/wpa_supplicant/events.c new file mode 100644 index 0000000..d8762e9 --- /dev/null +++ b/contrib/wpa_supplicant/events.c @@ -0,0 +1,759 @@ +/* + * WPA Supplicant - Driver event processing + * Copyright (c) 2003-2006, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +#include "common.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "eloop.h" +#include "wpa_supplicant.h" +#include "config.h" +#include "l2_packet.h" +#include "wpa_supplicant_i.h" +#include "pcsc_funcs.h" +#include "preauth.h" +#include "wpa_ctrl.h" +#include "eap.h" + + +static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) + return 0; + + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) { + wpa_printf(MSG_INFO, "No network configuration found for the " + "current AP"); + return -1; + } + + if (ssid->disabled) { + wpa_printf(MSG_DEBUG, "Selected network is disabled"); + return -1; + } + + wpa_printf(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)) { + 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); + } + + wpa_s->current_ssid = ssid; + wpa_sm_set_config(wpa_s->wpa, wpa_s->current_ssid); + wpa_supplicant_initiate_eapol(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); + } +} + + +static void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) +{ + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + memset(wpa_s->bssid, 0, ETH_ALEN); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); +} + + +static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) +{ + struct wpa_ie_data ie; + int i, pmksa_set = -1; + + 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_printf(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_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event"); + return; + } + wpa_printf(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; + + 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; + } + + 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) +{ + int aka = 0, sim = 0, type; + + if (ssid->pcsc == NULL || wpa_s->scard != NULL) + return 0; + + if (ssid->eap_methods == NULL) { + sim = 1; + aka = 1; + } else { + u8 *eap = ssid->eap_methods; + while (*eap != EAP_TYPE_NONE) { + if (*eap == EAP_TYPE_SIM) + sim = 1; + else if (*eap == EAP_TYPE_AKA) + aka = 1; + eap++; + } + } + +#ifndef EAP_SIM + sim = 0; +#endif /* EAP_SIM */ +#ifndef EAP_AKA + aka = 0; +#endif /* EAP_AKA */ + + if (!sim && !aka) { + wpa_printf(MSG_DEBUG, "Selected network is configured to use " + "SIM, but neither EAP-SIM nor EAP-AKA are enabled"); + return 0; + } + + wpa_printf(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_printf(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); + + return 0; +} + + +static int wpa_supplicant_match_privacy(struct wpa_scan_result *bss, + struct wpa_ssid *ssid) +{ + int i, privacy = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) { + privacy = 1; + break; + } + } + 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; + + if (bss->caps & IEEE80211_CAP_PRIVACY) + return privacy; + return !privacy; +} + + +static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid, + struct wpa_scan_result *bss) +{ + struct wpa_ie_data ie; + int proto_match = 0; + + while ((ssid->proto & WPA_PROTO_RSN) && bss->rsn_ie_len > 0) { + proto_match++; + + if (wpa_parse_wpa_ie(bss->rsn_ie, bss->rsn_ie_len, &ie)) { + wpa_printf(MSG_DEBUG, " skip RSN IE - parse failed"); + break; + } + if (!(ie.proto & ssid->proto)) { + wpa_printf(MSG_DEBUG, " skip RSN IE - proto " + "mismatch"); + break; + } + + if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { + wpa_printf(MSG_DEBUG, " skip RSN IE - PTK cipher " + "mismatch"); + break; + } + + if (!(ie.group_cipher & ssid->group_cipher)) { + wpa_printf(MSG_DEBUG, " skip RSN IE - GTK cipher " + "mismatch"); + break; + } + + if (!(ie.key_mgmt & ssid->key_mgmt)) { + wpa_printf(MSG_DEBUG, " skip RSN IE - key mgmt " + "mismatch"); + break; + } + + wpa_printf(MSG_DEBUG, " selected based on RSN IE"); + return 1; + } + + while ((ssid->proto & WPA_PROTO_WPA) && bss->wpa_ie_len > 0) { + proto_match++; + + if (wpa_parse_wpa_ie(bss->wpa_ie, bss->wpa_ie_len, &ie)) { + wpa_printf(MSG_DEBUG, " skip WPA IE - parse failed"); + break; + } + if (!(ie.proto & ssid->proto)) { + wpa_printf(MSG_DEBUG, " skip WPA IE - proto " + "mismatch"); + break; + } + + if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { + wpa_printf(MSG_DEBUG, " skip WPA IE - PTK cipher " + "mismatch"); + break; + } + + if (!(ie.group_cipher & ssid->group_cipher)) { + wpa_printf(MSG_DEBUG, " skip WPA IE - GTK cipher " + "mismatch"); + break; + } + + if (!(ie.key_mgmt & ssid->key_mgmt)) { + wpa_printf(MSG_DEBUG, " skip WPA IE - key mgmt " + "mismatch"); + break; + } + + wpa_printf(MSG_DEBUG, " selected based on WPA IE"); + return 1; + } + + if (proto_match == 0) + wpa_printf(MSG_DEBUG, " skip - no WPA/RSN proto match"); + + return 0; +} + + +static struct wpa_scan_result * +wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, + struct wpa_scan_result *results, int num, + struct wpa_ssid **selected_ssid) +{ + struct wpa_ssid *ssid; + struct wpa_scan_result *bss, *selected = NULL; + int i; + struct wpa_blacklist *e; + + wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d", + group->priority); + + bss = NULL; + ssid = NULL; + /* First, try to find WPA-enabled AP */ + for (i = 0; i < num && !selected; i++) { + bss = &results[i]; + wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " + "wpa_ie_len=%lu rsn_ie_len=%lu caps=0x%x", + i, MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), + (unsigned long) bss->wpa_ie_len, + (unsigned long) bss->rsn_ie_len, bss->caps); + if ((e = wpa_blacklist_get(wpa_s, bss->bssid)) && + e->count > 1) { + wpa_printf(MSG_DEBUG, " skip - blacklisted"); + continue; + } + + if (bss->wpa_ie_len == 0 && bss->rsn_ie_len == 0) { + wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); + continue; + } + + for (ssid = group; ssid; ssid = ssid->pnext) { + if (ssid->disabled) + continue; + if (bss->ssid_len != ssid->ssid_len || + memcmp(bss->ssid, ssid->ssid, + bss->ssid_len) != 0) { + wpa_printf(MSG_DEBUG, " skip - " + "SSID mismatch"); + continue; + } + if (ssid->bssid_set && + memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, " skip - " + "BSSID mismatch"); + continue; + } + if (wpa_supplicant_ssid_bss_match(ssid, bss)) { + selected = bss; + *selected_ssid = ssid; + break; + } + } + } + + /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration + * allows this. */ + for (i = 0; i < num && !selected; i++) { + bss = &results[i]; + if ((e = wpa_blacklist_get(wpa_s, bss->bssid)) && + e->count > 1) { + continue; + } + for (ssid = group; ssid; ssid = ssid->pnext) { + if (!ssid->disabled && + (ssid->ssid_len == 0 || + (bss->ssid_len == ssid->ssid_len && + memcmp(bss->ssid, ssid->ssid, bss->ssid_len) == + 0)) && + (!ssid->bssid_set || + memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0) && + ((ssid->key_mgmt & WPA_KEY_MGMT_NONE) || + (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) + && bss->wpa_ie_len == 0 && bss->rsn_ie_len == 0 && + wpa_supplicant_match_privacy(bss, ssid) && + !(bss->caps & IEEE80211_CAP_IBSS)) + { + selected = bss; + *selected_ssid = ssid; + wpa_printf(MSG_DEBUG, " selected non-WPA AP " + MACSTR " ssid='%s'", + MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, + bss->ssid_len)); + break; + } + } + } + + return selected; +} + + +static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s) +{ + int num, prio; + struct wpa_scan_result *selected = NULL; + struct wpa_ssid *ssid = NULL; + struct wpa_scan_result *results; + + if (wpa_supplicant_get_scan_results(wpa_s) < 0) { + if (wpa_s->conf->ap_scan == 2) + return; + wpa_printf(MSG_DEBUG, "Failed to get scan results - try " + "scanning again"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + return; + } + if (wpa_s->conf->ap_scan == 2) + return; + results = wpa_s->scan_results; + num = wpa_s->num_scan_results; + + while (selected == NULL) { + for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { + selected = wpa_supplicant_select_bss( + wpa_s, wpa_s->conf->pssid[prio], results, num, + &ssid); + if (selected) + break; + } + + if (selected == NULL && wpa_s->blacklist) { + wpa_printf(MSG_DEBUG, "No APs found - clear blacklist " + "and try again"); + wpa_blacklist_clear(wpa_s); + } else if (selected == NULL) { + break; + } + } + + if (selected) { + if (wpa_s->reassociate || + memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0) { + if (wpa_supplicant_scard_init(wpa_s, ssid)) { + wpa_supplicant_req_scan(wpa_s, 10, 0); + return; + } + wpa_supplicant_associate(wpa_s, selected, ssid); + } else { + wpa_printf(MSG_DEBUG, "Already associated with the " + "selected AP."); + } + rsn_preauth_scan_results(wpa_s->wpa, results, num); + } else { + wpa_printf(MSG_DEBUG, "No suitable AP found."); + wpa_supplicant_req_scan(wpa_s, 5, 0); + } +} + + +static void wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int l, len, found = 0, wpa_found, rsn_found; + u8 *p; + + wpa_printf(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); + if (data->assoc_info.beacon_ies) + wpa_hexdump(MSG_DEBUG, "beacon_ies", + data->assoc_info.beacon_ies, + data->assoc_info.beacon_ies_len); + + 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] == GENERIC_INFO_ELEM && p[1] >= 6 && + (memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == RSN_INFO_ELEM && 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); + + /* 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] == GENERIC_INFO_ELEM && p[1] >= 6 && + 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] == RSN_INFO_ELEM && 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); +} + + +static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + u8 bssid[ETH_ALEN]; + + if (data) + wpa_supplicant_event_associnfo(wpa_s, data); + + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); + if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && + memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" + MACSTR, MAC2STR(bssid)); + memcpy(wpa_s->bssid, bssid, ETH_ALEN); + if (wpa_supplicant_dynamic_keys(wpa_s)) { + wpa_clear_keys(wpa_s, bssid); + } + if (wpa_supplicant_select_config(wpa_s) < 0) { + wpa_supplicant_disassociate(wpa_s, + REASON_DEAUTH_LEAVING); + return; + } + } + + 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); + 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. + */ + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) + 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_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + } else { + /* Timeout for receiving the first EAPOL packet */ + wpa_supplicant_req_auth_timeout(wpa_s, 10, 0); + } +} + + +static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s) +{ + 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_printf(MSG_DEBUG, "Disconnect event - ignore in " + "IBSS/WPA-None mode"); + return; + } + + if (wpa_s->wpa_state == WPA_4WAY_HANDSHAKE && + wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) { + wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " + "pre-shared key may be incorrect"); + } + if (wpa_s->wpa_state >= WPA_ASSOCIATED) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + wpa_blacklist_add(wpa_s, wpa_s->bssid); + wpa_sm_notify_disassoc(wpa_s->wpa); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "- Disconnect event - " + "remove keys"); + if (wpa_supplicant_dynamic_keys(wpa_s)) { + wpa_s->keys_cleared = 0; + wpa_clear_keys(wpa_s, wpa_s->bssid); + } +} + + +static void +wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int pairwise; + time_t now; + + wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); + pairwise = (data && data->michael_mic_failure.unicast); + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); + time(&now); + if (wpa_s->last_michael_mic_error && + now - wpa_s->last_michael_mic_error <= 60) { + /* 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. */ + usleep(10000); + + wpa_drv_set_countermeasures(wpa_s, 1); + wpa_supplicant_deauthenticate(wpa_s, + 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.. */ + } + wpa_s->last_michael_mic_error = now; +} + + +static void +wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (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_printf(MSG_DEBUG, "Configured interface was added."); + if (wpa_supplicant_driver_init(wpa_s, 1) < 0) { + wpa_printf(MSG_INFO, "Failed to initialize the driver " + "after interface was added."); + } + break; + case EVENT_INTERFACE_REMOVED: + wpa_printf(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; + break; + } +} + + +void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, + union wpa_event_data *data) +{ + switch (event) { + case EVENT_ASSOC: + wpa_supplicant_event_assoc(wpa_s, data); + break; + case EVENT_DISASSOC: + wpa_supplicant_event_disassoc(wpa_s); + break; + case EVENT_MICHAEL_MIC_FAILURE: + wpa_supplicant_event_michael_mic_failure(wpa_s, data); + break; + case EVENT_SCAN_RESULTS: + wpa_supplicant_event_scan_results(wpa_s); + break; + 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; + default: + wpa_printf(MSG_INFO, "Unknown event %d", event); + break; + } +} diff --git a/contrib/wpa_supplicant/examples/ieee8021x.conf b/contrib/wpa_supplicant/examples/ieee8021x.conf new file mode 100644 index 0000000..e8a5503 --- /dev/null +++ b/contrib/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/contrib/wpa_supplicant/examples/plaintext.conf b/contrib/wpa_supplicant/examples/plaintext.conf new file mode 100644 index 0000000..542ac1d --- /dev/null +++ b/contrib/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/contrib/wpa_supplicant/examples/wep.conf b/contrib/wpa_supplicant/examples/wep.conf new file mode 100644 index 0000000..9c7b55f --- /dev/null +++ b/contrib/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/contrib/wpa_supplicant/examples/wpa-psk-tkip.conf b/contrib/wpa_supplicant/examples/wpa-psk-tkip.conf new file mode 100644 index 0000000..93d7fc2 --- /dev/null +++ b/contrib/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/contrib/wpa_supplicant/examples/wpa2-eap-ccmp.conf b/contrib/wpa_supplicant/examples/wpa2-eap-ccmp.conf new file mode 100644 index 0000000..d7a64d8 --- /dev/null +++ b/contrib/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/contrib/wpa_supplicant/hostapd.h b/contrib/wpa_supplicant/hostapd.h new file mode 100644 index 0000000..ce1fae9 --- /dev/null +++ b/contrib/wpa_supplicant/hostapd.h @@ -0,0 +1,38 @@ +#ifndef HOSTAPD_H +#define HOSTAPD_H + +/* + * Minimal version of hostapd header files for eapol_test to build + * radiusclient.c. + */ + +#include "common.h" + +void hostapd_logger(void *ctx, u8 *addr, unsigned int module, int level, + char *fmt, ...) __attribute__ ((format (printf, 5, 6))); + +struct hostapd_ip_addr; + +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen);; + +enum { + HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, + HOSTAPD_LEVEL_DEBUG = 1, + HOSTAPD_LEVEL_INFO = 2, + HOSTAPD_LEVEL_NOTICE = 3, + HOSTAPD_LEVEL_WARNING = 4 +}; + +#ifndef BIT +#define BIT(n) (1 << (n)) +#endif + +#define HOSTAPD_MODULE_IEEE80211 BIT(0) +#define HOSTAPD_MODULE_IEEE8021X BIT(1) +#define HOSTAPD_MODULE_RADIUS BIT(2) +#define HOSTAPD_MODULE_WPA BIT(3) +#define HOSTAPD_MODULE_DRIVER BIT(4) +#define HOSTAPD_MODULE_IAPP BIT(5) + +#endif /* HOSTAPD_H */ diff --git a/contrib/wpa_supplicant/l2_packet.h b/contrib/wpa_supplicant/l2_packet.h index 3e3914c..eb966d3 100644 --- a/contrib/wpa_supplicant/l2_packet.h +++ b/contrib/wpa_supplicant/l2_packet.h @@ -1,3 +1,23 @@ +/* + * WPA Supplicant - Layer2 packet interface definition + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 defines an interface for layer 2 (link layer) packet sending and + * receiving. l2_packet_linux.c is one implementation for such a layer 2 + * implementation using Linux packet sockets and l2_packet_pcap.c another one + * using libpcap and libdnet. When porting %wpa_supplicant to other operating + * systems, a new l2_packet implementation may need to be added. + */ + #ifndef L2_PACKET_H #define L2_PACKET_H @@ -12,6 +32,14 @@ #define ETH_P_RSN_PREAUTH 0x88c7 #endif +/** + * struct l2_packet_data - Internal l2_packet data structure + * + * This structure is used by the l2_packet implementation to store its private + * data. Other files use a pointer to this data when calling the l2_packet + * functions, but the contents of this structure should not be used directly + * outside l2_packet implementation. + */ struct l2_packet_data; struct l2_ethhdr { @@ -20,15 +48,86 @@ struct l2_ethhdr { u16 h_proto; } __attribute__ ((packed)); +/** + * l2_packet_init - Initialize l2_packet interface + * @ifname: Interface name + * @own_addr: Optional own MAC address if available from driver interface or + * %NULL if not available + * @protocol: Ethernet protocol number in host byte order + * @rx_callback: Callback function that will be called for each received packet + * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback() + * @l2_hdr: 1 = include layer 2 header, 0 = do not include header + * Returns: Pointer to internal data or %NULL on failure + * + * rx_callback function will be called with src_addr pointing to the source + * address (MAC address) of the the packet. If l2_hdr is set to 0, buf + * points to len bytes of the payload after the layer 2 header and similarly, + * TX buffers start with payload. This behavior can be changed by setting + * l2_hdr=1 to include the layer 2 header in the data buffer. + */ struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, - void (*rx_callback)(void *ctx, unsigned char *src_addr, - unsigned char *buf, size_t len), - void *rx_callback_ctx); + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr); + +/** + * l2_packet_deinit - Deinitialize l2_packet interface + * @l2: Pointer to internal l2_packet data from l2_packet_init() + */ void l2_packet_deinit(struct l2_packet_data *l2); +/** + * l2_packet_get_own_addr - Get own layer 2 address + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @addr: Buffer for the own address (6 bytes) + * Returns: 0 on success, -1 on failure + */ int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr); -int l2_packet_send(struct l2_packet_data *l2, u8 *buf, size_t len); -void l2_packet_set_rx_l2_hdr(struct l2_packet_data *l2, int rx_l2_hdr); + +/** + * l2_packet_send - Send a packet + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @dst_addr: Destination address for the packet (only used if l2_hdr == 0) + * @proto: Protocol/ethertype for the packet in host byte order (only used if + * l2_hdr == 0) + * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was + * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet + * is included. + * @len: Length of the buffer (including l2 header only if l2_hdr == 1) + * Returns: >=0 on success, <0 on failure + */ +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len); + +/** + * l2_packet_get_ip_addr - Get the current IP address from the interface + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @buf: Buffer for the IP address in text format + * @len: Maximum buffer length + * Returns: 0 on success, -1 on failure + * + * This function can be used to get the current IP address from the interface + * bound to the l2_packet. This is mainly for status information and the IP + * address will be stored as an ASCII string. This function is not essential + * for %wpa_supplicant operation, so full implementation is not required. + * l2_packet implementation will need to define the function, but it can return + * -1 if the IP address information is not available. + */ +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len); + + +/** + * l2_packet_notify_auth_start - Notify l2_packet about start of authentication + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * + * This function is called when authentication is expected to start, e.g., when + * association has been completed, in order to prepare l2_packet implementation + * for EAPOL frames. This function is used mainly if the l2_packet code needs + * to do polling in which case it can increasing polling frequency. This can + * also be an empty function if the l2_packet implementation does not benefit + * from knowing about the starting authentication. + */ +void l2_packet_notify_auth_start(struct l2_packet_data *l2); #endif /* L2_PACKET_H */ diff --git a/contrib/wpa_supplicant/main.c b/contrib/wpa_supplicant/main.c new file mode 100644 index 0000000..8d82666 --- /dev/null +++ b/contrib/wpa_supplicant/main.c @@ -0,0 +1,250 @@ +/* + * WPA Supplicant / main() function for UNIX like OSes and MinGW + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> + +#include "common.h" +#include "wpa_supplicant_i.h" + + +extern const char *wpa_supplicant_version; +extern const char *wpa_supplicant_license; +#ifndef CONFIG_NO_STDOUT_DEBUG +extern const char *wpa_supplicant_full_license; +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +extern struct wpa_driver_ops *wpa_supplicant_drivers[]; + + +static void usage(void) +{ + int i; + printf("%s\n\n%s\n" + "usage:\n" + " wpa_supplicant [-BddehLqqvwW] [-P<pid file>] " + "[-g<global ctrl>] \\\n" + " -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] " + "[-p<driver_param>] \\\n" + " [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] " + "[-p<driver_param>] ...]\n" + "\n" + "drivers:\n", + wpa_supplicant_version, wpa_supplicant_license); + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + printf(" %s = %s\n", + wpa_supplicant_drivers[i]->name, + wpa_supplicant_drivers[i]->desc); + } + +#ifndef CONFIG_NO_STDOUT_DEBUG + printf("options:\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\n" + " -g = global ctrl_interface\n" + " -K = include keys (passwords, etc.) in debug output\n" + " -t = include timestamp in debug messages\n" + " -h = show this help text\n" + " -L = show license (GPL and BSD)\n" + " -p = driver parameters\n" + " -P = PID file\n" + " -q = decrease debugging verbosity (-qq even less)\n" + " -v = show version\n" + " -w = wait for interface to be added, if needed\n" + " -W = wait for a control interface monitor before starting\n" + " -N = start describing new interface\n"); + + printf("example:\n" + " wpa_supplicant -Dwext -iwlan0 -c/etc/wpa_supplicant.conf\n"); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static void license(void) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + printf("%s\n\n%s\n", + wpa_supplicant_version, wpa_supplicant_full_license); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static void wpa_supplicant_fd_workaround(void) +{ + 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; + } + } +} + + +int main(int argc, char *argv[]) +{ + int c, i; + struct wpa_interface *ifaces, *iface; + int iface_count, exitcode; + struct wpa_params params; + struct wpa_global *global; + +#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 */ + + memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + iface = ifaces = malloc(sizeof(struct wpa_interface)); + if (ifaces == NULL) + return -1; + memset(iface, 0, sizeof(*iface)); + iface_count = 1; + + wpa_supplicant_fd_workaround(); + + for (;;) { + c = getopt(argc, argv, "Bc:C:D:dg:hi:KLNp:P:qtvwW"); + if (c < 0) + break; + switch (c) { + 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"); + return -1; +#else /* CONFIG_NO_STDOUT_DEBUG */ + params.wpa_debug_level--; + break; +#endif /* CONFIG_NO_STDOUT_DEBUG */ + case 'g': + params.ctrl_interface = optarg; + break; + case 'h': + usage(); + return -1; + case 'i': + iface->ifname = optarg; + break; + case 'K': + params.wpa_debug_show_keys++; + break; + case 'L': + license(); + return -1; + case 'p': + iface->driver_param = optarg; + break; + case 'P': + params.pid_file = rel2abs_path(optarg); + break; + case 'q': + params.wpa_debug_level++; + break; + case 't': + params.wpa_debug_timestamp++; + break; + case 'v': + printf("%s\n", wpa_supplicant_version); + return -1; + case 'w': + params.wait_for_interface++; + break; + case 'W': + params.wait_for_monitor++; + break; + case 'N': + iface_count++; + iface = realloc(ifaces, iface_count * + sizeof(struct wpa_interface)); + if (iface == NULL) { + free(ifaces); + return -1; + } + ifaces = iface; + iface = &ifaces[iface_count - 1]; + memset(iface, 0, sizeof(*iface)); + break; + default: + usage(); + return -1; + } + } + + 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) + break; + usage(); + return -1; + } + if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + exitcode = -1; + } + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + free(ifaces); + free(params.pid_file); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return exitcode; +} diff --git a/contrib/wpa_supplicant/md5.c b/contrib/wpa_supplicant/md5.c index 1564e8f..82388e0 100644 --- a/contrib/wpa_supplicant/md5.c +++ b/contrib/wpa_supplicant/md5.c @@ -1,6 +1,6 @@ /* * MD5 hash implementation and interface functions - * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -18,36 +18,38 @@ #include "common.h" #include "md5.h" +#include "crypto.h" -void md5_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac) -{ - MD5_CTX context; - MD5Init(&context); - MD5Update(&context, key, key_len); - MD5Update(&context, data, data_len); - MD5Update(&context, key, key_len); - MD5Final(mac, &context); -} - - -/* HMAC code is based on RFC 2104 */ +/** + * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (16 bytes) + */ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - MD5_CTX context; - u8 k_ipad[65]; /* inner padding - key XORd with ipad */ - u8 k_opad[65]; /* outer padding - key XORd with opad */ + u8 k_pad[64]; /* padding - key XORd with ipad/opad */ u8 tk[16]; int i; + const u8 *_addr[6]; + size_t _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } /* if key is longer than 64 bytes reset it to key = MD5(key) */ if (key_len > 64) { - MD5Init(&context); - MD5Update(&context, key, key_len); - MD5Final(tk, &context); - + md5_vector(1, &key, &key_len, tk); key = tk; key_len = 16; } @@ -61,35 +63,46 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ - /* start out by storing key in pads */ - memset(k_ipad, 0, sizeof(k_ipad)); - memset(k_opad, 0, sizeof(k_opad)); - memcpy(k_ipad, key, key_len); - memcpy(k_opad, key, key_len); + /* start out by storing key in ipad */ + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); - /* XOR key with ipad and opad values */ - for (i = 0; i < 64; i++) { - k_ipad[i] ^= 0x36; - k_opad[i] ^= 0x5c; - } + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; /* perform inner MD5 */ - MD5Init(&context); /* init context for 1st pass */ - MD5Update(&context, k_ipad, 64); /* start with inner pad */ - /* then text of datagram; all fragments */ + _addr[0] = k_pad; + _len[0] = 64; for (i = 0; i < num_elem; i++) { - MD5Update(&context, addr[i], len[i]); + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; } - MD5Final(mac, &context); /* finish up 1st pass */ + md5_vector(1 + num_elem, _addr, _len, mac); + + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; /* perform outer MD5 */ - MD5Init(&context); /* init context for 2nd pass */ - MD5Update(&context, k_opad, 64); /* start with outer pad */ - MD5Update(&context, mac, 16); /* then results of 1st hash */ - MD5Final(mac, &context); /* finish up 2nd pass */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = MD5_MAC_LEN; + md5_vector(2, _addr, _len, mac); } +/** + * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (16 bytes) + */ void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { @@ -99,6 +112,40 @@ void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, #ifndef EAP_TLS_FUNCS +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +static void MD5Init(struct MD5Context *context); +static void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +static void MD5Final(unsigned char digest[16], struct MD5Context *context); +static void MD5Transform(u32 buf[4], u32 const in[16]); + +typedef struct MD5Context MD5_CTX; + + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + int i; + + MD5Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5Update(&ctx, addr[i], len[i]); + MD5Final(mac, &ctx); +} + + /* ===== start - public domain MD5 implementation ===== */ /* * This code implements the MD5 message-digest algorithm. @@ -120,13 +167,10 @@ void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, #ifndef WORDS_BIGENDIAN #define byteReverse(buf, len) /* Nothing */ #else -void byteReverse(unsigned char *buf, unsigned longs); - -#ifndef ASM_MD5 /* * Note: this code is harmless on little-endian machines. */ -void byteReverse(unsigned char *buf, unsigned longs) +static void byteReverse(unsigned char *buf, unsigned longs) { u32 t; do { @@ -137,13 +181,12 @@ void byteReverse(unsigned char *buf, unsigned longs) } while (--longs); } #endif -#endif /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ -void MD5Init(struct MD5Context *ctx) +static void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; @@ -158,7 +201,8 @@ void MD5Init(struct MD5Context *ctx) * Update context to reflect the concatenation of another buffer full * of bytes. */ -void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, + unsigned len) { u32 t; @@ -206,7 +250,7 @@ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ -void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned count; unsigned char *p; @@ -247,8 +291,6 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ } -#ifndef ASM_MD5 - /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ @@ -266,7 +308,7 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ -void MD5Transform(u32 buf[4], u32 const in[16]) +static void MD5Transform(u32 buf[4], u32 const in[16]) { register u32 a, b, c, d; @@ -348,8 +390,6 @@ void MD5Transform(u32 buf[4], u32 const in[16]) buf[2] += c; buf[3] += d; } - -#endif /* ===== end - public domain MD5 implementation ===== */ #endif /* !EAP_TLS_FUNCS */ diff --git a/contrib/wpa_supplicant/md5.h b/contrib/wpa_supplicant/md5.h index cc3eb95..a724804 100644 --- a/contrib/wpa_supplicant/md5.h +++ b/contrib/wpa_supplicant/md5.h @@ -1,40 +1,22 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 MD5_H #define MD5_H -#ifdef EAP_TLS_FUNCS - -#include <openssl/md5.h> - -#define MD5Init MD5_Init -#define MD5Update MD5_Update -#define MD5Final MD5_Final -#define MD5Transform MD5_Transform - -#define MD5_MAC_LEN MD5_DIGEST_LENGTH - -#else /* EAP_TLS_FUNCS */ - #define MD5_MAC_LEN 16 -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - u8 in[64]; -}; - -void MD5Init(struct MD5Context *context); -void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); -void MD5Final(unsigned char digest[16], struct MD5Context *context); -void MD5Transform(u32 buf[4], u32 const in[16]); - -typedef struct MD5Context MD5_CTX; - -#endif /* EAP_TLS_FUNCS */ - - -void md5_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac); void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, diff --git a/contrib/wpa_supplicant/ms_funcs.c b/contrib/wpa_supplicant/ms_funcs.c index 590b589..c26cddf 100644 --- a/contrib/wpa_supplicant/ms_funcs.c +++ b/contrib/wpa_supplicant/ms_funcs.c @@ -20,10 +20,19 @@ #include "sha1.h" #include "ms_funcs.h" #include "crypto.h" +#include "rc4.h" -static void challenge_hash(u8 *peer_challenge, u8 *auth_challenge, - u8 *username, size_t username_len, +/** + * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @challenge: 8-octet Challenge (OUT) + */ +static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge) { u8 hash[SHA1_MAC_LEN]; @@ -42,10 +51,18 @@ static void challenge_hash(u8 *peer_challenge, u8 *auth_challenge, } -void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash) +/** + * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (OUT) + */ +void nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash) { u8 *buf; int i; + size_t len; /* Convert password into unicode */ buf = malloc(password_len * 2); @@ -55,18 +72,32 @@ void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash) for (i = 0; i < password_len; i++) buf[2 * i] = password[i]; - md4(buf, password_len * 2, password_hash); + len = password_len * 2; + md4_vector(1, (const u8 **) &buf, &len, password_hash); free(buf); } -void hash_nt_password_hash(u8 *password_hash, u8 *password_hash_hash) +/** + * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 + * @password_hash: 16-octet PasswordHash (IN) + * @password_hash_hash: 16-octet PaswordHashHash (OUT) + */ +void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) { - md4(password_hash, 16, password_hash_hash); + size_t len = 16; + md4_vector(1, &password_hash, &len, password_hash_hash); } -void challenge_response(u8 *challenge, u8 *password_hash, u8 *response) +/** + * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5 + * @challenge: 8-octet Challenge (IN) + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + */ +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) { u8 zpwd[7]; des_encrypt(challenge, password_hash, response); @@ -78,9 +109,19 @@ void challenge_response(u8 *challenge, u8 *password_hash, u8 *response) } -void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge, - u8 *username, size_t username_len, - u8 *password, size_t password_len, +/** + * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_hallenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + */ +void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, u8 *response) { u8 challenge[8]; @@ -93,11 +134,22 @@ void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge, } -void generate_authenticator_response(u8 *password, size_t password_len, - u8 *peer_challenge, - u8 *auth_challenge, - u8 *username, size_t username_len, - u8 *nt_response, u8 *response) +/** + * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 42-octet AuthenticatorResponse (OUT) + */ +void generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) { static const u8 magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, @@ -137,8 +189,15 @@ void generate_authenticator_response(u8 *password, size_t password_len, } -void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len, - u8 *response) +/** + * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 + * @challenge: 8-octet Challenge (IN) + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + */ +void nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response) { u8 password_hash[16]; nt_password_hash(password, password_len, password_hash); @@ -146,8 +205,12 @@ void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len, } -/* IN: 16-octet password_hash_hash and 24-octet nt_response - * OUT: 16-octet master_key */ +/** + * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4 + * @password_hash_hash: 16-octet PasswordHashHash (IN) + * @nt_response: 24-octet NTResponse (IN) + * @master_key: 16-octet MasterKey (OUT) + */ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, u8 *master_key) { @@ -169,6 +232,14 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, } +/** + * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 + * @master_key: 16-octet MasterKey (IN) + * @session_key: 8-to-16 octet SessionKey (OUT) + * @session_key_len: SessionKeyLength (Length of session_key) + * @is_send: IsSend (IN, BOOLEAN) + * @is_server: IsServer (IN, BOOLEAN) + */ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, size_t session_key_len, int is_send, int is_server) @@ -229,7 +300,98 @@ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, } +#define PWBLOCK_LEN 516 + +/** + * encrypt_pw_block_with_password_hash - EncryptPwBlobkWithPasswordHash() - RFC 2759, Sect. 8.10 + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (IN) + * @pw_block: 516-byte PwBlock (OUT) + */ +static void encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block) +{ + size_t i, offset; + u8 *pos; + + if (password_len > 256) + return; + + memset(pw_block, 0, PWBLOCK_LEN); + offset = (256 - password_len) * 2; + for (i = 0; i < password_len; i++) + pw_block[offset + i * 2] = password[i]; + pos = &pw_block[2 * 256]; + WPA_PUT_LE16(pos, password_len * 2); + rc4(pw_block, PWBLOCK_LEN, password_hash, 16); +} + + +/** + * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 + * @new_password: 0-to-256-unicode-char NewPassword (IN) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN) + * @old_password_len: Length of old_password + * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) + */ +void new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block) +{ + u8 password_hash[16]; + + nt_password_hash(old_password, old_password_len, password_hash); + encrypt_pw_block_with_password_hash(new_password, new_password_len, + password_hash, encrypted_pw_block); +} + + +/** + * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 + * @password_hash: 16-octer PasswordHash (IN) + * @block: 16-octet Block (IN) + * @cypher: 16-octer Cypher (OUT) + */ +static void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, + u8 *cypher) +{ + des_encrypt(password_hash, block, cypher); + des_encrypt(password_hash + 8, block + 7, cypher + 8); +} + + +/** + * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 + * @new_password: 0-to-256-unicode-char NewPassword (IN) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN) + * @old_password_len: Length of old_password + * @encrypted_password_ash: 16-octet EncryptedPasswordHash (OUT) + */ +void old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash) +{ + u8 old_password_hash[16], new_password_hash[16]; + + nt_password_hash(old_password, old_password_len, old_password_hash); + nt_password_hash(new_password, new_password_len, new_password_hash); + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash); +} + + #ifdef TEST_MAIN_MS_FUNCS + +#include "rc4.c" + int main(int argc, char *argv[]) { /* Test vector from RFC2759 example */ diff --git a/contrib/wpa_supplicant/ms_funcs.h b/contrib/wpa_supplicant/ms_funcs.h index a08ab06..38d1bd6 100644 --- a/contrib/wpa_supplicant/ms_funcs.h +++ b/contrib/wpa_supplicant/ms_funcs.h @@ -1,25 +1,50 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 MS_FUNCS_H #define MS_FUNCS_H -void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge, - u8 *username, size_t username_len, - u8 *password, size_t password_len, +void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, u8 *response); -void generate_authenticator_response(u8 *password, size_t password_len, - u8 *peer_challenge, - u8 *auth_challenge, - u8 *username, size_t username_len, - u8 *nt_response, u8 *response); -void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len, - u8 *response); +void generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +void nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response); -void challenge_response(u8 *challenge, u8 *password_hash, u8 *response); -void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash); -void hash_nt_password_hash(u8 *password_hash, u8 *password_hash_hash); +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); +void nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash); +void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, u8 *master_key); void get_asymetric_start_key(const u8 *master_key, u8 *session_key, size_t session_key_len, int is_send, int is_server); +void new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block); +void old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash); + #endif /* MS_FUNCS_H */ diff --git a/contrib/wpa_supplicant/openssl-tls-extensions.patch b/contrib/wpa_supplicant/openssl-tls-extensions.patch index 77e9a41..44490cc 100644 --- a/contrib/wpa_supplicant/openssl-tls-extensions.patch +++ b/contrib/wpa_supplicant/openssl-tls-extensions.patch @@ -1,166 +1,429 @@ -This is a quick hack for testing EAP-FAST with openssl. +This patch is adding support for TLS hello extensions and externally +generated pre-shared key material to OpenSSL 0.9.8. This is +based on the patch from Alexey Kobozev <akobozev@cisco.com> +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). -Addition of TLS extensions to ClientHello/ServerHello is more or less -ok, though not very clean in the way that the caller needs to take -care of constructing set of all extensions. In addition there is not -mechanism for reading the TLS extensions, i.e., this would not be -enough for EAP-FAST authenticator. -Rest of the changes are obviously ugly and/or incorrect for most -parts, but it demonstrates the minimum set of changes to skip some of -the error cases that prevented completion of TLS handshake without -certificates. In other words, this is just a proof-of-concept type of -example to make it possible to experiment with EAP-FAST. Cleaner patch -for the needed functionality would be welcome.. - -diff -upr openssl-0.9.7e.orig/include/openssl/ssl.h openssl-0.9.7e/include/openssl/ssl.h ---- openssl-0.9.7e.orig/include/openssl/ssl.h 2004-07-27 11:28:49.000000000 -0700 -+++ openssl-0.9.7e/include/openssl/ssl.h 2004-12-24 20:29:01.000000000 -0800 -@@ -929,6 +929,11 @@ struct ssl_st +diff -uprN openssl-0.9.8.orig/include/openssl/ssl.h openssl-0.9.8/include/openssl/ssl.h +--- openssl-0.9.8.orig/include/openssl/ssl.h 2005-06-10 12:51:16.000000000 -0700 ++++ openssl-0.9.8/include/openssl/ssl.h 2005-07-19 20:02:15.000000000 -0700 +@@ -340,6 +340,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -361,6 +362,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -968,6 +971,15 @@ struct ssl_st int first_packet; int client_version; /* what was passed, used for * SSLv3/TLS rollback check */ + -+ /* Optional ClientHello/ServerHello extension to be added to the end -+ * of the SSLv3/TLS hello message. */ -+ char *hello_extension; -+ int hello_extension_len; ++ /* TLS externsions */ ++ TLS_EXTENSION *tls_extension; ++ int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); ++ void *tls_extension_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; }; #ifdef __cplusplus -diff -upr openssl-0.9.7e.orig/ssl/s3_both.c openssl-0.9.7e/ssl/s3_both.c ---- openssl-0.9.7e.orig/ssl/s3_both.c 2003-02-12 09:05:17.000000000 -0800 -+++ openssl-0.9.7e/ssl/s3_both.c 2004-12-31 21:18:15.556846272 -0800 -@@ -199,6 +199,12 @@ int ssl3_get_finished(SSL *s, int a, int - 64, /* should actually be 36+4 :-) */ - &ok); - -+ if (!ok && s->hello_extension) -+ { -+ /* Quick hack to test EAP-FAST. */ -+ return(1); -+ } -+ - if (!ok) return((int)n); +@@ -1533,6 +1545,13 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif - /* If this occurs, we have missed a message */ -diff -upr openssl-0.9.7e.orig/ssl/s3_clnt.c openssl-0.9.7e/ssl/s3_clnt.c ---- openssl-0.9.7e.orig/ssl/s3_clnt.c 2004-05-15 09:39:22.000000000 -0700 -+++ openssl-0.9.7e/ssl/s3_clnt.c 2004-12-31 21:16:38.617583280 -0800 -@@ -588,6 +588,12 @@ static int ssl3_client_hello(SSL *s) - *(p++)=comp->id; - } - *(p++)=0; /* Add the NULL method */ ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); + -+ if (s->hello_extension) -+ { -+ memcpy(p,s->hello_extension,s->hello_extension_len); -+ p+=s->hello_extension_len; -+ } - - l=(p-d); - d=buf; -@@ -779,6 +785,11 @@ static int ssl3_get_server_certificate(S ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1714,6 +1733,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 - if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE) - { -+ if (s->hello_extension) -+ { -+ /* Quick hack to test EAP-FAST. */ -+ return(1); -+ } - al=SSL_AD_UNEXPECTED_MESSAGE; - SSLerr(SSL_F_SSL3_GET_SERVER_CERTIFICATE,SSL_R_BAD_MESSAGE_TYPE); - goto f_err; -@@ -951,6 +962,12 @@ static int ssl3_get_key_exchange(SSL *s) - DH *dh=NULL; + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -uprN openssl-0.9.8.orig/include/openssl/tls1.h openssl-0.9.8/include/openssl/tls1.h +--- openssl-0.9.8.orig/include/openssl/tls1.h 2003-07-22 05:34:21.000000000 -0700 ++++ openssl-0.9.8/include/openssl/tls1.h 2005-07-19 20:02:15.000000000 -0700 +@@ -282,6 +282,14 @@ extern "C" { + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ #endif -+ if (s->hello_extension) -+ { -+ /* Quick hack to test EAP-FAST. */ -+ return(1); -+ } ++/* TLS extension struct */ ++struct tls_extension_st ++{ ++ unsigned short type; ++ unsigned short length; ++ void *data; ++}; + - /* use same message size as in ssl3_get_certificate_request() - * as ServerKeyExchange message may be skipped */ - n=ssl3_get_message(s, -@@ -1264,6 +1281,12 @@ static int ssl3_get_certificate_request( - unsigned char *p,*d,*q; - STACK_OF(X509_NAME) *ca_sk=NULL; - -+ if (s->hello_extension) + #ifdef __cplusplus + } + #endif +diff -uprN openssl-0.9.8.orig/ssl/Makefile openssl-0.9.8/ssl/Makefile +--- openssl-0.9.8.orig/ssl/Makefile 2005-05-30 16:20:30.000000000 -0700 ++++ openssl-0.9.8/ssl/Makefile 2005-07-19 20:02:15.000000000 -0700 +@@ -24,7 +24,7 @@ LIBSRC= \ + s2_meth.c s2_srvr.c s2_clnt.c s2_lib.c s2_enc.c s2_pkt.c \ + s3_meth.c s3_srvr.c s3_clnt.c s3_lib.c s3_enc.c s3_pkt.c s3_both.c \ + s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c s23_pkt.c \ +- t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c \ ++ t1_meth.c t1_srvr.c t1_clnt.c t1_lib.c t1_enc.c t1_ext.c \ + d1_meth.c d1_srvr.c d1_clnt.c d1_lib.c d1_pkt.c \ + d1_both.c d1_enc.c \ + ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \ +@@ -35,7 +35,7 @@ LIBOBJ= \ + s2_meth.o s2_srvr.o s2_clnt.o s2_lib.o s2_enc.o s2_pkt.o \ + s3_meth.o s3_srvr.o s3_clnt.o s3_lib.o s3_enc.o s3_pkt.o s3_both.o \ + s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o s23_pkt.o \ +- t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o \ ++ t1_meth.o t1_srvr.o t1_clnt.o t1_lib.o t1_enc.o t1_ext.o \ + d1_meth.o d1_srvr.o d1_clnt.o d1_lib.o d1_pkt.o \ + d1_both.o d1_enc.o \ + ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \ +@@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h .. + t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h + t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h + t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c ++t1_ext.o: t1_ext.c ssl_locl.h +diff -uprN openssl-0.9.8.orig/ssl/s3_clnt.c openssl-0.9.8/ssl/s3_clnt.c +--- openssl-0.9.8.orig/ssl/s3_clnt.c 2005-05-16 03:11:03.000000000 -0700 ++++ openssl-0.9.8/ssl/s3_clnt.c 2005-07-19 20:02:15.000000000 -0700 +@@ -606,6 +606,20 @@ int ssl3_client_hello(SSL *s) + } + *(p++)=0; /* Add the NULL method */ + ++ /* send client hello extensions if any */ ++ if (s->version >= TLS1_VERSION && s->tls_extension) + { -+ /* Quick hack to test EAP-FAST. */ -+ return(1); ++ // set the total extensions length ++ s2n(s->tls_extension->length + 4, p); ++ ++ // put the extensions with type and length ++ s2n(s->tls_extension->type, p); ++ s2n(s->tls_extension->length, p); ++ ++ memcpy(p, s->tls_extension->data, s->tls_extension->length); ++ p+=s->tls_extension->length; + } + - n=ssl3_get_message(s, - SSL3_ST_CR_CERT_REQ_A, - SSL3_ST_CR_CERT_REQ_B, -@@ -1407,6 +1430,12 @@ static int ssl3_get_server_done(SSL *s) - int ok,ret=0; + l=(p-d); + d=buf; + *(d++)=SSL3_MT_CLIENT_HELLO; +@@ -628,7 +642,7 @@ int ssl3_get_server_hello(SSL *s) + STACK_OF(SSL_CIPHER) *sk; + SSL_CIPHER *c; + unsigned char *p,*d; +- int i,al,ok; ++ int i,al,ok,pre_shared; + unsigned int j; long n; + SSL_COMP *comp; +@@ -693,7 +707,24 @@ int ssl3_get_server_hello(SSL *s) + goto f_err; + } -+ if (s->hello_extension) +- if (j != 0 && j == s->session->session_id_length ++ /* check if we want to resume the session based on external pre-shared secret */ ++ pre_shared = 0; ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) + { -+ /* Quick hack to test EAP-FAST. */ -+ return(1); ++ s->hit=1; ++ s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j); ++ s->session->session_id_length = j; ++ memcpy(s->session->session_id, p, j); ++ pre_shared = 1; + } ++ } + - n=ssl3_get_message(s, - SSL3_ST_CR_SRVR_DONE_A, - SSL3_ST_CR_SRVR_DONE_B, -@@ -1439,6 +1468,12 @@ static int ssl3_send_client_key_exchange - KSSL_ERR kssl_err; - #endif /* OPENSSL_NO_KRB5 */ ++ if ((pre_shared || j != 0) && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { + if(s->sid_ctx_length != s->session->sid_ctx_length +diff -uprN openssl-0.9.8.orig/ssl/s3_srvr.c openssl-0.9.8/ssl/s3_srvr.c +--- openssl-0.9.8.orig/ssl/s3_srvr.c 2005-05-22 17:32:55.000000000 -0700 ++++ openssl-0.9.8/ssl/s3_srvr.c 2005-07-19 20:02:15.000000000 -0700 +@@ -955,6 +955,75 @@ int ssl3_get_client_hello(SSL *s) + } + #endif -+ if (s->hello_extension) ++ /* Check for TLS client hello extension here */ ++ if (p < (d+n) && s->version >= TLS1_VERSION) ++ { ++ if (s->tls_extension_cb) + { -+ /* Quick hack to test EAP-FAST. */ -+ return(1); ++ TLS_EXTENSION tls_ext; ++ unsigned short ext_total_len; ++ ++ n2s(p, ext_total_len); ++ n2s(p, tls_ext.type); ++ n2s(p, tls_ext.length); ++ ++ // sanity check in TLS extension len ++ if (tls_ext.length > (d+n) - p) ++ { ++ // just cut the lenth to packet border ++ tls_ext.length = (d+n) - p; ++ } ++ ++ tls_ext.data = p; ++ ++ // returns an alert code or 0 ++ al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg); ++ if (al != 0) ++ { ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR); ++ goto f_err; ++ } + } ++ } + - if (s->state == SSL3_ST_CW_KEY_EXCH_A) - { - d=(unsigned char *)s->init_buf->data; -@@ -1880,6 +1915,12 @@ static int ssl3_check_cert_and_algorithm - DH *dh; - #endif - -+ if (s->hello_extension) ++ /* Check if we want to use external pre-shared secret for this handshake */ ++ /* for not reused session only */ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) ++ { ++ SSL_CIPHER *pref_cipher=NULL; ++ ++ s->session->master_key_length=sizeof(s->session->master_key); ++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, ++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) + { -+ /* Quick hack to test EAP-FAST. */ -+ return(1); ++ s->hit=1; ++ s->session->ciphers=ciphers; ++ s->session->verify_result=X509_V_OK; ++ ++ ciphers=NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) ++ { ++ al=SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher=pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); + } ++ } + - sc=s->session->sess_cert; + /* Given s->session->ciphers and SSL_get_ciphers, we must + * pick a cipher */ - if (sc == NULL) -diff -upr openssl-0.9.7e.orig/ssl/ssl.h openssl-0.9.7e/ssl/ssl.h ---- openssl-0.9.7e.orig/ssl/ssl.h 2004-07-27 11:28:49.000000000 -0700 -+++ openssl-0.9.7e/ssl/ssl.h 2004-12-24 20:29:01.000000000 -0800 -@@ -929,6 +929,11 @@ struct ssl_st +diff -uprN openssl-0.9.8.orig/ssl/ssl_err.c openssl-0.9.8/ssl/ssl_err.c +--- openssl-0.9.8.orig/ssl/ssl_err.c 2005-06-10 12:51:16.000000000 -0700 ++++ openssl-0.9.8/ssl/ssl_err.c 2005-07-19 20:02:15.000000000 -0700 +@@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]= + {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"}, + {0,NULL} + }; + +diff -uprN openssl-0.9.8.orig/ssl/ssl.h openssl-0.9.8/ssl/ssl.h +--- openssl-0.9.8.orig/ssl/ssl.h 2005-06-10 12:51:16.000000000 -0700 ++++ openssl-0.9.8/ssl/ssl.h 2005-07-19 20:02:15.000000000 -0700 +@@ -340,6 +340,7 @@ extern "C" { + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_extension_st TLS_EXTENSION; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st +@@ -361,6 +362,8 @@ DECLARE_STACK_OF(SSL_CIPHER) + typedef struct ssl_st SSL; + typedef struct ssl_ctx_st SSL_CTX; + ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st + { +@@ -968,6 +971,15 @@ struct ssl_st int first_packet; int client_version; /* what was passed, used for * SSLv3/TLS rollback check */ + -+ /* Optional ClientHello/ServerHello extension to be added to the end -+ * of the SSLv3/TLS hello message. */ -+ char *hello_extension; -+ int hello_extension_len; ++ /* TLS externsions */ ++ TLS_EXTENSION *tls_extension; ++ int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg); ++ void *tls_extension_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; }; #ifdef __cplusplus -diff -upr openssl-0.9.7e.orig/ssl/ssl_lib.c openssl-0.9.7e/ssl/ssl_lib.c ---- openssl-0.9.7e.orig/ssl/ssl_lib.c 2004-05-11 05:46:12.000000000 -0700 -+++ openssl-0.9.7e/ssl/ssl_lib.c 2004-12-24 20:35:22.000000000 -0800 -@@ -478,6 +478,7 @@ void SSL_free(SSL *s) - kssl_ctx_free(s->kssl_ctx); - #endif /* OPENSSL_NO_KRB5 */ - -+ OPENSSL_free(s->hello_extension); - OPENSSL_free(s); +@@ -1533,6 +1545,13 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id,void *cm); + #endif + ++/* TLS extensions functions */ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len); ++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); ++ + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. +@@ -1714,6 +1733,7 @@ void ERR_load_SSL_strings(void); + #define SSL_F_TLS1_ENC 210 + #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + #define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_HELLO_EXTENSION 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -uprN openssl-0.9.8.orig/ssl/ssl_sess.c openssl-0.9.8/ssl/ssl_sess.c +--- openssl-0.9.8.orig/ssl/ssl_sess.c 2005-04-29 13:10:06.000000000 -0700 ++++ openssl-0.9.8/ssl/ssl_sess.c 2005-07-19 20:02:15.000000000 -0700 +@@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX * + return(s->session_timeout); } ++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) ++{ ++ if (s == NULL) return(0); ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return(1); ++} ++ + typedef struct timeout_param_st + { + SSL_CTX *ctx; +diff -uprN openssl-0.9.8.orig/ssl/t1_ext.c openssl-0.9.8/ssl/t1_ext.c +--- openssl-0.9.8.orig/ssl/t1_ext.c 1969-12-31 16:00:00.000000000 -0800 ++++ openssl-0.9.8/ssl/t1_ext.c 2005-07-19 20:03:29.000000000 -0700 +@@ -0,0 +1,48 @@ ++ ++#include <stdio.h> ++#include "ssl_locl.h" ++ ++ ++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len) ++{ ++ if(s->version >= TLS1_VERSION) ++ { ++ if(s->tls_extension) ++ { ++ OPENSSL_free(s->tls_extension); ++ s->tls_extension = NULL; ++ } ++ ++ if(ext_data) ++ { ++ s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len); ++ if(!s->tls_extension) ++ { ++ SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE); ++ return 0; ++ } ++ ++ s->tls_extension->type = ext_type; ++ s->tls_extension->length = ext_len; ++ s->tls_extension->data = s->tls_extension + 1; ++ memcpy(s->tls_extension->data, ext_data, ext_len); ++ } ++ ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg) ++{ ++ if(s->version >= TLS1_VERSION) ++ { ++ s->tls_extension_cb = cb; ++ s->tls_extension_cb_arg = arg; ++ ++ return 1; ++ } ++ ++ return 0; ++} +diff -uprN openssl-0.9.8.orig/ssl/t1_lib.c openssl-0.9.8/ssl/t1_lib.c +--- openssl-0.9.8.orig/ssl/t1_lib.c 2005-04-26 09:02:40.000000000 -0700 ++++ openssl-0.9.8/ssl/t1_lib.c 2005-07-19 20:02:15.000000000 -0700 +@@ -131,6 +131,10 @@ int tls1_new(SSL *s) + + void tls1_free(SSL *s) + { ++ if(s->tls_extension) ++ { ++ OPENSSL_free(s->tls_extension); ++ } + ssl3_free(s); + } + +diff -uprN openssl-0.9.8.orig/ssl/tls1.h openssl-0.9.8/ssl/tls1.h +--- openssl-0.9.8.orig/ssl/tls1.h 2003-07-22 05:34:21.000000000 -0700 ++++ openssl-0.9.8/ssl/tls1.h 2005-07-19 20:02:15.000000000 -0700 +@@ -282,6 +282,14 @@ extern "C" { + #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ + #endif + ++/* TLS extension struct */ ++struct tls_extension_st ++{ ++ unsigned short type; ++ unsigned short length; ++ void *data; ++}; ++ + #ifdef __cplusplus + } + #endif +diff -uprN openssl-0.9.8.orig/util/ssleay.num openssl-0.9.8/util/ssleay.num +--- openssl-0.9.8.orig/util/ssleay.num 2005-05-08 17:22:02.000000000 -0700 ++++ openssl-0.9.8/util/ssleay.num 2005-07-19 20:02:15.000000000 -0700 +@@ -226,3 +226,6 @@ DTLSv1_server_method + SSL_COMP_get_compression_methods 276 EXIST:!VMS:FUNCTION:COMP + SSL_COMP_get_compress_methods 276 EXIST:VMS:FUNCTION:COMP + SSL_SESSION_get_id 277 EXIST::FUNCTION: ++SSL_set_hello_extension 278 EXIST::FUNCTION: ++SSL_set_hello_extension_cb 279 EXIST::FUNCTION: ++SSL_set_session_secret_cb 280 EXIST::FUNCTION: diff --git a/contrib/wpa_supplicant/pcsc_funcs.c b/contrib/wpa_supplicant/pcsc_funcs.c index 541661f..2ccfd00 100644 --- a/contrib/wpa_supplicant/pcsc_funcs.c +++ b/contrib/wpa_supplicant/pcsc_funcs.c @@ -86,8 +86,8 @@ struct scard_data { long ctx; long card; unsigned long protocol; - SCARD_IO_REQUEST recv_pci; sim_types sim_type; + int pin1_required; }; @@ -96,7 +96,7 @@ static int _scard_select_file(struct scard_data *scard, unsigned short file_id, sim_types sim_type, unsigned char *aid); static int scard_select_file(struct scard_data *scard, unsigned short file_id, unsigned char *buf, size_t *buf_len); -static int scard_verify_pin(struct scard_data *scard, char *pin); +static int scard_verify_pin(struct scard_data *scard, const char *pin); static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, @@ -183,7 +183,7 @@ static int scard_pin_needed(struct scard_data *scard, } -struct scard_data * scard_init(scard_sim_type sim_type, char *pin) +struct scard_data * scard_init(scard_sim_type sim_type) { long ret, len; struct scard_data *scard; @@ -294,28 +294,39 @@ struct scard_data * scard_init(scard_sim_type sim_type, char *pin) /* Verify whether CHV1 (PIN1) is needed to access the card. */ if (scard_pin_needed(scard, buf, blen)) { + scard->pin1_required = 1; wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access"); + } + + return scard; + +failed: + free(readers); + scard_deinit(scard); + return NULL; +} + + +int scard_set_pin(struct scard_data *scard, const char *pin) +{ + if (scard == NULL) + return -1; + + /* Verify whether CHV1 (PIN1) is needed to access the card. */ + if (scard->pin1_required) { if (pin == NULL) { - wpa_printf(MSG_INFO, "No PIN configured for SIM " + wpa_printf(MSG_DEBUG, "No PIN configured for SIM " "access"); - /* TODO: ask PIN from user through a frontend (e.g., - * wpa_cli) */ - goto failed; + return -1; } if (scard_verify_pin(scard, pin)) { wpa_printf(MSG_INFO, "PIN verification failed for " "SIM access"); - /* TODO: what to do? */ - goto failed; + return -1; } } - return scard; - -failed: - free(readers); - scard_deinit(scard); - return NULL; + return 0; } @@ -360,7 +371,7 @@ static long scard_transmit(struct scard_data *scard, scard->protocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0, send, (unsigned long) send_len, - &scard->recv_pci, recv, &rlen); + NULL, recv, &rlen); *recv_len = rlen; if (ret == SCARD_S_SUCCESS) { wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv", @@ -498,7 +509,7 @@ static int scard_read_file(struct scard_data *scard, } -static int scard_verify_pin(struct scard_data *scard, char *pin) +static int scard_verify_pin(struct scard_data *scard, const char *pin) { long ret; unsigned char resp[3]; @@ -621,7 +632,7 @@ int scard_gsm_auth(struct scard_data *scard, unsigned char *rand, memcpy(cmd + 6, rand, 16); } len = sizeof(resp); - ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + ret = scard_transmit(scard, cmd, cmdlen, resp, &len); if (ret != SCARD_S_SUCCESS) return -2; diff --git a/contrib/wpa_supplicant/pcsc_funcs.h b/contrib/wpa_supplicant/pcsc_funcs.h index 1b64fa2..47ac66c 100644 --- a/contrib/wpa_supplicant/pcsc_funcs.h +++ b/contrib/wpa_supplicant/pcsc_funcs.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 PCSC_FUNCS_H #define PCSC_FUNCS_H @@ -27,9 +41,10 @@ typedef enum { #ifdef PCSC_FUNCS -struct scard_data * scard_init(scard_sim_type sim_type, char *pin); +struct scard_data * scard_init(scard_sim_type sim_type); void scard_deinit(struct scard_data *scard); +int scard_set_pin(struct scard_data *scard, const char *pin); int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len); int scard_gsm_auth(struct scard_data *scard, unsigned char *rand, unsigned char *sres, unsigned char *kc); @@ -39,8 +54,9 @@ int scard_umts_auth(struct scard_data *scard, unsigned char *rand, #else /* PCSC_FUNCS */ -#define scard_init(s, p) NULL +#define scard_init(s) NULL #define scard_deinit(s) do { } while (0) +#define scard_set_pin(s, p) -1 #define scard_get_imsi(s, i, l) -1 #define scard_gsm_auth(s, r, s2, k) -1 #define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1 diff --git a/contrib/wpa_supplicant/preauth.c b/contrib/wpa_supplicant/preauth.c new file mode 100644 index 0000000..74a4b0c --- /dev/null +++ b/contrib/wpa_supplicant/preauth.c @@ -0,0 +1,934 @@ +/* + * WPA Supplicant - RSN pre-authentication and PMKSA caching + * Copyright (c) 2003-2006, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <sys/time.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <netinet/in.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <string.h> +#include <time.h> + +#include "common.h" +#include "sha1.h" +#include "wpa.h" +#include "driver.h" +#include "eloop.h" +#include "wpa_supplicant.h" +#include "config.h" +#include "l2_packet.h" +#include "eapol_sm.h" +#include "preauth.h" +#include "wpa_i.h" + + +#define PMKID_CANDIDATE_PRIO_SCAN 1000 +static const int pmksa_cache_max_entries = 32; + + +struct rsn_pmksa_candidate { + struct rsn_pmksa_candidate *next; + u8 bssid[ETH_ALEN]; + int priority; +}; + + +/** + * rsn_pmkid - Calculate PMK identifier + * @pmk: Pairwise master key + * @aa: Authenticator address + * @spa: Supplicant address + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + */ +static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid) +{ + char *title = "PMK Name"; + const unsigned char *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA1_MAC_LEN]; + + addr[0] = (unsigned char *) title; + addr[1] = aa; + addr[2] = spa; + + hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash); + memcpy(pmkid, hash, PMKID_LEN); +} + + +static void pmksa_cache_set_expiration(struct wpa_sm *sm); + + +static void pmksa_cache_free_entry(struct wpa_sm *sm, + struct rsn_pmksa_cache *entry, int replace) +{ + int current; + + current = sm->cur_pmksa == entry || + (sm->pmk_len == entry->pmk_len && + memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0); + + free(entry); + sm->pmksa_count--; + + if (current) { + wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry"); + sm->cur_pmksa = NULL; + + if (replace) { + /* A new entry is being added, so no need to + * deauthenticate in this case. This happens when EAP + * authentication is completed again (reauth or failed + * PMKSA caching attempt). */ + return; + } + + memset(sm->pmk, 0, sizeof(sm->pmk)); + wpa_sm_deauthenticate(sm, REASON_UNSPECIFIED); + wpa_sm_req_scan(sm, 0, 0); + } +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + time_t now; + + time(&now); + while (sm->pmksa && sm->pmksa->expiration <= now) { + struct rsn_pmksa_cache *entry = sm->pmksa; + sm->pmksa = entry->next; + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->aa)); + pmksa_cache_free_entry(sm, entry, 0); + } + + pmksa_cache_set_expiration(sm); +} + + +static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + sm->cur_pmksa = NULL; + eapol_sm_request_reauth(sm->eapol); +} + + +static void pmksa_cache_set_expiration(struct wpa_sm *sm) +{ + int sec; + struct rsn_pmksa_cache *entry; + + eloop_cancel_timeout(pmksa_cache_expire, sm, NULL); + eloop_cancel_timeout(pmksa_cache_reauth, sm, NULL); + if (sm->pmksa == NULL) + return; + sec = sm->pmksa->expiration - time(NULL); + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, sm, NULL); + + entry = sm->cur_pmksa ? sm->cur_pmksa : + pmksa_cache_get(sm, sm->bssid, NULL); + if (entry) { + sec = sm->pmksa->reauth_time - time(NULL); + if (sec < 0) + sec = 0; + eloop_register_timeout(sec, 0, pmksa_cache_reauth, sm, NULL); + } +} + + +/** + * pmksa_cache_add - Add a PMKSA cache entry + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @aa: Authenticator address + * @spa: Supplicant address + * @ssid: The network configuration for which this PMK is being added + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function create a PMKSA entry for a new PMK and adds it to the PMKSA + * cache. If an old entry is already in the cache for the same Authenticator, + * this entry will be replaced with the new entry. PMKID will be calculated + * based on the PMK and the driver interface is notified of the new PMKID. + */ +struct rsn_pmksa_cache * +pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, + size_t pmk_len, const u8 *aa, const u8 *spa, + struct wpa_ssid *ssid) +{ + struct rsn_pmksa_cache *entry, *pos, *prev; + time_t now; + + if (sm->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN) + return NULL; + + entry = malloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + memset(entry, 0, sizeof(*entry)); + memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + rsn_pmkid(pmk, aa, spa, entry->pmkid); + now = time(NULL); + entry->expiration = now + sm->dot11RSNAConfigPMKLifetime; + entry->reauth_time = now + sm->dot11RSNAConfigPMKLifetime * + sm->dot11RSNAConfigPMKReauthThreshold / 100; + entry->akmp = WPA_KEY_MGMT_IEEE8021X; + memcpy(entry->aa, aa, ETH_ALEN); + entry->ssid = ssid; + + /* Replace an old entry for the same Authenticator (if found) with the + * new entry */ + pos = sm->pmksa; + prev = NULL; + while (pos) { + if (memcmp(aa, pos->aa, ETH_ALEN) == 0) { + if (pos->pmk_len == pmk_len && + memcmp(pos->pmk, pmk, pmk_len) == 0 && + memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "WPA: reusing previous " + "PMKSA entry"); + free(entry); + return pos; + } + if (prev == NULL) + sm->pmksa = pos->next; + else + prev->next = pos->next; + wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " + "the current AP"); + pmksa_cache_free_entry(sm, pos, 1); + break; + } + prev = pos; + pos = pos->next; + } + + if (sm->pmksa_count >= pmksa_cache_max_entries && sm->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + pos = sm->pmksa; + sm->pmksa = pos->next; + wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " + "entry (for " MACSTR ") to make room for new one", + MAC2STR(pos->aa)); + wpa_sm_remove_pmkid(sm, pos->aa, pos->pmkid); + pmksa_cache_free_entry(sm, pos, 0); + } + + /* Add the new entry; order by expiration time */ + pos = sm->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = sm->pmksa; + sm->pmksa = entry; + pmksa_cache_set_expiration(sm); + } else { + entry->next = prev->next; + prev->next = entry; + } + sm->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, + MAC2STR(entry->aa)); + wpa_sm_add_pmkid(sm, entry->aa, entry->pmkid); + + return entry; +} + + +/** + * pmksa_cache_free - Free all entries in PMKSA cache + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_cache_free(struct wpa_sm *sm) +{ + struct rsn_pmksa_cache *entry, *prev; + + if (sm == NULL) + return; + + entry = sm->pmksa; + sm->pmksa = NULL; + while (entry) { + prev = entry; + entry = entry->next; + free(prev); + } + pmksa_cache_set_expiration(sm); + sm->cur_pmksa = NULL; +} + + +/** + * pmksa_cache_get - Fetch a PMKSA cache entry + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @aa: Authenticator address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + */ +struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_sm *sm, + const u8 *aa, const u8 *pmkid) +{ + struct rsn_pmksa_cache *entry = sm->pmksa; + while (entry) { + if ((aa == NULL || memcmp(entry->aa, aa, ETH_ALEN) == 0) && + (pmkid == NULL || + memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Clear references to old data structures when wpa_supplicant is reconfigured. + */ +void pmksa_cache_notify_reconfig(struct wpa_sm *sm) +{ + struct rsn_pmksa_cache *entry = sm->pmksa; + while (entry) { + entry->ssid = NULL; + entry = entry->next; + } +} + + +static struct rsn_pmksa_cache * +pmksa_cache_clone_entry(struct wpa_sm *sm, + const struct rsn_pmksa_cache *old_entry, const u8 *aa) +{ + struct rsn_pmksa_cache *new_entry; + + new_entry = pmksa_cache_add(sm, old_entry->pmk, old_entry->pmk_len, + aa, sm->own_addr, old_entry->ssid); + if (new_entry == NULL) + return NULL; + + /* TODO: reorder entries based on expiration time? */ + new_entry->expiration = old_entry->expiration; + new_entry->opportunistic = 1; + + return new_entry; +} + + +/** + * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ssid: Pointer to the current network configuration + * @aa: Authenticator address for the new AP + * Returns: Pointer to a new PMKSA cache entry or %NULL if not available + * + * Try to create a new PMKSA cache entry opportunistically by guessing that the + * new AP is sharing the same PMK as another AP that has the same SSID and has + * already an entry in PMKSA cache. + */ +static struct rsn_pmksa_cache * +pmksa_cache_get_opportunistic(struct wpa_sm *sm, + struct wpa_ssid *ssid, const u8 *aa) +{ + struct rsn_pmksa_cache *entry = sm->pmksa; + + if (ssid == NULL) + return NULL; + while (entry) { + if (entry->ssid == ssid) { + entry = pmksa_cache_clone_entry(sm, entry, aa); + if (entry) { + wpa_printf(MSG_DEBUG, "RSN: added " + "opportunistic PMKSA cache entry " + "for " MACSTR, MAC2STR(aa)); + } + return entry; + } + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_get_current - Get the current used PMKSA entry + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to the current PMKSA cache entry or %NULL if not available + */ +struct rsn_pmksa_cache * pmksa_cache_get_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return NULL; + return sm->cur_pmksa; +} + + +/** + * pmksa_cache_clear_current - Clear the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_cache_clear_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + sm->cur_pmksa = NULL; +} + + +/** + * pmksa_cache_set_current - Set the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmkid: PMKID for selecting PMKSA or %NULL if not used + * @bssid: BSSID for PMKSA or %NULL if not used + * @ssid: The network configuration for the current network + * @try_opportunistic: Whether to allow opportunistic PMKSA caching + * Returns: 0 if PMKSA was found or -1 if no matching entry was found + */ +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, struct wpa_ssid *ssid, + int try_opportunistic) +{ + sm->cur_pmksa = NULL; + if (pmkid) + sm->cur_pmksa = pmksa_cache_get(sm, NULL, pmkid); + if (sm->cur_pmksa == NULL && bssid) + sm->cur_pmksa = pmksa_cache_get(sm, bssid, NULL); + if (sm->cur_pmksa == NULL && try_opportunistic) + sm->cur_pmksa = pmksa_cache_get_opportunistic(sm, ssid, bssid); + if (sm->cur_pmksa) { + wpa_hexdump(MSG_DEBUG, "RSN: PMKID", + sm->cur_pmksa->pmkid, PMKID_LEN); + return 0; + } + return -1; +} + + +/** + * pmksa_cache_list - Dump text list of entries in PMKSA cache + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA command. + */ +int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +{ + int i, j; + char *pos = buf; + struct rsn_pmksa_cache *entry; + time_t now; + + time(&now); + pos += snprintf(pos, buf + len - pos, + "Index / AA / PMKID / expiration (in seconds) / " + "opportunistic\n"); + i = 0; + entry = sm->pmksa; + while (entry) { + i++; + pos += snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->aa)); + for (j = 0; j < PMKID_LEN; j++) + pos += snprintf(pos, buf + len - pos, "%02x", + entry->pmkid[j]); + pos += snprintf(pos, buf + len - pos, " %d %d\n", + (int) (entry->expiration - now), + entry->opportunistic); + entry = entry->next; + } + return pos - buf; +} + + +/** + * pmksa_candidate_free - Free all entries in PMKSA candidate list + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_candidate_free(struct wpa_sm *sm) +{ + struct rsn_pmksa_candidate *entry, *prev; + + if (sm == NULL) + return; + + entry = sm->pmksa_candidates; + sm->pmksa_candidates = NULL; + while (entry) { + prev = entry; + entry = entry->next; + free(prev); + } +} + + +#ifdef IEEE8021X_EAPOL + +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_sm *sm = ctx; + + wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); + wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); + + if (sm->preauth_eapol == NULL || + memcmp(sm->preauth_bssid, "\x00\x00\x00\x00\x00\x00", + ETH_ALEN) == 0 || + memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " + "unexpected source " MACSTR " - dropped", + MAC2STR(src_addr)); + return; + } + + eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len); +} + + +static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, + void *ctx) +{ + struct wpa_sm *sm = ctx; + u8 pmk[PMK_LEN]; + + if (success) { + int res, pmk_len; + pmk_len = PMK_LEN; + res = eapol_sm_get_key(eapol, pmk, PMK_LEN); +#ifdef EAP_LEAP + if (res) { + res = eapol_sm_get_key(eapol, pmk, 16); + pmk_len = 16; + } +#endif /* EAP_LEAP */ + if (res == 0) { + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", + pmk, pmk_len); + sm->pmk_len = pmk_len; + pmksa_cache_add(sm, pmk, pmk_len, + sm->preauth_bssid, sm->own_addr, + sm->cur_ssid); + } else { + wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: failed to get " + "master session key from pre-auth EAPOL state " + "machines"); + success = 0; + } + } + + wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR + " %s", MAC2STR(sm->preauth_bssid), + success ? "completed successfully" : "failed"); + + rsn_preauth_deinit(sm); + rsn_preauth_candidate_process(sm); +} + + +static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + + wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR + " timed out", MAC2STR(sm->preauth_bssid)); + rsn_preauth_deinit(sm); + rsn_preauth_candidate_process(sm); +} + + +static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf, + size_t len) +{ + struct wpa_sm *sm = ctx; + u8 *msg; + size_t msglen; + int res; + + /* TODO: could add l2_packet_sendmsg that allows fragments to avoid + * extra copy here */ + + if (sm->l2_preauth == NULL) + return -1; + + msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL); + if (msg == NULL) + return -1; + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen); + res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid, + ETH_P_RSN_PREAUTH, msg, msglen); + free(msg); + return res; +} + + +/** + * rsn_preauth_init - Start new RSN pre-authentication + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Authenticator address (BSSID) with which to preauthenticate + * @config: Current network configuration + * Returns: 0 on success, -1 on another pre-authentication is in progress, + * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine + * initialization failure, -4 on memory allocation failure + * + * This function request an RSN pre-authentication with a given destination + * address. This is usually called for PMKSA candidates found from scan results + * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger + * pre-authentication. + */ +int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, struct wpa_ssid *config) +{ + struct eapol_config eapol_conf; + struct eapol_ctx *ctx; + + if (sm->preauth_eapol) + return -1; + + wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: starting pre-authentication " + "with " MACSTR, MAC2STR(dst)); + + sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr, + ETH_P_RSN_PREAUTH, + rsn_preauth_receive, sm, 0); + if (sm->l2_preauth == NULL) { + wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " + "processing for pre-authentication"); + return -2; + } + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); + return -4; + } + memset(ctx, 0, sizeof(*ctx)); + ctx->ctx = sm->ctx->ctx; + ctx->msg_ctx = sm->ctx->ctx; + ctx->preauth = 1; + ctx->cb = rsn_preauth_eapol_cb; + ctx->cb_ctx = sm; + ctx->scard_ctx = sm->scard_ctx; + ctx->eapol_send = rsn_preauth_eapol_send; + ctx->eapol_send_ctx = sm; + ctx->set_config_blob = sm->ctx->set_config_blob; + ctx->get_config_blob = sm->ctx->get_config_blob; + + sm->preauth_eapol = eapol_sm_init(ctx); + if (sm->preauth_eapol == NULL) { + free(ctx); + wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " + "state machines for pre-authentication"); + return -3; + } + memset(&eapol_conf, 0, sizeof(eapol_conf)); + eapol_conf.accept_802_1x_keys = 0; + eapol_conf.required_keys = 0; + eapol_conf.fast_reauth = sm->fast_reauth; + if (config) + eapol_conf.workaround = config->eap_workaround; + eapol_sm_notify_config(sm->preauth_eapol, config, &eapol_conf); + /* + * Use a shorter startPeriod with preauthentication since the first + * preauth EAPOL-Start frame may end up being dropped due to race + * condition in the AP between the data receive and key configuration + * after the 4-Way Handshake. + */ + eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6); + memcpy(sm->preauth_bssid, dst, ETH_ALEN); + + eapol_sm_notify_portValid(sm->preauth_eapol, TRUE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE); + + eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0, + rsn_preauth_timeout, sm, NULL); + + return 0; +} + + +/** + * rsn_preauth_deinit - Abort RSN pre-authentication + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * This function aborts the current RSN pre-authentication (if one is started) + * and frees resources allocated for it. + */ +void rsn_preauth_deinit(struct wpa_sm *sm) +{ + if (sm == NULL || !sm->preauth_eapol) + return; + + eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL); + eapol_sm_deinit(sm->preauth_eapol); + sm->preauth_eapol = NULL; + memset(sm->preauth_bssid, 0, ETH_ALEN); + + l2_packet_deinit(sm->l2_preauth); + sm->l2_preauth = NULL; +} + + +/** + * rsn_preauth_candidate_process - Process PMKSA candidates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Go through the PMKSA candidates and start pre-authentication if a candidate + * without an existing PMKSA cache entry is found. Processed candidates will be + * removed from the list. + */ +void rsn_preauth_candidate_process(struct wpa_sm *sm) +{ + struct rsn_pmksa_candidate *candidate; + + if (sm->pmksa_candidates == NULL) + return; + + /* TODO: drop priority for old candidate entries */ + + wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: processing PMKSA candidate " + "list"); + if (sm->preauth_eapol || + sm->proto != WPA_PROTO_RSN || + wpa_sm_get_state(sm) != WPA_COMPLETED || + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X) { + wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: not in suitable state " + "for new pre-authentication"); + return; /* invalid state for new pre-auth */ + } + + while (sm->pmksa_candidates) { + struct rsn_pmksa_cache *p = NULL; + candidate = sm->pmksa_candidates; + p = pmksa_cache_get(sm, candidate->bssid, NULL); + if (memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && + (p == NULL || p->opportunistic)) { + wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: PMKSA " + "candidate " MACSTR + " selected for pre-authentication", + MAC2STR(candidate->bssid)); + sm->pmksa_candidates = candidate->next; + rsn_preauth_init(sm, candidate->bssid, sm->cur_ssid); + free(candidate); + return; + } + wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: PMKSA candidate " + MACSTR " does not need pre-authentication anymore", + MAC2STR(candidate->bssid)); + /* Some drivers (e.g., NDIS) expect to get notified about the + * PMKIDs again, so report the existing data now. */ + if (p) { + wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid); + } + + sm->pmksa_candidates = candidate->next; + free(candidate); + } + wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: no more pending PMKSA " + "candidates"); +} + + +/** + * pmksa_candidate_add - Add a new PMKSA candidate + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @bssid: BSSID (authenticator address) of the candidate + * @prio: Priority (the smaller number, the higher priority) + * @preauth: Whether the candidate AP advertises support for pre-authentication + * + * This function is used to add PMKSA candidates for RSN pre-authentication. It + * is called from scan result processing and from driver events for PMKSA + * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event(). + */ +void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, + int prio, int preauth) +{ + struct rsn_pmksa_candidate *cand, *prev, *pos; + + if (sm->cur_ssid && sm->cur_ssid->proactive_key_caching) + pmksa_cache_get_opportunistic(sm, sm->cur_ssid, bssid); + + if (!preauth) { + wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " + "preauth flag"); + return; + } + + /* If BSSID already on candidate list, update the priority of the old + * entry. Do not override priority based on normal scan results. */ + prev = NULL; + cand = sm->pmksa_candidates; + while (cand) { + if (memcmp(cand->bssid, bssid, ETH_ALEN) == 0) { + if (prev) + prev->next = cand->next; + else + sm->pmksa_candidates = cand->next; + break; + } + prev = cand; + cand = cand->next; + } + + if (cand) { + if (prio < PMKID_CANDIDATE_PRIO_SCAN) + cand->priority = prio; + } else { + cand = malloc(sizeof(*cand)); + if (cand == NULL) + return; + memset(cand, 0, sizeof(*cand)); + memcpy(cand->bssid, bssid, ETH_ALEN); + cand->priority = prio; + } + + /* Add candidate to the list; order by increasing priority value. i.e., + * highest priority (smallest value) first. */ + prev = NULL; + pos = sm->pmksa_candidates; + while (pos) { + if (cand->priority <= pos->priority) + break; + prev = pos; + pos = pos->next; + } + cand->next = pos; + if (prev) + prev->next = cand; + else + sm->pmksa_candidates = cand; + + wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: added PMKSA cache " + "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); + rsn_preauth_candidate_process(sm); +} + + +/* TODO: schedule periodic scans if current AP supports preauth */ + +/** + * rsn_preauth_scan_results - Process scan results to find PMKSA candidates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @results: Scan results + * @count: Number of BSSes in scan results + * + * This functions goes through the scan results and adds all suitable APs + * (Authenticators) into PMKSA candidate list. + */ +void rsn_preauth_scan_results(struct wpa_sm *sm, + struct wpa_scan_result *results, int count) +{ + struct wpa_scan_result *r; + struct wpa_ie_data ie; + int i; + struct rsn_pmksa_cache *pmksa; + + if (sm->cur_ssid == NULL) + return; + + /* + * TODO: is it ok to free all candidates? What about the entries + * received from EVENT_PMKID_CANDIDATE? + */ + pmksa_candidate_free(sm); + + for (i = count - 1; i >= 0; i--) { + r = &results[i]; + if (r->ssid_len != sm->cur_ssid->ssid_len || + memcmp(r->ssid, sm->cur_ssid->ssid, + r->ssid_len) != 0) + continue; + + if (memcmp(r->bssid, sm->bssid, ETH_ALEN) == 0) + continue; + + if (r->rsn_ie_len == 0 || + wpa_parse_wpa_ie(r->rsn_ie, r->rsn_ie_len, &ie)) + continue; + + pmksa = pmksa_cache_get(sm, r->bssid, NULL); + if (pmksa && + (!pmksa->opportunistic || + !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) + continue; + + /* + * Give less priority to candidates found from normal + * scan results. + */ + pmksa_candidate_add(sm, r->bssid, + PMKID_CANDIDATE_PRIO_SCAN, + ie.capabilities & WPA_CAPABILITY_PREAUTH); + } +} + + +/** + * rsn_preauth_get_status - Get pre-authentication status + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query WPA2 pre-authentication for status information. This function fills in + * a text area with current status information. If the buffer (buf) is not + * large enough, status information will be truncated to fit the buffer. + */ +int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose) +{ + char *pos = buf, *end = buf + buflen; + int res; + + if (sm->preauth_eapol) { + pos += snprintf(pos, end - pos, "Pre-authentication " + "EAPOL state machines:\n"); + res = eapol_sm_get_status(sm->preauth_eapol, + pos, end - pos, verbose); + if (res >= 0) + pos += res; + } + + return pos - buf; +} + + +/** + * rsn_preauth_in_progress - Verify whether pre-authentication is in progress + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +int rsn_preauth_in_progress(struct wpa_sm *sm) +{ + return sm->preauth_eapol != NULL; +} + +#endif /* IEEE8021X_EAPOL */ diff --git a/contrib/wpa_supplicant/preauth.h b/contrib/wpa_supplicant/preauth.h new file mode 100644 index 0000000..9b528f0 --- /dev/null +++ b/contrib/wpa_supplicant/preauth.h @@ -0,0 +1,132 @@ +/* + * wpa_supplicant - WPA2/RSN pre-authentication functions + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 PREAUTH_H +#define PREAUTH_H + +struct wpa_scan_result; + +#ifndef CONFIG_NO_WPA + +void pmksa_cache_free(struct wpa_sm *sm); +struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_sm *sm, + const u8 *aa, const u8 *pmkid); +int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +void pmksa_candidate_free(struct wpa_sm *sm); +struct rsn_pmksa_cache * +pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, + size_t pmk_len, const u8 *aa, const u8 *spa, + struct wpa_ssid *ssid); +void pmksa_cache_notify_reconfig(struct wpa_sm *sm); +struct rsn_pmksa_cache * pmksa_cache_get_current(struct wpa_sm *sm); +void pmksa_cache_clear_current(struct wpa_sm *sm); +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, struct wpa_ssid *ssid, + int try_opportunistic); + +#else /* CONFIG_NO_WPA */ + +static inline void pmksa_cache_free(struct wpa_sm *sm) +{ +} + +static inline void pmksa_candidate_free(struct wpa_sm *sm) +{ +} + +static inline void pmksa_cache_notify_reconfig(struct wpa_sm *sm) +{ +} + +static inline struct rsn_pmksa_cache * +pmksa_cache_get_current(struct wpa_sm *sm) +{ + return NULL; +} + +static inline int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +{ + return -1; +} + +static inline void pmksa_cache_clear_current(struct wpa_sm *sm) +{ +} + +static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, + struct wpa_ssid *ssid, + int try_opportunistic) +{ + return -1; +} + +#endif /* CONFIG_NO_WPA */ + + +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) + +int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct wpa_ssid *config); +void rsn_preauth_deinit(struct wpa_sm *sm); +void rsn_preauth_scan_results(struct wpa_sm *sm, + struct wpa_scan_result *results, int count); +void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, + int prio, int preauth); +void rsn_preauth_candidate_process(struct wpa_sm *sm); +int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose); +int rsn_preauth_in_progress(struct wpa_sm *sm); + +#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA */ + +static inline void rsn_preauth_candidate_process(struct wpa_sm *sm) +{ +} + +static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct wpa_ssid *config) +{ + return -1; +} + +static inline void rsn_preauth_deinit(struct wpa_sm *sm) +{ +} +static inline void rsn_preauth_scan_results(struct wpa_sm *sm, + struct wpa_scan_result *results, + int count) +{ +} + +static inline void pmksa_candidate_add(struct wpa_sm *sm, + const u8 *bssid, + int prio, int preauth) +{ +} + +static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} + +static inline int rsn_preauth_in_progress(struct wpa_sm *sm) +{ + return 0; +} + +#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA */ + +#endif /* PREAUTH_H */ diff --git a/contrib/wpa_supplicant/preauth_test.c b/contrib/wpa_supplicant/preauth_test.c index 13741bb..d89058d 100644 --- a/contrib/wpa_supplicant/preauth_test.c +++ b/contrib/wpa_supplicant/preauth_test.c @@ -1,6 +1,15 @@ /* * WPA Supplicant - test code for pre-authentication - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2003-2006, Jouni Malinen <jkmaline@cc.hut.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. @@ -27,181 +36,175 @@ #include "l2_packet.h" #include "ctrl_iface.h" #include "pcsc_funcs.h" +#include "preauth.h" extern int wpa_debug_level; extern int wpa_debug_show_keys; -void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...) -{ - va_list ap; - char *buf; - const int buflen = 2048; - int len; - - buf = malloc(buflen); - if (buf == NULL) { - printf("Failed to allocate message buffer for:\n"); - va_start(ap, fmt); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); - return; - } - va_start(ap, fmt); - len = vsnprintf(buf, buflen, fmt, ap); - va_end(ap); - wpa_printf(level, "%s", buf); - wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len); - free(buf); -} +struct wpa_driver_ops *wpa_supplicant_drivers[] = { }; -void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, - union wpa_event_data *data) +struct preauth_test_data { + int auth_timed_out; +}; + + +static void _wpa_supplicant_req_scan(void *wpa_s, int sec, int usec) { + wpa_supplicant_req_scan(wpa_s, sec, usec); } -int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst) +static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) { - return -1; + wpa_supplicant_disassociate(wpa_s, reason_code); } -void rsn_preauth_deinit(struct wpa_supplicant *wpa_s) +static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) { + wpa_supplicant_deauthenticate(wpa_s, reason_code); } -int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len) +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) { - return 0; + struct ieee802_1x_hdr *hdr; + + *msg_len = sizeof(*hdr) + data_len; + hdr = 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) + memcpy(hdr + 1, data, data_len); + else + memset(hdr + 1, 0, data_len); + + if (data_pos) + *data_pos = hdr + 1; + + return (u8 *) hdr; } -int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) { - return 0; + return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos); } -void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) +static void _wpa_supplicant_set_state(void *ctx, wpa_states state) { + struct wpa_supplicant *wpa_s = ctx; + wpa_s->wpa_state = state; } -const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len) +static wpa_states _wpa_supplicant_get_state(void *ctx) { - return NULL; + struct wpa_supplicant *wpa_s = ctx; + return wpa_s->wpa_state; } -int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) +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 int eapol_test_eapol_send(void *ctx, int type, u8 *buf, size_t len) +static struct wpa_ssid * _wpa_supplicant_get_ssid(void *wpa_s) { - struct wpa_supplicant *wpa_s = ctx; - u8 *msg; - size_t msglen; - struct l2_ethhdr *ethhdr; - struct ieee802_1x_hdr *hdr; - int res; - - printf("WPA: wpa_eapol_send(type=%d len=%d)\n", type, len); + return wpa_supplicant_get_ssid(wpa_s); +} - if (wpa_s->l2_preauth == NULL) - return -1; - msglen = sizeof(*ethhdr) + sizeof(*hdr) + len; - msg = malloc(msglen); - if (msg == NULL) - return -1; +static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s) +{ + wpa_supplicant_cancel_auth_timeout(wpa_s); +} - ethhdr = (struct l2_ethhdr *) msg; - memcpy(ethhdr->h_dest, wpa_s->preauth_bssid, ETH_ALEN); - memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_RSN_PREAUTH); - hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - hdr->version = wpa_s->conf->eapol_version; - hdr->type = type; - hdr->length = htons(len); +static int wpa_supplicant_get_beacon_ie(void *wpa_s) +{ + printf("%s - not implemented\n", __func__); + return -1; +} - memcpy((u8 *) (hdr + 1), buf, len); - wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen); - res = l2_packet_send(wpa_s->l2_preauth, msg, msglen); - free(msg); - return res; +void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +{ + printf("%s - not implemented\n", __func__); } -static void eapol_test_eapol_done_cb(void *ctx) +static int wpa_supplicant_get_bssid(void *wpa_s, u8 *bssid) { - printf("WPA: EAPOL processing complete\n"); + printf("%s - not implemented\n", __func__); + return -1; } -static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) +static int wpa_supplicant_set_key(void *wpa_s, 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("eapol_sm_cb: success=%d\n", success); - eloop_terminate(); + printf("%s - not implemented\n", __func__); + return -1; } -static int test_eapol(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +static int wpa_supplicant_add_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) { - struct eapol_config eapol_conf; - struct eapol_ctx *ctx; + printf("%s - not implemented\n", __func__); + return -1; +} - ctx = malloc(sizeof(*ctx)); - if (ctx == NULL) { - printf("Failed to allocate EAPOL context.\n"); - return -1; - } - memset(ctx, 0, sizeof(*ctx)); - ctx->ctx = wpa_s; - ctx->msg_ctx = wpa_s; - ctx->scard_ctx = wpa_s->scard; - ctx->cb = eapol_sm_cb; - ctx->cb_ctx = wpa_s; - ctx->preauth = 0; - ctx->eapol_done_cb = eapol_test_eapol_done_cb; - ctx->eapol_send = eapol_test_eapol_send; - - wpa_s->preauth_eapol = eapol_sm_init(ctx); - if (wpa_s->preauth_eapol == NULL) { - free(ctx); - printf("Failed to initialize EAPOL state machines.\n"); - return -1; - } - wpa_s->current_ssid = ssid; - memset(&eapol_conf, 0, sizeof(eapol_conf)); - eapol_conf.accept_802_1x_keys = 1; - eapol_conf.required_keys = 0; - eapol_conf.workaround = ssid->eap_workaround; - eapol_sm_notify_config(wpa_s->preauth_eapol, ssid, &eapol_conf); +static int wpa_supplicant_remove_pmkid(void *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + printf("%s - not implemented\n", __func__); + return -1; +} - eapol_sm_notify_portValid(wpa_s->preauth_eapol, FALSE); - /* 802.1X::portControl = Auto */ - eapol_sm_notify_portEnabled(wpa_s->preauth_eapol, TRUE); +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); +} - return 0; + +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) { - l2_packet_deinit(wpa_s->l2_preauth); - eapol_sm_deinit(wpa_s->preauth_eapol); - wpa_s->preauth_eapol = NULL; + rsn_preauth_deinit(wpa_s->wpa); + pmksa_candidate_free(wpa_s->wpa); + pmksa_cache_free(wpa_s->wpa); + wpa_sm_deinit(wpa_s->wpa); scard_deinit(wpa_s->scard); wpa_supplicant_ctrl_iface_deinit(wpa_s); wpa_config_free(wpa_s->conf); @@ -210,117 +213,75 @@ static void test_eapol_clean(struct wpa_supplicant *wpa_s) static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx) { - struct wpa_supplicant *wpa_s = eloop_ctx; + struct preauth_test_data *p = eloop_ctx; printf("EAPOL test timed out\n"); - wpa_s->auth_timed_out = 1; + p->auth_timed_out = 1; eloop_terminate(); } -static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) +static void eapol_test_poll(void *eloop_ctx, void *timeout_ctx) { - if (ssid->identity == NULL && wpa_s->imsi) { - ssid->identity = malloc(1 + wpa_s->imsi_len); - if (ssid->identity) { - ssid->identity[0] = '1'; - memcpy(ssid->identity + 1, wpa_s->imsi, - wpa_s->imsi_len); - ssid->identity_len = 1 + wpa_s->imsi_len; - wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " - "IMSI", ssid->identity, - ssid->identity_len); - } + 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 void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - char buf[100]; - size_t len; - - if (ssid->pcsc == NULL) - return; - if (wpa_s->scard != NULL) { - wpa_supplicant_imsi_identity(wpa_s, ssid); - return; - } - wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - " - "initialize PCSC"); - wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin); - if (wpa_s->scard == NULL) { - wpa_printf(MSG_WARNING, "Failed to initialize SIM " - "(pcsc-lite)"); - /* TODO: what to do here? */ - return; - } - eapol_sm_register_scard_ctx(wpa_s->preauth_eapol, wpa_s->scard); - - len = sizeof(buf); - if (scard_get_imsi(wpa_s->scard, buf, &len)) { - wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); - /* TODO: what to do here? */ - return; - } - - wpa_hexdump(MSG_DEBUG, "IMSI", (u8 *) buf, len); - free(wpa_s->imsi); - wpa_s->imsi = malloc(len); - if (wpa_s->imsi) { - memcpy(wpa_s->imsi, buf, len); - wpa_s->imsi_len = len; - wpa_supplicant_imsi_identity(wpa_s, ssid); - } -} +static struct wpa_driver_ops dummy_driver; -static void rsn_preauth_receive(void *ctx, unsigned char *src_addr, - unsigned char *buf, size_t len) +static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname) { - struct wpa_supplicant *wpa_s = ctx; + struct l2_packet_data *l2; + struct wpa_sm_ctx *ctx; - wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); - wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); - - if (wpa_s->preauth_eapol == NULL || - memcmp(wpa_s->preauth_bssid, "\x00\x00\x00\x00\x00\x00", - ETH_ALEN) == 0 || - memcmp(wpa_s->preauth_bssid, src_addr, ETH_ALEN) != 0) { - wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " - "unexpected source " MACSTR " - dropped", - MAC2STR(src_addr)); - return; - } + memset(&dummy_driver, 0, sizeof(dummy_driver)); + wpa_s->driver = &dummy_driver; - eapol_sm_rx_eapol(wpa_s->preauth_eapol, src_addr, buf, len); -} + ctx = malloc(sizeof(*ctx)); + assert(ctx != NULL); + memset(ctx, 0, sizeof(*ctx)); + ctx->ctx = wpa_s; + ctx->set_state = _wpa_supplicant_set_state; + ctx->get_state = _wpa_supplicant_get_state; + ctx->req_scan = _wpa_supplicant_req_scan; + ctx->deauthenticate = _wpa_supplicant_deauthenticate; + ctx->disassociate = _wpa_supplicant_disassociate; + ctx->set_key = wpa_supplicant_set_key; + ctx->scan = wpa_supplicant_scan; + ctx->get_ssid = _wpa_supplicant_get_ssid; + 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; + + 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); -static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *target, - const char *ifname) -{ strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname); - if (hwaddr_aton(target, wpa_s->preauth_bssid)) { - printf("Failed to parse target address '%s'.\n", target); - exit(-1); - } - - wpa_s->l2_preauth = l2_packet_init(wpa_s->ifname, NULL, - ETH_P_RSN_PREAUTH, - rsn_preauth_receive, wpa_s); - if (wpa_s->l2_preauth == NULL) { - wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " - "processing for pre-authentication"); - exit(-1); - } - - if (l2_packet_get_own_addr(wpa_s->l2_preauth, wpa_s->own_addr)) { + 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); } @@ -337,16 +298,25 @@ int main(int argc, char *argv[]) { struct wpa_supplicant wpa_s; int ret = 1; + u8 bssid[ETH_ALEN]; + struct preauth_test_data preauth_test; + + memset(&preauth_test, 0, sizeof(preauth_test)); wpa_debug_level = 0; wpa_debug_show_keys = 1; if (argc != 4) { - printf("usage: eapol_test <conf> <target MAC address> " + 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; + } + eloop_init(&wpa_s); memset(&wpa_s, 0, sizeof(wpa_s)); @@ -360,33 +330,36 @@ int main(int argc, char *argv[]) return -1; } - wpa_init_conf(&wpa_s, argv[2], argv[3]); + wpa_init_conf(&wpa_s, argv[3]); if (wpa_supplicant_ctrl_iface_init(&wpa_s)) { printf("Failed to initialize control interface '%s'.\n" - "You may have another eapol_test process already " + "You may have another preauth_test process already " "running or the file was\n" - "left by an unclean termination of eapol_test in " + "left by an unclean termination of preauth_test in " "which case you will need\n" "to manually remove this file before starting " - "eapol_test again.\n", + "preauth_test again.\n", wpa_s.conf->ctrl_interface); return -1; } - wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid); + if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) + return -1; - if (test_eapol(&wpa_s, wpa_s.conf->ssid)) + if (rsn_preauth_init(wpa_s.wpa, bssid, wpa_s.conf->ssid)) return -1; - eloop_register_timeout(30, 0, eapol_test_timeout, &wpa_s, NULL); + 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(SIGINT, eapol_test_terminate, NULL); eloop_register_signal(SIGTERM, eapol_test_terminate, NULL); eloop_register_signal(SIGHUP, eapol_test_terminate, NULL); eloop_run(); - if (wpa_s.auth_timed_out) + if (preauth_test.auth_timed_out) ret = -2; - else - ret = 0; + else { + ret = pmksa_cache_get(wpa_s.wpa, bssid, NULL) ? 0 : -3; + } test_eapol_clean(&wpa_s); diff --git a/contrib/wpa_supplicant/radius.c b/contrib/wpa_supplicant/radius.c index 5fd323d..ce79a62 100644 --- a/contrib/wpa_supplicant/radius.c +++ b/contrib/wpa_supplicant/radius.c @@ -16,18 +16,20 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <netinet/in.h> #include <string.h> -#include <sys/ioctl.h> #include <signal.h> #include <sys/time.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <netinet/in.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <arpa/inet.h> - +#endif /* CONFIG_NATIVE_WINDOWS */ #include "common.h" #include "radius.h" #include "md5.h" +#include "crypto.h" struct radius_msg *radius_msg_new(u8 code, u8 identifier) @@ -124,8 +126,10 @@ static const char *radius_code_string(u8 code) struct radius_attr_type { u8 type; char *name; - enum { RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, - RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32 } data_type; + enum { + RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, + RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 + } data_type; }; static struct radius_attr_type radius_attrs[] = @@ -179,8 +183,8 @@ static struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", - RADIUS_ATTR_INT32 } - + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, }; #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) @@ -231,6 +235,19 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) printf(" Invalid IP address length %d\n", len); break; +#ifdef CONFIG_IPV6 + case RADIUS_ATTR_IPV6: + if (len == 16) { + char buf[128]; + const char *atxt; + struct in6_addr *addr = (struct in6_addr *) pos; + atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); + printf(" Value: %s\n", atxt ? atxt : "?"); + } else + printf(" Invalid IPv6 address length %d\n", len); + break; +#endif /* CONFIG_IPV6 */ + case RADIUS_ATTR_HEXDUMP: case RADIUS_ATTR_UNDIST: printf(" Value:"); @@ -242,7 +259,8 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) case RADIUS_ATTR_INT32: if (len == 4) { u32 *val = (u32 *) pos; - printf(" Value: %d\n", ntohl(*val)); + printf(" Value: %u\n", + (unsigned int) ntohl(*val)); } else printf(" Invalid INT32 length %d\n", len); break; @@ -302,7 +320,8 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, { u8 auth[MD5_MAC_LEN]; struct radius_attr_hdr *attr; - MD5_CTX context; + const u8 *addr[4]; + size_t len[4]; memset(auth, 0, MD5_MAC_LEN); attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, @@ -318,13 +337,15 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, (u8 *) (attr + 1)); /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ - MD5Init(&context); - MD5Update(&context, (u8 *) msg->hdr, 1 + 1 + 2); - MD5Update(&context, req_authenticator, MD5_MAC_LEN); - MD5Update(&context, (u8 *) (msg->hdr + 1), - msg->buf_used - sizeof(*msg->hdr)); - MD5Update(&context, secret, secret_len); - MD5Final(msg->hdr->authenticator, &context); + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = req_authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, msg->hdr->authenticator); if (msg->buf_used > 0xffff) { printf("WARNING: too long RADIUS message (%lu)\n", @@ -338,14 +359,16 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, size_t secret_len) { - MD5_CTX context; + const u8 *addr[2]; + size_t len[2]; msg->hdr->length = htons(msg->buf_used); memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); - MD5Init(&context); - MD5Update(&context, msg->buf, msg->buf_used); - MD5Update(&context, secret, secret_len); - MD5Final(msg->hdr->authenticator, &context); + addr[0] = msg->buf; + len[0] = msg->buf_used; + addr[1] = secret; + len[1] = secret_len; + md5_vector(2, addr, len, msg->hdr->authenticator); if (msg->buf_used > 0xffff) { printf("WARNING: too long RADIUS messages (%lu)\n", @@ -378,7 +401,7 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, - u8 *data, size_t data_len) + const u8 *data, size_t data_len) { size_t buf_needed; struct radius_attr_hdr *attr; @@ -493,9 +516,9 @@ struct radius_msg *radius_msg_parse(const u8 *data, size_t len) } -int radius_msg_add_eap(struct radius_msg *msg, u8 *data, size_t data_len) +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) { - u8 *pos = data; + const u8 *pos = data; size_t left = data_len; while (left > 0) { @@ -605,10 +628,11 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, } -int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len, - struct radius_msg *sent_msg) +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, int auth) { - MD5_CTX context; + const u8 *addr[4]; + size_t len[4]; u8 hash[MD5_MAC_LEN]; if (sent_msg == NULL) { @@ -616,19 +640,22 @@ int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len, return 1; } - if (radius_msg_verify_msg_auth(msg, secret, secret_len, + if (auth && + radius_msg_verify_msg_auth(msg, secret, secret_len, sent_msg->hdr->authenticator)) { return 1; } /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ - MD5Init(&context); - MD5Update(&context, (u8 *) msg->hdr, 1 + 1 + 2); - MD5Update(&context, sent_msg->hdr->authenticator, MD5_MAC_LEN); - MD5Update(&context, (u8 *) (msg->hdr + 1), - msg->buf_used - sizeof(*msg->hdr)); - MD5Update(&context, secret, secret_len); - MD5Final(hash, &context); + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = sent_msg->hdr->authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { printf("Response Authenticator invalid!\n"); return 1; @@ -639,29 +666,6 @@ int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len, } -int radius_msg_verify_acct(struct radius_msg *msg, u8 *secret, - size_t secret_len, struct radius_msg *sent_msg) -{ - MD5_CTX context; - u8 hash[MD5_MAC_LEN]; - - MD5Init(&context); - MD5Update(&context, msg->buf, 4); - MD5Update(&context, sent_msg->hdr->authenticator, MD5_MAC_LEN); - if (msg->buf_used > sizeof(struct radius_hdr)) - MD5Update(&context, msg->buf + sizeof(struct radius_hdr), - msg->buf_used - sizeof(struct radius_hdr)); - MD5Update(&context, secret, secret_len); - MD5Final(hash, &context); - if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { - printf("Response Authenticator invalid!\n"); - return 1; - } - - return 0; -} - - int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type) { @@ -694,16 +698,19 @@ void radius_msg_make_authenticator(struct radius_msg *msg, u8 *data, size_t len) { struct timeval tv; - MD5_CTX context; long int l; + const u8 *addr[3]; + size_t elen[3]; gettimeofday(&tv, NULL); l = random(); - MD5Init(&context); - MD5Update(&context, (u8 *) &tv, sizeof(tv)); - MD5Update(&context, data, len); - MD5Update(&context, (u8 *) &l, sizeof(l)); - MD5Final(msg->hdr->authenticator, &context); + addr[0] = (u8 *) &tv; + elen[0] = sizeof(tv); + addr[1] = data; + elen[1] = len; + addr[2] = (u8 *) &l; + elen[2] = sizeof(l); + md5_vector(3, addr, elen, msg->hdr->authenticator); } @@ -780,8 +787,9 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, const u8 *pos; size_t left, plen; u8 hash[MD5_MAC_LEN]; - MD5_CTX context; int i, first = 1; + const u8 *addr[3]; + size_t elen[3]; /* key: 16-bit salt followed by encrypted key info */ @@ -804,15 +812,19 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, /* b(1) = MD5(Secret + Request-Authenticator + Salt) * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ - MD5Init(&context); - MD5Update(&context, secret, secret_len); + addr[0] = secret; + elen[0] = secret_len; if (first) { - MD5Update(&context, req_authenticator, MD5_MAC_LEN); - MD5Update(&context, key, 2); /* Salt */ - first = 0; - } else - MD5Update(&context, pos - MD5_MAC_LEN, MD5_MAC_LEN); - MD5Final(hash, &context); + addr[1] = req_authenticator; + elen[1] = MD5_MAC_LEN; + addr[2] = key; + elen[2] = 2; /* Salt */ + } else { + addr[1] = pos - MD5_MAC_LEN; + elen[1] = MD5_MAC_LEN; + } + md5_vector(first ? 3 : 2, addr, elen, hash); + first = 0; for (i = 0; i < MD5_MAC_LEN; i++) *ppos++ = *pos++ ^ hash[i]; @@ -845,7 +857,8 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, { int i, len, first = 1; u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; - MD5_CTX context; + const u8 *addr[3]; + size_t _len[3]; saltbuf[0] = salt >> 8; saltbuf[1] = salt; @@ -864,16 +877,19 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, while (len > 0) { /* b(1) = MD5(Secret + Request-Authenticator + Salt) * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ - MD5Init(&context); - MD5Update(&context, secret, secret_len); + addr[0] = secret; + _len[0] = secret_len; if (first) { - MD5Update(&context, req_authenticator, MD5_MAC_LEN); - MD5Update(&context, saltbuf, sizeof(saltbuf)); - first = 0; + addr[1] = req_authenticator; + _len[1] = MD5_MAC_LEN; + addr[2] = saltbuf; + _len[2] = sizeof(saltbuf); } else { - MD5Update(&context, pos - MD5_MAC_LEN, MD5_MAC_LEN); + addr[1] = pos - MD5_MAC_LEN; + _len[1] = MD5_MAC_LEN; } - MD5Final(hash, &context); + md5_vector(first ? 3 : 2, addr, _len, hash); + first = 0; for (i = 0; i < MD5_MAC_LEN; i++) *pos++ ^= hash[i]; @@ -1037,8 +1053,9 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, { u8 buf[128]; int padlen, i, pos; - MD5_CTX context; size_t buf_len; + const u8 *addr[2]; + size_t len[2]; u8 hash[16]; if (data_len > 128) @@ -1054,20 +1071,22 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, buf_len += padlen; } - MD5Init(&context); - MD5Update(&context, secret, secret_len); - MD5Update(&context, msg->hdr->authenticator, 16); - MD5Final(hash, &context); + addr[0] = secret; + len[0] = secret_len; + addr[1] = msg->hdr->authenticator; + len[1] = 16; + md5_vector(2, addr, len, hash); for (i = 0; i < 16; i++) buf[i] ^= hash[i]; pos = 16; while (pos < buf_len) { - MD5Init(&context); - MD5Update(&context, secret, secret_len); - MD5Update(&context, &buf[pos - 16], 16); - MD5Final(hash, &context); + addr[0] = secret; + len[0] = secret_len; + addr[1] = &buf[pos - 16]; + len[1] = 16; + md5_vector(2, addr, len, hash); for (i = 0; i < 16; i++) buf[pos + i] ^= hash[i]; @@ -1104,13 +1123,14 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, - size_t *len) + size_t *len, const u8 *start) { int i; struct radius_attr_hdr *attr = NULL; for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == type) { + if (msg->attrs[i]->type == type && + (start == NULL || (u8 *) msg->attrs[i] > start)) { attr = msg->attrs[i]; break; } @@ -1123,3 +1143,18 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, *len = attr->length - sizeof(*attr); return 0; } + + +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) +{ + int i, count; + + for (count = 0, i = 0; i < msg->attr_used; i++) { + if (msg->attrs[i]->type == type && + msg->attrs[i]->length >= + sizeof(struct radius_attr_hdr) + min_len) + count++; + } + + return count; +} diff --git a/contrib/wpa_supplicant/radius.h b/contrib/wpa_supplicant/radius.h index 318e0ad..b9f8977 100644 --- a/contrib/wpa_supplicant/radius.h +++ b/contrib/wpa_supplicant/radius.h @@ -63,7 +63,8 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_CONNECT_INFO = 77, RADIUS_ATTR_EAP_MESSAGE = 79, RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, - RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85 + RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, + RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 }; @@ -168,16 +169,16 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, size_t secret_len); struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, - u8 *data, size_t data_len); + const u8 *data, size_t data_len); struct radius_msg *radius_msg_parse(const u8 *data, size_t len); -int radius_msg_add_eap(struct radius_msg *msg, u8 *data, size_t data_len); +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, + size_t data_len); u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); -int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len, - struct radius_msg *sent_msg); +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, + int auth); int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_auth); -int radius_msg_verify_acct(struct radius_msg *msg, u8 *secret, - size_t secret_len, struct radius_msg *sent_msg); int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type); void radius_msg_make_authenticator(struct radius_msg *msg, @@ -219,6 +220,7 @@ static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type, return 0; } int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, - size_t *len); + size_t *len, const u8 *start); +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len); #endif /* RADIUS_H */ diff --git a/contrib/wpa_supplicant/radius_client.c b/contrib/wpa_supplicant/radius_client.c index 91ab3c7..abc28bd 100644 --- a/contrib/wpa_supplicant/radius_client.c +++ b/contrib/wpa_supplicant/radius_client.c @@ -1,41 +1,39 @@ /* * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / RADIUS client / modified for eapol_test - * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * Host AP kernel driver / RADIUS client + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.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. See README and COPYING for - * more details. + * 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 <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <netinet/in.h> #include <string.h> #include <time.h> #include <sys/types.h> +#include <sys/time.h> +#include <errno.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> +#endif /* CONFIG_NATIVE_WINDOWS */ -#include "common.h" -#include "wpa.h" -#include "config_ssid.h" -#include "wpa_supplicant.h" -#include "wpa_supplicant_i.h" +#include "hostapd.h" #include "radius.h" #include "radius_client.h" #include "eloop.h" -#include "l2_packet.h" - -#include "wpa_supplicant.h" -#define hostapd_logger(h, a, m, l, t...) wpa_printf(MSG_DEBUG, t) -#define HOSTAPD_DEBUG(l, a...) wpa_printf(MSG_DEBUG, a) -#define HOSTAPD_DEBUG_COND(l) 1 /* Defaults for RADIUS retransmit values (exponential backoff) */ -#define RADIUS_CLIENT_FIRST_WAIT 1 /* seconds */ +#define RADIUS_CLIENT_FIRST_WAIT 3 /* seconds */ #define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */ #define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts * before entry is removed from retransmit @@ -47,11 +45,66 @@ * many failed retry attempts */ +struct radius_rx_handler { + RadiusRxResult (*handler)(struct radius_msg *msg, + struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, + void *data); + void *data; +}; + + +/* RADIUS message retransmit list */ +struct radius_msg_list { + u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages + * for the same STA. */ + struct radius_msg *msg; + RadiusType msg_type; + time_t first_try; + time_t next_try; + int attempts; + int next_wait; + struct timeval last_attempt; + + u8 *shared_secret; + size_t shared_secret_len; + + /* TODO: server config with failover to backup server(s) */ + + struct radius_msg_list *next; +}; + + +struct radius_client_data { + void *ctx; + struct hostapd_radius_servers *conf; + + int auth_serv_sock; /* socket for authentication RADIUS messages */ + int acct_serv_sock; /* socket for accounting RADIUS messages */ + int auth_serv_sock6; + int acct_serv_sock6; + int auth_sock; /* currently used socket */ + int acct_sock; /* currently used socket */ + + struct radius_rx_handler *auth_handlers; + size_t num_auth_handlers; + struct radius_rx_handler *acct_handlers; + size_t num_acct_handlers; + + struct radius_msg_list *msgs; + size_t num_msgs; + + u8 next_radius_identifier; +}; + static int -radius_change_server(struct wpa_supplicant *wpa_s, struct hostapd_radius_server *nserv, +radius_change_server(struct radius_client_data *radius, + struct hostapd_radius_server *nserv, struct hostapd_radius_server *oserv, - int sock, int auth); + int sock, int sock6, int auth); +static int radius_client_init_acct(struct radius_client_data *radius); +static int radius_client_init_auth(struct radius_client_data *radius); static void radius_client_msg_free(struct radius_msg_list *req) @@ -62,9 +115,9 @@ static void radius_client_msg_free(struct radius_msg_list *req) } -int radius_client_register(struct wpa_supplicant *wpa_s, RadiusType msg_type, - RadiusRxResult (*handler)(struct wpa_supplicant *wpa_s, - struct radius_msg *msg, +int radius_client_register(struct radius_client_data *radius, + RadiusType msg_type, + RadiusRxResult (*handler)(struct radius_msg *msg, struct radius_msg *req, u8 *shared_secret, size_t shared_secret_len, @@ -75,11 +128,11 @@ int radius_client_register(struct wpa_supplicant *wpa_s, RadiusType msg_type, size_t *num; if (msg_type == RADIUS_ACCT) { - handlers = &wpa_s->radius->acct_handlers; - num = &wpa_s->radius->num_acct_handlers; + handlers = &radius->acct_handlers; + num = &radius->num_acct_handlers; } else { - handlers = &wpa_s->radius->auth_handlers; - num = &wpa_s->radius->num_auth_handlers; + handlers = &radius->auth_handlers; + num = &radius->num_auth_handlers; } newh = (struct radius_rx_handler *) @@ -97,24 +150,62 @@ int radius_client_register(struct wpa_supplicant *wpa_s, RadiusType msg_type, } -static int radius_client_retransmit(struct wpa_supplicant *wpa_s, +static void radius_client_handle_send_error(struct radius_client_data *radius, + int s, RadiusType msg_type) +{ +#ifndef CONFIG_NATIVE_WINDOWS + int _errno = errno; + perror("send[RADIUS]"); + if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL) { + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Send failed - maybe interface status changed -" + " try to connect again"); + eloop_unregister_read_sock(s); + close(s); + if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) + radius_client_init_acct(radius); + else + radius_client_init_auth(radius); + } +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +static int radius_client_retransmit(struct radius_client_data *radius, struct radius_msg_list *entry, time_t now) { + struct hostapd_radius_servers *conf = radius->conf; int s; if (entry->msg_type == RADIUS_ACCT || - entry->msg_type == RADIUS_ACCT_INTERIM) - s = wpa_s->radius->acct_serv_sock; - else - s = wpa_s->radius->auth_serv_sock; + entry->msg_type == RADIUS_ACCT_INTERIM) { + s = radius->acct_sock; + if (entry->attempts == 0) + conf->acct_server->requests++; + else { + conf->acct_server->timeouts++; + conf->acct_server->retransmissions++; + } + } else { + s = radius->auth_sock; + if (entry->attempts == 0) + conf->auth_server->requests++; + else { + conf->auth_server->timeouts++; + conf->auth_server->retransmissions++; + } + } /* retransmit; remove entry if too many attempts */ entry->attempts++; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Resending RADIUS message (id=%d)" - "\n", entry->msg->hdr->identifier); + hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", + entry->msg->hdr->identifier); + gettimeofday(&entry->last_attempt, NULL); if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0) - perror("send[RADIUS]"); + radius_client_handle_send_error(radius, s, entry->msg_type); entry->next_try = now + entry->next_wait; entry->next_wait *= 2; @@ -132,12 +223,14 @@ static int radius_client_retransmit(struct wpa_supplicant *wpa_s, static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) { - struct wpa_supplicant *wpa_s = eloop_ctx; + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; time_t now, first; struct radius_msg_list *entry, *prev, *tmp; int auth_failover = 0, acct_failover = 0; + char abuf[50]; - entry = wpa_s->radius->msgs; + entry = radius->msgs; if (!entry) return; @@ -147,16 +240,16 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) prev = NULL; while (entry) { if (now >= entry->next_try && - radius_client_retransmit(wpa_s, entry, now)) { + radius_client_retransmit(radius, entry, now)) { if (prev) prev->next = entry->next; else - wpa_s->radius->msgs = entry->next; + radius->msgs = entry->next; tmp = entry; entry = entry->next; radius_client_msg_free(tmp); - wpa_s->radius->num_msgs--; + radius->num_msgs--; continue; } @@ -175,51 +268,98 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) entry = entry->next; } - if (wpa_s->radius->msgs) { + if (radius->msgs) { if (first < now) first = now; eloop_register_timeout(first - now, 0, - radius_client_timer, wpa_s, NULL); + radius_client_timer, radius, NULL); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " + "retransmit in %ld seconds", + (long int) (first - now)); } - if (auth_failover && wpa_s->num_auth_servers > 1) { + if (auth_failover && conf->num_auth_servers > 1) { struct hostapd_radius_server *next, *old; - old = wpa_s->auth_server; - hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS, + old = conf->auth_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_NOTICE, "No response from Authentication server " "%s:%d - failover", - inet_ntoa(old->addr), old->port); + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_AUTH) + old->timeouts++; + } next = old + 1; - if (next > &(wpa_s->auth_servers - [wpa_s->num_auth_servers - 1])) - next = wpa_s->auth_servers; - wpa_s->auth_server = next; - radius_change_server(wpa_s, next, old, - wpa_s->radius->auth_serv_sock, 1); + if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) + next = conf->auth_servers; + conf->auth_server = next; + radius_change_server(radius, next, old, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); } - if (acct_failover && wpa_s->num_acct_servers > 1) { + if (acct_failover && conf->num_acct_servers > 1) { struct hostapd_radius_server *next, *old; - old = wpa_s->acct_server; - hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS, + old = conf->acct_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_NOTICE, "No response from Accounting server " "%s:%d - failover", - inet_ntoa(old->addr), old->port); + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + old->timeouts++; + } + next = old + 1; - if (next > &wpa_s->acct_servers - [wpa_s->num_acct_servers - 1]) - next = wpa_s->acct_servers; - wpa_s->acct_server = next; - radius_change_server(wpa_s, next, old, - wpa_s->radius->acct_serv_sock, 0); + if (next > &conf->acct_servers[conf->num_acct_servers - 1]) + next = conf->acct_servers; + conf->acct_server = next; + radius_change_server(radius, next, old, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); } } -static void radius_client_list_add(struct wpa_supplicant *wpa_s, struct radius_msg *msg, +static void radius_client_update_timeout(struct radius_client_data *radius) +{ + time_t now, first; + struct radius_msg_list *entry; + + eloop_cancel_timeout(radius_client_timer, radius, NULL); + + if (radius->msgs == NULL) { + return; + } + + first = 0; + for (entry = radius->msgs; entry; entry = entry->next) { + if (first == 0 || entry->next_try < first) + first = entry->next_try; + } + + time(&now); + if (first < now) + first = now; + eloop_register_timeout(first - now, 0, radius_client_timer, radius, + NULL); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" + " %ld seconds\n", (long int) (first - now)); +} + + +static void radius_client_list_add(struct radius_client_data *radius, + struct radius_msg *msg, RadiusType msg_type, u8 *shared_secret, size_t shared_secret_len, u8 *addr) { @@ -251,16 +391,13 @@ static void radius_client_list_add(struct wpa_supplicant *wpa_s, struct radius_m time(&entry->first_try); entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 1; + gettimeofday(&entry->last_attempt, NULL); entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + entry->next = radius->msgs; + radius->msgs = entry; + radius_client_update_timeout(radius); - if (!wpa_s->radius->msgs) - eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, - radius_client_timer, wpa_s, NULL); - - entry->next = wpa_s->radius->msgs; - wpa_s->radius->msgs = entry; - - if (wpa_s->radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { + if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { printf("Removing the oldest un-ACKed RADIUS packet due to " "retransmit list limits.\n"); prev = NULL; @@ -273,11 +410,11 @@ static void radius_client_list_add(struct wpa_supplicant *wpa_s, struct radius_m radius_client_msg_free(entry); } } else - wpa_s->radius->num_msgs++; + radius->num_msgs++; } -static void radius_client_list_del(struct wpa_supplicant *wpa_s, +static void radius_client_list_del(struct radius_client_data *radius, RadiusType msg_type, u8 *addr) { struct radius_msg_list *entry, *prev, *tmp; @@ -285,7 +422,7 @@ static void radius_client_list_del(struct wpa_supplicant *wpa_s, if (addr == NULL) return; - entry = wpa_s->radius->msgs; + entry = radius->msgs; prev = NULL; while (entry) { if (entry->msg_type == msg_type && @@ -293,14 +430,15 @@ static void radius_client_list_del(struct wpa_supplicant *wpa_s, if (prev) prev->next = entry->next; else - wpa_s->radius->msgs = entry->next; + radius->msgs = entry->next; tmp = entry; entry = entry->next; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Removing matching RADIUS message for " - MACSTR "\n", MAC2STR(addr)); + hostapd_logger(radius->ctx, addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing matching RADIUS message"); radius_client_msg_free(tmp); - wpa_s->radius->num_msgs--; + radius->num_msgs--; continue; } prev = entry; @@ -309,9 +447,10 @@ static void radius_client_list_del(struct wpa_supplicant *wpa_s, } -int radius_client_send(struct wpa_supplicant *wpa_s, struct radius_msg *msg, - RadiusType msg_type, u8 *addr) +int radius_client_send(struct radius_client_data *radius, + struct radius_msg *msg, RadiusType msg_type, u8 *addr) { + struct hostapd_radius_servers *conf = radius->conf; u8 *shared_secret; size_t shared_secret_len; char *name; @@ -319,33 +458,36 @@ int radius_client_send(struct wpa_supplicant *wpa_s, struct radius_msg *msg, if (msg_type == RADIUS_ACCT_INTERIM) { /* Remove any pending interim acct update for the same STA. */ - radius_client_list_del(wpa_s, msg_type, addr); + radius_client_list_del(radius, msg_type, addr); } if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { - shared_secret = wpa_s->acct_server->shared_secret; - shared_secret_len = wpa_s->acct_server->shared_secret_len; + shared_secret = conf->acct_server->shared_secret; + shared_secret_len = conf->acct_server->shared_secret_len; radius_msg_finish_acct(msg, shared_secret, shared_secret_len); name = "accounting"; - s = wpa_s->radius->acct_serv_sock; + s = radius->acct_sock; + conf->acct_server->requests++; } else { - shared_secret = wpa_s->auth_server->shared_secret; - shared_secret_len = wpa_s->auth_server->shared_secret_len; + shared_secret = conf->auth_server->shared_secret; + shared_secret_len = conf->auth_server->shared_secret_len; radius_msg_finish(msg, shared_secret, shared_secret_len); name = "authentication"; - s = wpa_s->radius->auth_serv_sock; + s = radius->auth_sock; + conf->auth_server->requests++; } - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Sending RADIUS message to %s server\n", name); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " + "server", name); + if (conf->msg_dumps) radius_msg_dump(msg); res = send(s, msg->buf, msg->buf_used, 0); if (res < 0) - perror("send[RADIUS]"); + radius_client_handle_send_error(radius, s, msg_type); - radius_client_list_add(wpa_s, msg, msg_type, shared_secret, + radius_client_list_add(radius, msg, msg_type, shared_secret, shared_secret_len, addr); return res; @@ -354,22 +496,37 @@ int radius_client_send(struct wpa_supplicant *wpa_s, struct radius_msg *msg, static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) { - struct wpa_supplicant *wpa_s = (struct wpa_supplicant *) eloop_ctx; + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; RadiusType msg_type = (RadiusType) sock_ctx; - int len, i; + int len, i, roundtrip; unsigned char buf[3000]; struct radius_msg *msg; struct radius_rx_handler *handlers; size_t num_handlers; struct radius_msg_list *req, *prev_req; + struct timeval tv; + struct hostapd_radius_server *rconf; + int invalid_authenticator = 0; - len = recv(sock, buf, sizeof(buf), 0); + if (msg_type == RADIUS_ACCT) { + handlers = radius->acct_handlers; + num_handlers = radius->num_acct_handlers; + rconf = conf->acct_server; + } else { + handlers = radius->auth_handlers; + num_handlers = radius->num_auth_handlers; + rconf = conf->auth_server; + } + + len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); if (len < 0) { perror("recv[RADIUS]"); return; } - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Received %d bytes from RADIUS server\n", len); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " + "server", len); if (len == sizeof(buf)) { printf("Possibly too long UDP frame for our buffer - " "dropping it\n"); @@ -379,24 +536,32 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) msg = radius_msg_parse(buf, len); if (msg == NULL) { printf("Parsing incoming RADIUS frame failed\n"); + rconf->malformed_responses++; return; } - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Received RADIUS message\n"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); + if (conf->msg_dumps) radius_msg_dump(msg); - if (msg_type == RADIUS_ACCT) { - handlers = wpa_s->radius->acct_handlers; - num_handlers = wpa_s->radius->num_acct_handlers; - } else { - handlers = wpa_s->radius->auth_handlers; - num_handlers = wpa_s->radius->num_auth_handlers; + switch (msg->hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + rconf->access_accepts++; + break; + case RADIUS_CODE_ACCESS_REJECT: + rconf->access_rejects++; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + rconf->access_challenges++; + break; + case RADIUS_CODE_ACCOUNTING_RESPONSE: + rconf->responses++; + break; } prev_req = NULL; - req = wpa_s->radius->msgs; + req = radius->msgs; while (req) { /* TODO: also match by src addr:port of the packet when using * alternative RADIUS servers (?) */ @@ -411,24 +576,34 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) } if (req == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "No matching RADIUS request found (type=%d " - "id=%d) - dropping packet\n", - msg_type, msg->hdr->identifier); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "No matching RADIUS request found (type=%d " + "id=%d) - dropping packet", + msg_type, msg->hdr->identifier); goto fail; } + gettimeofday(&tv, NULL); + roundtrip = (tv.tv_sec - req->last_attempt.tv_sec) * 100 + + (tv.tv_usec - req->last_attempt.tv_usec) / 10000; + hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Received RADIUS packet matched with a pending " + "request, round trip time %d.%02d sec", + roundtrip / 100, roundtrip % 100); + rconf->round_trip_time = roundtrip; + /* Remove ACKed RADIUS packet from retransmit list */ if (prev_req) prev_req->next = req->next; else - wpa_s->radius->msgs = req->next; - wpa_s->radius->num_msgs--; + radius->msgs = req->next; + radius->num_msgs--; for (i = 0; i < num_handlers; i++) { RadiusRxResult res; - res = handlers[i].handler(wpa_s, msg, req->msg, - req->shared_secret, + res = handlers[i].handler(msg, req->msg, req->shared_secret, req->shared_secret_len, handlers[i].data); switch (res) { @@ -439,14 +614,25 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) case RADIUS_RX_QUEUED: radius_client_msg_free(req); return; + case RADIUS_RX_INVALID_AUTHENTICATOR: + invalid_authenticator++; + /* continue */ case RADIUS_RX_UNKNOWN: /* continue with next handler */ break; } } - printf("No RADIUS RX handler found (type=%d code=%d id=%d) - dropping " - "packet\n", msg_type, msg->hdr->code, msg->hdr->identifier); + if (invalid_authenticator) + rconf->bad_authenticators++; + else + rconf->unknown_types++; + hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " + "(type=%d code=%d id=%d)%s - dropping packet", + msg_type, msg->hdr->code, msg->hdr->identifier, + invalid_authenticator ? " [INVALID AUTHENTICATOR]" : + ""); radius_client_msg_free(req); fail: @@ -455,24 +641,26 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) } -u8 radius_client_get_id(struct wpa_supplicant *wpa_s) +u8 radius_client_get_id(struct radius_client_data *radius) { struct radius_msg_list *entry, *prev, *remove; - u8 id = wpa_s->radius->next_radius_identifier++; + u8 id = radius->next_radius_identifier++; /* remove entries with matching id from retransmit list to avoid * using new reply from the RADIUS server with an old request */ - entry = wpa_s->radius->msgs; + entry = radius->msgs; prev = NULL; while (entry) { if (entry->msg->hdr->identifier == id) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Removing pending RADIUS message, since " - "its id (%d) is reused\n", id); + hostapd_logger(radius->ctx, entry->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing pending RADIUS message, " + "since its id (%d) is reused", id); if (prev) prev->next = entry->next; else - wpa_s->radius->msgs = entry->next; + radius->msgs = entry->next; remove = entry; } else remove = NULL; @@ -487,18 +675,18 @@ u8 radius_client_get_id(struct wpa_supplicant *wpa_s) } -void radius_client_flush(struct wpa_supplicant *wpa_s) +void radius_client_flush(struct radius_client_data *radius) { struct radius_msg_list *entry, *prev; - if (!wpa_s->radius) + if (!radius) return; - eloop_cancel_timeout(radius_client_timer, wpa_s, NULL); + eloop_cancel_timeout(radius_client_timer, radius, NULL); - entry = wpa_s->radius->msgs; - wpa_s->radius->msgs = NULL; - wpa_s->radius->num_msgs = 0; + entry = radius->msgs; + radius->msgs = NULL; + radius->num_msgs = 0; while (entry) { prev = entry; entry = entry->next; @@ -508,16 +696,26 @@ void radius_client_flush(struct wpa_supplicant *wpa_s) static int -radius_change_server(struct wpa_supplicant *wpa_s, struct hostapd_radius_server *nserv, +radius_change_server(struct radius_client_data *radius, + struct hostapd_radius_server *nserv, struct hostapd_radius_server *oserv, - int sock, int auth) + int sock, int sock6, int auth) { struct sockaddr_in serv; - - hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, +#ifdef CONFIG_IPV6 + struct sockaddr_in6 serv6; +#endif /* CONFIG_IPV6 */ + struct sockaddr *addr; + socklen_t addrlen; + char abuf[50]; + int sel_sock; + + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "%s server %s:%d", auth ? "Authentication" : "Accounting", - inet_ntoa(nserv->addr), nserv->port); + hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), + nserv->port); if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || memcmp(nserv->shared_secret, oserv->shared_secret, @@ -527,11 +725,11 @@ radius_change_server(struct wpa_supplicant *wpa_s, struct hostapd_radius_server * update all message authenticators and * User-Passwords, etc. and retry with new server. For * now, just drop all pending packets. */ - radius_client_flush(wpa_s); + radius_client_flush(radius); } else { /* Reset retry counters for the new server */ struct radius_msg_list *entry; - entry = wpa_s->radius->msgs; + entry = radius->msgs; while (entry) { entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; @@ -539,136 +737,379 @@ radius_change_server(struct wpa_supplicant *wpa_s, struct hostapd_radius_server entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; entry = entry->next; } - if (wpa_s->radius->msgs) { - eloop_cancel_timeout(radius_client_timer, wpa_s, NULL); + if (radius->msgs) { + eloop_cancel_timeout(radius_client_timer, radius, + NULL); eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, - radius_client_timer, wpa_s, + radius_client_timer, radius, NULL); } } - memset(&serv, 0, sizeof(serv)); - serv.sin_family = AF_INET; - serv.sin_addr.s_addr = nserv->addr.s_addr; - serv.sin_port = htons(nserv->port); + switch (nserv->addr.af) { + case AF_INET: + memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; + serv.sin_port = htons(nserv->port); + addr = (struct sockaddr *) &serv; + addrlen = sizeof(serv); + sel_sock = sock; + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + memset(&serv6, 0, sizeof(serv6)); + serv6.sin6_family = AF_INET6; + memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, + sizeof(struct in6_addr)); + serv6.sin6_port = htons(nserv->port); + addr = (struct sockaddr *) &serv6; + addrlen = sizeof(serv6); + sel_sock = sock6; + break; +#endif /* CONFIG_IPV6 */ + default: + return -1; + } - if (connect(sock, (struct sockaddr *) &serv, sizeof(serv)) < 0) { + if (connect(sel_sock, addr, addrlen) < 0) { perror("connect[radius]"); return -1; } + if (auth) + radius->auth_sock = sel_sock; + else + radius->acct_sock = sel_sock; + return 0; } static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) { - struct wpa_supplicant *wpa_s = eloop_ctx; + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; struct hostapd_radius_server *oserv; - if (wpa_s->radius->auth_serv_sock >= 0 && wpa_s->auth_servers && - wpa_s->auth_server != wpa_s->auth_servers) { - oserv = wpa_s->auth_server; - wpa_s->auth_server = wpa_s->auth_servers; - radius_change_server(wpa_s, wpa_s->auth_server, oserv, - wpa_s->radius->auth_serv_sock, 1); + if (radius->auth_sock >= 0 && conf->auth_servers && + conf->auth_server != conf->auth_servers) { + oserv = conf->auth_server; + conf->auth_server = conf->auth_servers; + radius_change_server(radius, conf->auth_server, oserv, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); } - if (wpa_s->radius->acct_serv_sock >= 0 && wpa_s->acct_servers && - wpa_s->acct_server != wpa_s->acct_servers) { - oserv = wpa_s->acct_server; - wpa_s->acct_server = wpa_s->acct_servers; - radius_change_server(wpa_s, wpa_s->acct_server, oserv, - wpa_s->radius->acct_serv_sock, 0); + if (radius->acct_sock >= 0 && conf->acct_servers && + conf->acct_server != conf->acct_servers) { + oserv = conf->acct_server; + conf->acct_server = conf->acct_servers; + radius_change_server(radius, conf->acct_server, oserv, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); } - if (wpa_s->radius_retry_primary_interval) - eloop_register_timeout(wpa_s-> - radius_retry_primary_interval, 0, - radius_retry_primary_timer, wpa_s, NULL); + if (conf->retry_primary_interval) + eloop_register_timeout(conf->retry_primary_interval, 0, + radius_retry_primary_timer, radius, + NULL); } -static int radius_client_init_auth(struct wpa_supplicant *wpa_s) +static int radius_client_init_auth(struct radius_client_data *radius) { - wpa_s->radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (wpa_s->radius->auth_serv_sock < 0) { + struct hostapd_radius_servers *conf = radius->conf; + int ok = 0; + + radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (radius->auth_serv_sock < 0) perror("socket[PF_INET,SOCK_DGRAM]"); + else + ok++; + +#ifdef CONFIG_IPV6 + radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); + if (radius->auth_serv_sock6 < 0) + perror("socket[PF_INET6,SOCK_DGRAM]"); + else + ok++; +#endif /* CONFIG_IPV6 */ + + if (ok == 0) return -1; - } - radius_change_server(wpa_s, wpa_s->auth_server, NULL, - wpa_s->radius->auth_serv_sock, 1); + radius_change_server(radius, conf->auth_server, NULL, + radius->auth_serv_sock, radius->auth_serv_sock6, + 1); + + if (radius->auth_serv_sock >= 0 && + eloop_register_read_sock(radius->auth_serv_sock, + radius_client_receive, radius, + (void *) RADIUS_AUTH)) { + printf("Could not register read socket for authentication " + "server\n"); + return -1; + } - if (eloop_register_read_sock(wpa_s->radius->auth_serv_sock, - radius_client_receive, wpa_s, +#ifdef CONFIG_IPV6 + if (radius->auth_serv_sock6 >= 0 && + eloop_register_read_sock(radius->auth_serv_sock6, + radius_client_receive, radius, (void *) RADIUS_AUTH)) { printf("Could not register read socket for authentication " "server\n"); return -1; } +#endif /* CONFIG_IPV6 */ return 0; } -static int radius_client_init_acct(struct wpa_supplicant *wpa_s) +static int radius_client_init_acct(struct radius_client_data *radius) { - wpa_s->radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (wpa_s->radius->acct_serv_sock < 0) { + struct hostapd_radius_servers *conf = radius->conf; + int ok = 0; + + radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (radius->acct_serv_sock < 0) perror("socket[PF_INET,SOCK_DGRAM]"); + else + ok++; + + radius_change_server(radius, conf->acct_server, NULL, + radius->acct_serv_sock, radius->acct_serv_sock6, + 0); + + if (radius->acct_serv_sock >= 0 && + eloop_register_read_sock(radius->acct_serv_sock, + radius_client_receive, radius, + (void *) RADIUS_ACCT)) { + printf("Could not register read socket for accounting " + "server\n"); return -1; } - radius_change_server(wpa_s, wpa_s->acct_server, NULL, - wpa_s->radius->acct_serv_sock, 0); - - if (eloop_register_read_sock(wpa_s->radius->acct_serv_sock, - radius_client_receive, wpa_s, +#ifdef CONFIG_IPV6 + if (radius->acct_serv_sock6 >= 0 && + eloop_register_read_sock(radius->acct_serv_sock6, + radius_client_receive, radius, (void *) RADIUS_ACCT)) { printf("Could not register read socket for accounting " "server\n"); return -1; } +#endif /* CONFIG_IPV6 */ return 0; } -int radius_client_init(struct wpa_supplicant *wpa_s) +struct radius_client_data * +radius_client_init(void *ctx, struct hostapd_radius_servers *conf) { - wpa_s->radius = malloc(sizeof(struct radius_client_data)); - if (wpa_s->radius == NULL) - return -1; - - memset(wpa_s->radius, 0, sizeof(struct radius_client_data)); - wpa_s->radius->auth_serv_sock = wpa_s->radius->acct_serv_sock = -1; - - if (wpa_s->auth_server && radius_client_init_auth(wpa_s)) - return -1; + struct radius_client_data *radius; + + radius = malloc(sizeof(struct radius_client_data)); + if (radius == NULL) + return NULL; + + memset(radius, 0, sizeof(struct radius_client_data)); + radius->ctx = ctx; + radius->conf = conf; + radius->auth_serv_sock = radius->acct_serv_sock = + radius->auth_serv_sock6 = radius->acct_serv_sock6 = + radius->auth_sock = radius->acct_sock = -1; + + if (conf->auth_server && radius_client_init_auth(radius)) { + radius_client_deinit(radius); + return NULL; + } - if (wpa_s->acct_server && radius_client_init_acct(wpa_s)) - return -1; + if (conf->acct_server && radius_client_init_acct(radius)) { + radius_client_deinit(radius); + return NULL; + } - if (wpa_s->radius_retry_primary_interval) - eloop_register_timeout(wpa_s->radius_retry_primary_interval, 0, - radius_retry_primary_timer, wpa_s, + if (conf->retry_primary_interval) + eloop_register_timeout(conf->retry_primary_interval, 0, + radius_retry_primary_timer, radius, NULL); - return 0; + return radius; } -void radius_client_deinit(struct wpa_supplicant *wpa_s) +void radius_client_deinit(struct radius_client_data *radius) { - if (!wpa_s->radius) + if (!radius) return; - eloop_cancel_timeout(radius_retry_primary_timer, wpa_s, NULL); + eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); + + radius_client_flush(radius); + free(radius->auth_handlers); + free(radius->acct_handlers); + free(radius); +} + + +void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr) +{ + struct radius_msg_list *entry, *prev, *tmp; + + prev = NULL; + entry = radius->msgs; + while (entry) { + if (entry->msg_type == RADIUS_AUTH && + memcmp(entry->addr, addr, ETH_ALEN) == 0) { + hostapd_logger(radius->ctx, addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing pending RADIUS authentication" + " message for removed client"); + + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + + tmp = entry; + entry = entry->next; + radius_client_msg_free(tmp); + radius->num_msgs--; + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +static int radius_client_dump_auth_server(char *buf, size_t buflen, + struct hostapd_radius_server *serv, + struct radius_client_data *cli) +{ + int pending = 0; + struct radius_msg_list *msg; + char abuf[50]; + + if (cli) { + for (msg = cli->msgs; msg; msg = msg->next) { + if (msg->msg_type == RADIUS_AUTH) + pending++; + } + } + + return snprintf(buf, buflen, + "radiusAuthServerIndex=%d\n" + "radiusAuthServerAddress=%s\n" + "radiusAuthClientServerPortNumber=%d\n" + "radiusAuthClientRoundTripTime=%d\n" + "radiusAuthClientAccessRequests=%u\n" + "radiusAuthClientAccessRetransmissions=%u\n" + "radiusAuthClientAccessAccepts=%u\n" + "radiusAuthClientAccessRejects=%u\n" + "radiusAuthClientAccessChallenges=%u\n" + "radiusAuthClientMalformedAccessResponses=%u\n" + "radiusAuthClientBadAuthenticators=%u\n" + "radiusAuthClientPendingRequests=%u\n" + "radiusAuthClientTimeouts=%u\n" + "radiusAuthClientUnknownTypes=%u\n" + "radiusAuthClientPacketsDropped=%u\n", + serv->index, + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), + serv->port, + serv->round_trip_time, + serv->requests, + serv->retransmissions, + serv->access_accepts, + serv->access_rejects, + serv->access_challenges, + serv->malformed_responses, + serv->bad_authenticators, + pending, + serv->timeouts, + serv->unknown_types, + serv->packets_dropped); +} + + +static int radius_client_dump_acct_server(char *buf, size_t buflen, + struct hostapd_radius_server *serv, + struct radius_client_data *cli) +{ + int pending = 0; + struct radius_msg_list *msg; + char abuf[50]; + + if (cli) { + for (msg = cli->msgs; msg; msg = msg->next) { + if (msg->msg_type == RADIUS_ACCT || + msg->msg_type == RADIUS_ACCT_INTERIM) + pending++; + } + } + + return snprintf(buf, buflen, + "radiusAccServerIndex=%d\n" + "radiusAccServerAddress=%s\n" + "radiusAccClientServerPortNumber=%d\n" + "radiusAccClientRoundTripTime=%d\n" + "radiusAccClientRequests=%u\n" + "radiusAccClientRetransmissions=%u\n" + "radiusAccClientResponses=%u\n" + "radiusAccClientMalformedResponses=%u\n" + "radiusAccClientBadAuthenticators=%u\n" + "radiusAccClientPendingRequests=%u\n" + "radiusAccClientTimeouts=%u\n" + "radiusAccClientUnknownTypes=%u\n" + "radiusAccClientPacketsDropped=%u\n", + serv->index, + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), + serv->port, + serv->round_trip_time, + serv->requests, + serv->retransmissions, + serv->responses, + serv->malformed_responses, + serv->bad_authenticators, + pending, + serv->timeouts, + serv->unknown_types, + serv->packets_dropped); +} + + +int radius_client_get_mib(struct radius_client_data *radius, char *buf, + size_t buflen) +{ + struct hostapd_radius_servers *conf = radius->conf; + int i; + struct hostapd_radius_server *serv; + int count = 0; + + if (conf->auth_servers) { + for (i = 0; i < conf->num_auth_servers; i++) { + serv = &conf->auth_servers[i]; + count += radius_client_dump_auth_server( + buf + count, buflen - count, serv, + serv == conf->auth_server ? + radius : NULL); + } + } + + if (conf->acct_servers) { + for (i = 0; i < conf->num_acct_servers; i++) { + serv = &conf->acct_servers[i]; + count += radius_client_dump_acct_server( + buf + count, buflen - count, serv, + serv == conf->acct_server ? + radius : NULL); + } + } - radius_client_flush(wpa_s); - free(wpa_s->radius->auth_handlers); - free(wpa_s->radius->acct_handlers); - free(wpa_s->radius); - wpa_s->radius = NULL; + return count; } diff --git a/contrib/wpa_supplicant/radius_client.h b/contrib/wpa_supplicant/radius_client.h index 993f8d0..d21ca83 100644 --- a/contrib/wpa_supplicant/radius_client.h +++ b/contrib/wpa_supplicant/radius_client.h @@ -1,81 +1,87 @@ #ifndef RADIUS_CLIENT_H #define RADIUS_CLIENT_H -typedef enum { - RADIUS_AUTH, - RADIUS_ACCT, - RADIUS_ACCT_INTERIM /* used only with radius_client_send(); just like - * RADIUS_ACCT, but removes any pending interim - * RADIUS Accounting packages for the same STA - * before sending the new interim update */ -} RadiusType; +#include "config_types.h" -/* RADIUS message retransmit list */ -struct radius_msg_list { - u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages - * for the same STA. */ - struct radius_msg *msg; - RadiusType msg_type; - time_t first_try; - time_t next_try; - int attempts; - int next_wait; +struct radius_msg; +struct hostapd_radius_server { + /* MIB prefix for shared variables: + * @ = radiusAuth or radiusAcc depending on the type of the server */ + struct hostapd_ip_addr addr; /* @ServerAddress */ + int port; /* @ClientServerPortNumber */ u8 *shared_secret; size_t shared_secret_len; - /* TODO: server config with failover to backup server(s) */ - - struct radius_msg_list *next; + /* Dynamic (not from configuration file) MIB data */ + int index; /* @ServerIndex */ + int round_trip_time; /* @ClientRoundTripTime; in hundredths of a + * second */ + u32 requests; /* @Client{Access,}Requests */ + u32 retransmissions; /* @Client{Access,}Retransmissions */ + u32 access_accepts; /* radiusAuthClientAccessAccepts */ + u32 access_rejects; /* radiusAuthClientAccessRejects */ + u32 access_challenges; /* radiusAuthClientAccessChallenges */ + u32 responses; /* radiusAccClientResponses */ + u32 malformed_responses; /* @ClientMalformed{Access,}Responses */ + u32 bad_authenticators; /* @ClientBadAuthenticators */ + u32 timeouts; /* @ClientTimeouts */ + u32 unknown_types; /* @ClientUnknownTypes */ + u32 packets_dropped; /* @ClientPacketsDropped */ + /* @ClientPendingRequests: length of hapd->radius->msgs for matching + * msg_type */ }; +struct hostapd_radius_servers { + /* RADIUS Authentication and Accounting servers in priority order */ + struct hostapd_radius_server *auth_servers, *auth_server; + int num_auth_servers; + struct hostapd_radius_server *acct_servers, *acct_server; + int num_acct_servers; -typedef enum { - RADIUS_RX_PROCESSED, - RADIUS_RX_QUEUED, - RADIUS_RX_UNKNOWN -} RadiusRxResult; + int retry_primary_interval; + int acct_interim_interval; -struct radius_rx_handler { - RadiusRxResult (*handler)(struct wpa_supplicant *wpa_s, - struct radius_msg *msg, - struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, - void *data); - void *data; + int msg_dumps; }; -struct radius_client_data { - int auth_serv_sock; /* socket for authentication RADIUS messages */ - int acct_serv_sock; /* socket for accounting RADIUS messages */ - - struct radius_rx_handler *auth_handlers; - size_t num_auth_handlers; - struct radius_rx_handler *acct_handlers; - size_t num_acct_handlers; - struct radius_msg_list *msgs; - size_t num_msgs; +typedef enum { + RADIUS_AUTH, + RADIUS_ACCT, + RADIUS_ACCT_INTERIM /* used only with radius_client_send(); just like + * RADIUS_ACCT, but removes any pending interim + * RADIUS Accounting packages for the same STA + * before sending the new interim update */ +} RadiusType; - u8 next_radius_identifier; - u32 acct_session_id_hi; - u32 acct_session_id_lo; -}; +typedef enum { + RADIUS_RX_PROCESSED, + RADIUS_RX_QUEUED, + RADIUS_RX_UNKNOWN, + RADIUS_RX_INVALID_AUTHENTICATOR +} RadiusRxResult; +struct radius_client_data; -int radius_client_register(struct wpa_supplicant *wpa_s, RadiusType msg_type, +int radius_client_register(struct radius_client_data *radius, + RadiusType msg_type, RadiusRxResult (*handler) - (struct wpa_supplicant *wpa_s, - struct radius_msg *msg, struct radius_msg *req, + (struct radius_msg *msg, struct radius_msg *req, u8 *shared_secret, size_t shared_secret_len, void *data), void *data); -int radius_client_send(struct wpa_supplicant *wpa_s, struct radius_msg *msg, +int radius_client_send(struct radius_client_data *radius, + struct radius_msg *msg, RadiusType msg_type, u8 *addr); -u8 radius_client_get_id(struct wpa_supplicant *wpa_s); - -void radius_client_flush(struct wpa_supplicant *wpa_s); -int radius_client_init(struct wpa_supplicant *wpa_s); -void radius_client_deinit(struct wpa_supplicant *wpa_s); +u8 radius_client_get_id(struct radius_client_data *radius); + +void radius_client_flush(struct radius_client_data *radius); +struct radius_client_data * +radius_client_init(void *ctx, struct hostapd_radius_servers *conf); +void radius_client_deinit(struct radius_client_data *radius); +void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr); +int radius_client_get_mib(struct radius_client_data *radius, char *buf, + size_t buflen); #endif /* RADIUS_CLIENT_H */ diff --git a/contrib/wpa_supplicant/rc4.c b/contrib/wpa_supplicant/rc4.c index 97ec1b0..4cf14d9 100644 --- a/contrib/wpa_supplicant/rc4.c +++ b/contrib/wpa_supplicant/rc4.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / RC4 - * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -19,7 +18,20 @@ #define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) -void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) +/** + * rc4 - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @skip: number of bytes to skip from the beginning of the RC4 stream + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ +void rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) { u32 i, j, k; u8 S[256], *pos; @@ -57,7 +69,17 @@ void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) } -void rc4(u8 *buf, size_t len, u8 *key, size_t key_len) +/** + * rc4 - XOR RC4 stream to given data + * @buf: data to be XOR'ed with RC4 stream + * @len: buf length + * @key: RC4 key + * @key_len: RC4 key length + * + * Generate RC4 pseudo random stream for the given key and XOR this with the + * data buffer to perform RC4 encryption/decryption. + */ +void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len) { rc4_skip(key, key_len, 0, buf, len); } diff --git a/contrib/wpa_supplicant/rc4.h b/contrib/wpa_supplicant/rc4.h index 0e77b7e..3873240 100644 --- a/contrib/wpa_supplicant/rc4.h +++ b/contrib/wpa_supplicant/rc4.h @@ -1,7 +1,22 @@ +/* + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.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 RC4_H #define RC4_H -void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); -void rc4(u8 *buf, size_t len, u8 *key, size_t key_len); +void rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len); +void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len); #endif /* RC4_H */ diff --git a/contrib/wpa_supplicant/sha1.c b/contrib/wpa_supplicant/sha1.c index 04943b5..7e32e31 100644 --- a/contrib/wpa_supplicant/sha1.c +++ b/contrib/wpa_supplicant/sha1.c @@ -19,36 +19,38 @@ #include "common.h" #include "sha1.h" #include "md5.h" +#include "crypto.h" -void sha1_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac) -{ - SHA1_CTX context; - SHA1Init(&context); - SHA1Update(&context, key, key_len); - SHA1Update(&context, data, data_len); - SHA1Update(&context, key, key_len); - SHA1Final(mac, &context); -} - - -/* HMAC code is based on RFC 2104 */ +/** + * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (20 bytes) + */ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - SHA1_CTX context; - unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */ - unsigned char k_opad[65]; /* outer padding - key XORd with opad */ + unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[20]; int i; + const u8 *_addr[6]; + size_t _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } /* if key is longer than 64 bytes reset it to key = SHA1(key) */ if (key_len > 64) { - SHA1Init(&context); - SHA1Update(&context, key, key_len); - SHA1Final(tk, &context); - + sha1_vector(1, &key, &key_len, tk); key = tk; key_len = 20; } @@ -62,35 +64,45 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ - /* start out by storing key in pads */ - memset(k_ipad, 0, sizeof(k_ipad)); - memset(k_opad, 0, sizeof(k_opad)); - memcpy(k_ipad, key, key_len); - memcpy(k_opad, key, key_len); - - /* XOR key with ipad and opad values */ - for (i = 0; i < 64; i++) { - k_ipad[i] ^= 0x36; - k_opad[i] ^= 0x5c; - } + /* start out by storing key in ipad */ + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; /* perform inner SHA1 */ - SHA1Init(&context); /* init context for 1st pass */ - SHA1Update(&context, k_ipad, 64); /* start with inner pad */ - /* then text of datagram; all fragments */ + _addr[0] = k_pad; + _len[0] = 64; for (i = 0; i < num_elem; i++) { - SHA1Update(&context, addr[i], len[i]); + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; } - SHA1Final(mac, &context); /* finish up 1st pass */ + sha1_vector(1 + num_elem, _addr, _len, mac); + + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; /* perform outer SHA1 */ - SHA1Init(&context); /* init context for 2nd pass */ - SHA1Update(&context, k_opad, 64); /* start with outer pad */ - SHA1Update(&context, mac, 20); /* then results of 1st hash */ - SHA1Final(mac, &context); /* finish up 2nd pass */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = SHA1_MAC_LEN; + sha1_vector(2, _addr, _len, mac); } +/** + * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + */ void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { @@ -98,6 +110,19 @@ void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, } +/** + * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., PMK in IEEE 802.11i). + */ void sha1_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { @@ -135,7 +160,20 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label, } -/* draft-cam-winget-eap-fast-00.txt */ +/** + * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key for EAP-FAST. T-PRF is defined in + * draft-cam-winget-eap-fast-02.txt, Appendix B. + */ void sha1_t_prf(const u8 *key, size_t key_len, const char *label, const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) { @@ -177,7 +215,19 @@ void sha1_t_prf(const u8 *key, size_t key_len, const char *label, } -/* RFC 2246 */ +/** + * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) + * @secret: Key for PRF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate +* + * This function is used to derive new, cryptographically separate keys from a + * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. + */ int tls_prf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { @@ -285,6 +335,19 @@ static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, } +/** + * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * @passphrase: ASCII passphrase + * @ssid: SSID + * @ssid_len: SSID length in bytes + * @interations: Number of iterations to run + * @buf: Buffer for the generated key + * @buflen: Length of the buffer in bytes + * + * This function is used to derive PSK for WPA-PSK. For this protocol, + * iterations is set to 4096 and buflen to 32. This function is described in + * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. + */ void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { @@ -305,20 +368,27 @@ void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, } -void sha1_transform(u8 *state, u8 data[64]) -{ -#ifdef EAP_TLS_FUNCS - SHA_CTX context; - memset(&context, 0, sizeof(context)); - memcpy(&context.h0, state, 5 * 4); - SHA1_Transform(&context, data); - memcpy(state, &context.h0, 5 * 4); -#else /* EAP_TLS_FUNCS */ - SHA1Transform((u32 *) state, data); -#endif /* EAP_TLS_FUNCS */ -} +#ifndef EAP_TLS_FUNCS + +typedef struct { + u32 state[5]; + u32 count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +static void SHA1Init(SHA1_CTX *context); +static void SHA1Update(SHA1_CTX *context, const void *data, u32 len); +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context); +static void SHA1Transform(u32 state[5], const unsigned char buffer[64]); +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { @@ -332,7 +402,21 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, } -#ifndef EAP_TLS_FUNCS +/** + * sha1_transform - Perform one SHA-1 transform step + * @state: SHA-1 state + * @data: Input data for the SHA-1 transform + * + * This function is used to implement random number generation specified in + * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is + * similar to SHA-1, but has different message padding and as such, access to + * just part of the SHA-1 is needed. + */ +void sha1_transform(u8 *state, const u8 data[64]) +{ + SHA1Transform((u32 *) state, data); +} + /* ===== start - public domain SHA1 implementation ===== */ @@ -462,7 +546,7 @@ void SHAPrintContext(SHA1_CTX *context, char *msg) /* Hash a single 512-bit block. This is the core of the algorithm. */ -void SHA1Transform(u32 state[5], const unsigned char buffer[64]) +static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) { u32 a, b, c, d, e; typedef union { @@ -520,7 +604,7 @@ void SHA1Transform(u32 state[5], const unsigned char buffer[64]) /* SHA1Init - Initialize new context */ -void SHA1Init(SHA1_CTX* context) +static void SHA1Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; @@ -534,7 +618,7 @@ void SHA1Init(SHA1_CTX* context) /* Run your data through this. */ -void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) +static void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) { u32 i, j; const unsigned char *data = _data; @@ -564,7 +648,7 @@ void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) /* Add padding and return the message digest. */ -void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { u32 i; unsigned char finalcount[8]; diff --git a/contrib/wpa_supplicant/sha1.h b/contrib/wpa_supplicant/sha1.h index 186e3c1..3c6d915 100644 --- a/contrib/wpa_supplicant/sha1.h +++ b/contrib/wpa_supplicant/sha1.h @@ -1,36 +1,22 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 SHA1_H #define SHA1_H -#ifdef EAP_TLS_FUNCS - -#include <openssl/sha.h> - -#define SHA1_CTX SHA_CTX -#define SHA1Init SHA1_Init -#define SHA1Update SHA1_Update -#define SHA1Final SHA1_Final -#define SHA1Transform SHA1_Transform -#define SHA1_MAC_LEN SHA_DIGEST_LENGTH - -#else /* EAP_TLS_FUNCS */ - #define SHA1_MAC_LEN 20 -typedef struct { - u32 state[5]; - u32 count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -void SHA1Init(SHA1_CTX *context); -void SHA1Update(SHA1_CTX *context, const void *data, u32 len); -void SHA1Final(unsigned char digest[20], SHA1_CTX* context); -void SHA1Transform(u32 state[5], const unsigned char buffer[64]); - -#endif /* EAP_TLS_FUNCS */ - -void sha1_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac); void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, @@ -43,8 +29,5 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen); -void sha1_transform(u8 *state, u8 data[64]); -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac); #endif /* SHA1_H */ diff --git a/contrib/wpa_supplicant/tls.h b/contrib/wpa_supplicant/tls.h index 99c9b33..a6a8110 100644 --- a/contrib/wpa_supplicant/tls.h +++ b/contrib/wpa_supplicant/tls.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / SSL/TLS interface definition + * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.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 TLS_H #define TLS_H @@ -10,28 +24,115 @@ struct tls_keys { size_t client_random_len; const u8 *server_random; size_t server_random_len; + + /* + * If TLS library does not provide access to master_key, but only to + * EAP key block, this pointer can be set to point to the result of + * PRF(master_secret, "client EAP encryption", + * client_random + server_random). + */ + const u8 *eap_tls_prf; + size_t eap_tls_prf_len; +}; + +struct tls_config { + const char *opensc_engine_path; + const char *pkcs11_engine_path; + const char *pkcs11_module_path; }; /** - * tls_init - initialize TLS library - * - * Returns: Context data to be used as @tls_ctx in calls to other functions, + * struct tls_connection_params - Parameters for TLS connection + * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER + * format + * @ca_cert_blob: ca_cert as inlined data or %NULL if not used + * @ca_cert_blob_len: ca_cert_blob length + * @ca_path: Path to CA certificates (OpenSSL specific) + * @subject_match: String to match in the subject of the peer certificate or + * %NULL to allow all subjects + * @altsubject_match: String to match in the alternative subject of the peer + * certificate or %NULL to allow all alternative subjects + * @client_cert: File or reference name for client X.509 certificate in PEM or + * DER format + * @client_cert_blob: client_cert as inlined data or %NULL if not used + * @client_cert_blob_len: client_cert_blob length + * @private_key: File or reference name for client private key in PEM or DER + * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used + * @dh_blob: dh_file as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * @engine: 1 = use engine (e.g., a smartcard) for private key operations + * (this is OpenSSL specific for now) + * @engine_id: engine id string (this is OpenSSL specific for now) + * @ppin: pointer to the pin variable in the configuration + * (this is OpenSSL specific for now) + * @key_id: the private key's key id (this is OpenSSL specific for now) + * + * TLS connection parameters to be configured with tls_connection_set_params(). + * + * Certificates and private key can be configured either as a reference name + * (file path or reference to certificate store) or by providing the same data + * as a pointer to the data in memory. Only one option will be used for each + * field. + */ +struct tls_connection_params { + const char *ca_cert; + const u8 *ca_cert_blob; + size_t ca_cert_blob_len; + const char *ca_path; + const char *subject_match; + const char *altsubject_match; + const char *client_cert; + const u8 *client_cert_blob; + size_t client_cert_blob_len; + const char *private_key; + const u8 *private_key_blob; + size_t private_key_blob_len; + const char *private_key_passwd; + const char *dh_file; + const u8 *dh_blob; + size_t dh_blob_len; + + /* OpenSSL specific variables */ + int engine; + const char *engine_id; + const char *pin; + const char *key_id; +}; + + +/** + * tls_init - Initialize TLS library + * @conf: Configuration data for TLS library + * Returns: Context data to be used as tls_ctx in calls to other functions, * or %NULL on failure. * - * Called once during program startup. + * Called once during program startup and once for each RSN pre-authentication + * session. In other words, there can be two concurrent TLS contexts. If global + * library initialization is needed (i.e., one that is shared between both + * authentication types), the TLS library wrapper should maintain a reference + * counter and do global initialization only when moving from 0 to 1 reference. */ -void * tls_init(void); +void * tls_init(const struct tls_config *conf); /** - * tls_deinit - deinitialize TLS library + * tls_deinit - Deinitialize TLS library * @tls_ctx: TLS context data from tls_init() * - * Called once during program shutdown. + * Called once during program shutdown and once for each RSN pre-authentication + * session. If global library deinitialization is needed (i.e., one that is + * shared between both authentication types), the TLS library wrapper should + * maintain a reference counter and do global deinitialization only when moving + * from 1 to 0 references. */ void tls_deinit(void *tls_ctx); /** - * tls_get_errors - process pending errors + * tls_get_errors - Process pending errors * @tls_ctx: TLS context data from tls_init() * * Returns: Number of found error, 0 if no errors detected. @@ -41,15 +142,15 @@ void tls_deinit(void *tls_ctx); int tls_get_errors(void *tls_ctx); /** - * tls_connection_init - initialize a new TLS connection + * tls_connection_init - Initialize a new TLS connection * @tls_ctx: TLS context data from tls_init() * - * Returns: Connection context data, @conn for other function calls + * Returns: Connection context data, conn for other function calls */ struct tls_connection * tls_connection_init(void *tls_ctx); /** - * tls_connection_deinit - free TLS connection data + * tls_connection_deinit - Free TLS connection data * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -58,7 +159,7 @@ struct tls_connection * tls_connection_init(void *tls_ctx); void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); /** - * tls_connection_established - has the TLS connection been completed? + * tls_connection_established - Has the TLS connection been completed? * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -67,34 +168,40 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); int tls_connection_established(void *tls_ctx, struct tls_connection *conn); /** - * tls_connection_shutdown - shutdown TLS connection data. + * tls_connection_shutdown - Shutdown TLS connection data. * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * * Returns: 0 on success, -1 on failure * * Shutdown current TLS connection without releasing all resources. New - * connection can be started by using the same @conn without having to call + * connection can be started by using the same conn without having to call * tls_connection_init() or setting certificates etc. again. The new * connection should try to use session resumption. */ int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); +enum { + TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, + TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 +}; /** - * tls_connection_ca_cert - set trusted CA certificate for TLS connection + * tls_connection_set_params - Set TLS connection parameters * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * @ca_cert: File name for CA certificate in PEM or DER format - * @subject_match: String to match in the subject of the peer certificate or - * %NULL to allow all subjects + * @params: Connection parameters * - * Returns: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. */ -int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, - const char *ca_cert, const char *subject_match); +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params); /** - * tls_global_ca_cert - set trusted CA certificate for all TLS connections + * tls_global_ca_cert - Set trusted CA certificate for all TLS connections * @tls_ctx: TLS context data from tls_init() * @ca_cert: File name for CA certificate in PEM or DER format * %NULL to allow all subjects @@ -104,31 +211,28 @@ int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, int tls_global_ca_cert(void *tls_ctx, const char *ca_cert); /** - * tls_connection_ca_cert - set trusted CA certificate for TLS connection + * tls_global_set_verify - Set global certificate verification options * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @verify_peer: 1 = verify peer certificate - * @subject_match: String to match in the subject of the peer certificate or - * %NULL to allow all subjects + * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, + * 2 = verify CRL for all certificates * * Returns: 0 on success, -1 on failure */ -int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer, const char *subject_match); +int tls_global_set_verify(void *tls_ctx, int check_crl); /** - * tls_connection_client_cert - set client certificate for TLS connection + * tls_connection_set_verify - Set certificate verification options * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * @client_cert: File name for client certificate in PEM or DER format + * @verify_peer: 1 = verify peer certificate * * Returns: 0 on success, -1 on failure */ -int tls_connection_client_cert(void *tls_ctx, struct tls_connection *conn, - const char *client_cert); +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer); /** - * tls_global_client_cert - set client certificate for all TLS connections + * tls_global_client_cert - Set client certificate for all TLS connections * @tls_ctx: TLS context data from tls_init() * @client_cert: File name for client certificate in PEM or DER format * @@ -137,21 +241,7 @@ int tls_connection_client_cert(void *tls_ctx, struct tls_connection *conn, int tls_global_client_cert(void *tls_ctx, const char *client_cert); /** - * tls_connection_private_key - set private key for TLS connection - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @private_key: File name for client private key in PEM or DER format - * @private_key_passwd: Passphrase for decrypted private key, %NULL if no - * passphrase is used. - * - * Returns: 0 on success, -1 on failure - */ -int tls_connection_private_key(void *tls_ctx, struct tls_connection *conn, - const char *private_key, - const char *private_key_passwd); - -/** - * tls_global_private_key - set private key for all TLS connections + * tls_global_private_key - Set private key for all TLS connections * @tls_ctx: TLS context data from tls_init() * @private_key: File name for client private key in PEM or DER format * @private_key_passwd: Passphrase for decrypted private key, %NULL if no @@ -163,18 +253,7 @@ int tls_global_private_key(void *tls_ctx, const char *private_key, const char *private_key_passwd); /** - * tls_connection_dh - set DH/DSA parameters for TLS connection - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @dh_file: File name for DH/DSA data in PEM format. - * - * Returns: 0 on success, -1 on failure - */ -int tls_connection_dh(void *tls_ctx, struct tls_connection *conn, - const char *dh_file); - -/** - * tls_connection_get_keys - get master key and random data from TLS connection + * tls_connection_get_keys - Get master key and random data from TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @keys: Structure of key/random data (filled on success) @@ -185,23 +264,37 @@ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys); /** - * tls_connection_handshake - process TLS handshake (client side) + * tls_connection_handshake - Process TLS handshake (client side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Input data from TLS peer * @in_len: Input data length * @out_len: Length of the output buffer. * - * Returns: pointer to output data, %NULL on failure + * Returns: Pointer to output data, %NULL on failure * * Caller is responsible for freeing returned output data. + * + * This function is used during TLS handshake. The first call is done with + * in_data == %NULL and the library is expected to return ClientHello packet. + * This packet is then send to the server and a response from server is given + * to TLS library by calling this function again with in_data pointing to the + * TLS message from the server. + * + * If the TLS handshake fails, this function may return %NULL. However, if the + * TLS library has a TLS alert to send out, that should be returned as the + * output data. In this case, tls_connection_get_failed() must return failure + * (> 0). + * + * tls_connection_established() should return 1 once the TLS handshake has been + * completed successfully. */ u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const u8 *in_data, size_t in_len, size_t *out_len); /** - * tls_connection_servr_handshake - process TLS handshake (server side) + * tls_connection_server_handshake - Process TLS handshake (server side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Input data from TLS peer @@ -218,37 +311,43 @@ u8 * tls_connection_server_handshake(void *tls_ctx, size_t *out_len); /** - * tls_connection_encrypt - encrypt data into TLS tunnel + * tls_connection_encrypt - Encrypt data into TLS tunnel * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum @out_data length + * @out_len: Maximum out_data length + * + * Returns: Number of bytes written to out_data, -1 on failure * - * Returns: Number of bytes written to @out_data, -1 on failure + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. */ int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); /** - * tls_connection_decrypt - decrypt data from TLS tunnel + * tls_connection_decrypt - Decrypt data from TLS tunnel * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) - * @out_len: Maximum @out_data length + * @out_len: Maximum out_data length * - * Returns: Number of bytes written to @out_data, -1 on failure + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. */ int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); /** - * tls_connection_resumed - was session resumption used + * tls_connection_resumed - Was session resumption used * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -257,19 +356,19 @@ int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); /** - * tls_connection_set_master_key - configure master secret for TLS connection + * tls_connection_set_master_key - Configure master secret for TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @key: TLS pre-master-secret - * @key_len: length of @key in bytes + * @key_len: length of key in bytes * * Returns: 0 on success, -1 on failure */ -int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, +int tls_connection_set_master_key(void *tls_ctx, struct tls_connection *conn, const u8 *key, size_t key_len); /** - * tls_connection_set_anon_dh - configure TLS connection to use anonymous DH + * tls_connection_set_anon_dh - Configure TLS connection to use anonymous DH * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -278,22 +377,24 @@ int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, * TODO: consider changing this to more generic routine for configuring allowed * ciphers */ -int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn); +int tls_connection_set_anon_dh(void *tls_ctx, struct tls_connection *conn); /** - * tls_get_cipher - get current cipher name + * tls_get_cipher - Get current cipher name * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for the cipher name + * @buflen: buf size * * Returns: 0 on success, -1 on failure * * Get the name of the currently used cipher. */ -int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen); /** - * tls_connection_enable_workaround - enable TLS workaround options + * tls_connection_enable_workaround - Enable TLS workaround options * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -302,11 +403,61 @@ int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, * This function is used to enable connection-specific workaround options for * buffer SSL/TLS implementations. */ -int tls_connection_enable_workaround(void *ssl_ctx, +int tls_connection_enable_workaround(void *tls_ctx, struct tls_connection *conn); -int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, +/** + * tls_connection_client_hello_ext - Set TLS extension for ClientHello + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ext_type: Extension type + * @data: Extension payload (NULL to remove extension) + * @data_len: Extension payload length + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len); +/** + * tls_connection_get_failed - Get connection failure status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns >0 if connection has failed, 0 if not. + */ +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_read_alerts - Get connection read alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: Number of times a fatal read (remote end reported error) has + * happened during this connection. + */ +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_write_alerts - Get connection write alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: Number of times a fatal write (locally detected error) has happened + * during this connection. + */ +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_get_keyblock_size - Get TLS key_block size + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn); + #endif /* TLS_H */ diff --git a/contrib/wpa_supplicant/tls_gnutls.c b/contrib/wpa_supplicant/tls_gnutls.c new file mode 100644 index 0000000..2bc9ed8 --- /dev/null +++ b/contrib/wpa_supplicant/tls_gnutls.c @@ -0,0 +1,792 @@ +/* + * WPA Supplicant / SSL/TLS interface functions for openssl + * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include "common.h" +#include "tls.h" + + +/* + * It looks like gnutls does not provide access to client/server_random and + * master_key. This is somewhat unfortunate since these are needed for key + * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible + * hack that copies the gnutls_session_int definition from gnutls_int.h so that + * we can get the needed information. + */ + +typedef u8 uint8; +#define TLS_RANDOM_SIZE 32 +#define TLS_MASTER_SIZE 48 +typedef unsigned char opaque; +typedef struct { + uint8 suite[2]; +} cipher_suite_st; + +typedef struct { + gnutls_connection_end_t entity; + gnutls_kx_algorithm_t kx_algorithm; + gnutls_cipher_algorithm_t read_bulk_cipher_algorithm; + gnutls_mac_algorithm_t read_mac_algorithm; + gnutls_compression_method_t read_compression_algorithm; + gnutls_cipher_algorithm_t write_bulk_cipher_algorithm; + gnutls_mac_algorithm_t write_mac_algorithm; + gnutls_compression_method_t write_compression_algorithm; + cipher_suite_st current_cipher_suite; + opaque master_secret[TLS_MASTER_SIZE]; + opaque client_random[TLS_RANDOM_SIZE]; + opaque server_random[TLS_RANDOM_SIZE]; + /* followed by stuff we are not interested in */ +} security_parameters_st; + +struct gnutls_session_int { + security_parameters_st security_parameters; + /* followed by things we are not interested in */ +}; + +static int tls_gnutls_ref_count = 0; + +struct tls_connection { + gnutls_session session; + char *subject_match, *altsubject_match; + int read_alerts, write_alerts, failed; + + u8 *pre_shared_secret; + size_t pre_shared_secret_len; + int established; + int verify_peer; + + u8 *push_buf, *pull_buf, *pull_buf_offset; + size_t push_buf_len, pull_buf_len; + + gnutls_certificate_credentials_t xcred; +}; + + +static void tls_log_func(int level, const char *msg) +{ + char *s, *pos; + if (level == 6 || level == 7) { + /* These levels seem to be mostly I/O debug and msg dumps */ + return; + } + + s = strdup(msg); + if (s == NULL) + return; + + pos = s; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG, + "gnutls<%d> %s", level, s); + free(s); +} + + +extern int wpa_debug_show_keys; + +void * tls_init(const struct tls_config *conf) +{ + /* Because of the horrible hack to get master_secret and client/server + * random, we need to make sure that the gnutls version is something + * that is expected to have same structure definition for the session + * data.. */ + const char *ver; + const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", NULL }; + int i; + + if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) + return NULL; + tls_gnutls_ref_count++; + + ver = gnutls_check_version(NULL); + if (ver == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver); + for (i = 0; ok_ver[i]; i++) { + if (strcmp(ok_ver[i], ver) == 0) + break; + } + if (ok_ver[i] == NULL) { + wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs " + "to be tested and enabled in tls_gnutls.c", ver); + return NULL; + } + + gnutls_global_set_log_function(tls_log_func); + if (wpa_debug_show_keys) + gnutls_global_set_log_level(11); + return (void *) 1; +} + + +void tls_deinit(void *ssl_ctx) +{ + tls_gnutls_ref_count--; + if (tls_gnutls_ref_count == 0) + gnutls_global_deinit(); +} + + +int tls_get_errors(void *ssl_ctx) +{ + return 0; +} + + +static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, + size_t len) +{ + struct tls_connection *conn = (struct tls_connection *) ptr; + u8 *end; + if (conn->pull_buf == NULL) { + errno = EWOULDBLOCK; + return -1; + } + + end = conn->pull_buf + conn->pull_buf_len; + if (end - conn->pull_buf_offset < len) + len = end - conn->pull_buf_offset; + memcpy(buf, conn->pull_buf_offset, len); + conn->pull_buf_offset += len; + if (conn->pull_buf_offset == end) { + wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); + free(conn->pull_buf); + conn->pull_buf = conn->pull_buf_offset = NULL; + conn->pull_buf_len = 0; + } else { + wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in pull_buf", + __func__, end - conn->pull_buf_offset); + } + return len; +} + + +static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, + size_t len) +{ + struct tls_connection *conn = (struct tls_connection *) ptr; + u8 *nbuf; + + nbuf = realloc(conn->push_buf, conn->push_buf_len + len); + if (nbuf == NULL) { + errno = ENOMEM; + return -1; + } + memcpy(nbuf + conn->push_buf_len, buf, len); + conn->push_buf = nbuf; + conn->push_buf_len += len; + + return len; +} + + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + struct tls_connection *conn; + const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; + const int protos[2] = { GNUTLS_TLS1, 0 }; + + + conn = malloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + memset(conn, 0, sizeof(*conn)); + if (gnutls_init(&conn->session, GNUTLS_CLIENT) < 0) { + wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS " + "connection"); + free(conn); + return NULL; + } + + gnutls_set_default_priority(conn->session); + gnutls_certificate_type_set_priority(conn->session, cert_types); + gnutls_protocol_set_priority(conn->session, protos); + + gnutls_transport_set_pull_function(conn->session, tls_pull_func); + gnutls_transport_set_push_function(conn->session, tls_push_func); + gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn); + + gnutls_certificate_allocate_credentials(&conn->xcred); + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + gnutls_certificate_free_credentials(conn->xcred); + gnutls_deinit(conn->session); + free(conn->pre_shared_secret); + free(conn->subject_match); + free(conn->altsubject_match); + free(conn->push_buf); + free(conn->pull_buf); + free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->established : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + + /* Shutdown previous TLS connection without notifying the peer + * because the connection was already terminated in practice + * and "close notify" shutdown alert would confuse AS. */ + gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); + free(conn->push_buf); + conn->push_buf = NULL; + conn->push_buf_len = 0; + conn->established = 0; + /* TODO: what to do trigger new handshake for re-auth? */ + return 0; +} + + +#if 0 +static int tls_match_altsubject(X509 *cert, const char *match) +{ + GENERAL_NAME *gen; + char *field, *tmp; + void *ext; + int i, found = 0; + size_t len; + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + switch (gen->type) { + case GEN_EMAIL: + field = "EMAIL"; + break; + case GEN_DNS: + field = "DNS"; + break; + case GEN_URI: + field = "URI"; + break; + default: + field = NULL; + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " + "unsupported type=%d", gen->type); + break; + } + + if (!field) + continue; + + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", + field, gen->d.ia5->data); + len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + + 1; + tmp = malloc(len); + if (tmp == NULL) + continue; + snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); + if (strstr(tmp, match)) + found++; + free(tmp); + } + + return found; +} +#endif + + +#if 0 +static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + char buf[256]; + X509 *err_cert; + int err, depth; + SSL *ssl; + struct tls_connection *conn; + char *match, *altmatch; + + err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); + err = X509_STORE_CTX_get_error(x509_ctx); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); + + conn = SSL_get_app_data(ssl); + match = conn ? conn->subject_match : NULL; + altmatch = conn ? conn->altsubject_match : NULL; + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," + " error %d (%s) depth %d for '%s'", err, + X509_verify_cert_error_string(err), depth, buf); + } else { + wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " + "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", + preverify_ok, err, + X509_verify_cert_error_string(err), depth, buf); + if (depth == 0 && match && strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " + "match with '%s'", buf, match); + preverify_ok = 0; + } else if (depth == 0 && altmatch && + !tls_match_altsubject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, "TLS: altSubjectName match " + "'%s' not found", altmatch); + preverify_ok = 0; + } + } + + return preverify_ok; +} +#endif + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + int ret; + + if (conn == NULL || params == NULL) + return -1; + + free(conn->subject_match); + conn->subject_match = NULL; + if (params->subject_match) { + conn->subject_match = strdup(params->subject_match); + if (conn->subject_match == NULL) + return -1; + } + + free(conn->altsubject_match); + conn->altsubject_match = NULL; + if (params->altsubject_match) { + conn->altsubject_match = strdup(params->altsubject_match); + if (conn->altsubject_match == NULL) + return -1; + } + + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); + * to force peer validation(?) */ + + if (params->ca_cert) { + conn->verify_peer = 1; + ret = gnutls_certificate_set_x509_trust_file( + conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " + "in PEM format: %s", params->ca_cert, + gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_trust_file( + conn->xcred, params->ca_cert, + GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert " + "'%s' in DER format: %s", + params->ca_cert, + gnutls_strerror(ret)); + } + } + } + + if (params->client_cert && params->private_key) { + /* TODO: private_key_passwd? */ + ret = gnutls_certificate_set_x509_key_file( + conn->xcred, params->client_cert, params->private_key, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client cert/key " + "in PEM format: %s", gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_key_file( + conn->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client " + "cert/key in DER format: %s", + gnutls_strerror(ret)); + return ret; + } + } + } + + ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, + conn->xcred); + if (ret < 0) { + wpa_printf(MSG_INFO, "Failed to configure credentials: %s", + gnutls_strerror(ret)); + } + + return ret; +} + + +int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) +{ + /* TODO */ + return -1; +} + + +int tls_global_set_verify(void *ssl_ctx, int check_crl) +{ + /* TODO */ + return -1; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + if (conn == NULL) + return -1; + + /* TODO */ + + return 1; +} + + +int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) +{ + /* TODO */ + return -1; +} + + +int tls_global_private_key(void *_ssl_ctx, const char *private_key, + const char *private_key_passwd) +{ + /* TODO */ + return -1; +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ + security_parameters_st *sec; + + if (conn == NULL || conn->session == NULL || keys == NULL) + return -1; + + memset(keys, 0, sizeof(*keys)); + sec = &conn->session->security_parameters; + keys->master_key = sec->master_secret; + keys->master_key_len = TLS_MASTER_SIZE; + keys->client_random = sec->client_random; + keys->client_random_len = TLS_RANDOM_SIZE; + keys->server_random = sec->server_random; + keys->server_random_len = TLS_RANDOM_SIZE; + + return 0; +} + + +static int tls_connection_verify_peer(struct tls_connection *conn) +{ + unsigned int status, num_certs, i; + time_t now; + const gnutls_datum_t *certs; + gnutls_x509_crt_t cert; + + if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { + wpa_printf(MSG_INFO, "TLS: Failed to verify peer " + "certificate chain"); + return -1; + } + + if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { + wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); + return -1; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " + "known issuer"); + return -1; + } + + if (status & GNUTLS_CERT_REVOKED) { + wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); + return -1; + } + + now = time(NULL); + + certs = gnutls_certificate_get_peers(conn->session, &num_certs); + if (certs == NULL) { + wpa_printf(MSG_INFO, "TLS: No peer certificate chain " + "received"); + return -1; + } + + for (i = 0; i < num_certs; i++) { + char *buf; + size_t len; + if (gnutls_x509_crt_init(&cert) < 0) { + wpa_printf(MSG_INFO, "TLS: Certificate initialization " + "failed"); + return -1; + } + + if (gnutls_x509_crt_import(cert, &certs[i], + GNUTLS_X509_FMT_DER) < 0) { + wpa_printf(MSG_INFO, "TLS: Could not parse peer " + "certificate %d/%d", i + 1, num_certs); + gnutls_x509_crt_deinit(cert); + return -1; + } + + gnutls_x509_crt_get_dn(cert, NULL, &len); + len++; + buf = malloc(len + 1); + if (buf) { + buf[0] = buf[len] = '\0'; + gnutls_x509_crt_get_dn(cert, buf, &len); + } + wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", + i + 1, num_certs, buf); + + if (i == 0) { + /* TODO: validate subject_match and altsubject_match */ + } + + free(buf); + + if (gnutls_x509_crt_get_expiration_time(cert) < now || + gnutls_x509_crt_get_activation_time(cert) > now) { + wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " + "not valid at this time", + i + 1, num_certs); + gnutls_x509_crt_deinit(cert); + return -1; + } + + gnutls_x509_crt_deinit(cert); + } + + return 0; +} + + +u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + u8 *out_data; + int ret; + + if (in_data && in_len) { + if (conn->pull_buf) { + wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in " + "pull_buf", __func__, conn->pull_buf_len); + free(conn->pull_buf); + } + conn->pull_buf = malloc(in_len); + if (conn->pull_buf == NULL) + return NULL; + memcpy(conn->pull_buf, in_data, in_len); + conn->pull_buf_offset = conn->pull_buf; + conn->pull_buf_len = in_len; + } + + ret = gnutls_handshake(conn->session); + if (ret < 0) { + switch (ret) { + case GNUTLS_E_AGAIN: + break; + case GNUTLS_E_FATAL_ALERT_RECEIVED: + wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", + __func__, gnutls_alert_get_name( + gnutls_alert_get(conn->session))); + conn->read_alerts++; + /* continue */ + default: + wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " + "-> %s", __func__, gnutls_strerror(ret)); + conn->failed++; + } + } else { + wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); + if (conn->verify_peer && tls_connection_verify_peer(conn)) { + wpa_printf(MSG_INFO, "TLS: Peer certificate chain " + "failed validation"); + conn->failed++; + return NULL; + } + conn->established = 1; + if (conn->push_buf == NULL) { + /* Need to return something to get final TLS ACK. */ + conn->push_buf = malloc(1); + } + } + + out_data = conn->push_buf; + *out_len = conn->push_buf_len; + conn->push_buf = NULL; + conn->push_buf_len = 0; + return out_data; +} + + +u8 * tls_connection_server_handshake(void *ssl_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + /* TODO */ + return NULL; +} + + +int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + ssize_t res; + res = gnutls_record_send(conn->session, in_data, in_len); + if (conn->push_buf == NULL) + return -1; + if (conn->push_buf_len < out_len) + out_len = conn->push_buf_len; + memcpy(out_data, conn->push_buf, out_len); + free(conn->push_buf); + conn->push_buf = NULL; + conn->push_buf_len = 0; + return out_len; +} + + +int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + ssize_t res; + + if (conn->pull_buf) { + wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in " + "pull_buf", __func__, conn->pull_buf_len); + free(conn->pull_buf); + } + conn->pull_buf = malloc(in_len); + if (conn->pull_buf == NULL) + return -1; + memcpy(conn->pull_buf, in_data, in_len); + conn->pull_buf_offset = conn->pull_buf; + conn->pull_buf_len = in_len; + + res = gnutls_record_recv(conn->session, out_data, out_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " + "(%s)", __func__, res, gnutls_strerror(res)); + } + + return res; +} + + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return 0; + return gnutls_session_is_resumed(conn->session); +} + + +#ifdef EAP_FAST +int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, + const u8 *key, size_t key_len) +{ + /* TODO */ + return -1; +} +#endif /* EAP_FAST */ + + +int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) +{ + /* TODO: set ADH-AES128-SHA cipher */ + return -1; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + /* TODO */ + buf[0] = '\0'; + return 0; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + /* TODO: set SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ + return 0; +} + + +#ifdef EAP_FAST +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + /* TODO */ + return -1; +} +#endif /* EAP_FAST */ + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} diff --git a/contrib/wpa_supplicant/tls_none.c b/contrib/wpa_supplicant/tls_none.c index 2b3cafc..07fd879 100644 --- a/contrib/wpa_supplicant/tls_none.c +++ b/contrib/wpa_supplicant/tls_none.c @@ -12,7 +12,13 @@ * See README and COPYING for more details. */ -void * tls_init(void) +#include <stdlib.h> +#include <stdio.h> + +#include "common.h" +#include "tls.h" + +void * tls_init(const struct tls_config *conf) { return (void *) 1; } diff --git a/contrib/wpa_supplicant/tls_openssl.c b/contrib/wpa_supplicant/tls_openssl.c index 4e6ea53..08d5a80 100644 --- a/contrib/wpa_supplicant/tls_openssl.c +++ b/contrib/wpa_supplicant/tls_openssl.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.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 @@ -15,21 +15,504 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> + +#ifndef CONFIG_SMARTCARD +#ifndef OPENSSL_NO_ENGINE +#define OPENSSL_NO_ENGINE +#endif +#endif + #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/pkcs12.h> +#include <openssl/x509v3.h> +#ifndef OPENSSL_NO_ENGINE +#include <openssl/engine.h> +#endif /* OPENSSL_NO_ENGINE */ #include "common.h" #include "tls.h" +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#define OPENSSL_d2i_TYPE const unsigned char ** +#else +#define OPENSSL_d2i_TYPE unsigned char ** +#endif + +static int tls_openssl_ref_count = 0; struct tls_connection { SSL *ssl; BIO *ssl_in, *ssl_out; - char *subject_match; +#ifndef OPENSSL_NO_ENGINE + ENGINE *engine; /* functional reference to the engine */ + EVP_PKEY *private_key; /* the private key if using engine */ +#endif /* OPENSSL_NO_ENGINE */ + char *subject_match, *altsubject_match; + int read_alerts, write_alerts, failed; + + u8 *pre_shared_secret; + size_t pre_shared_secret_len; }; +#ifdef CONFIG_NO_STDOUT_DEBUG + +static void _tls_show_errors(void) +{ + unsigned long err; + + while ((err = ERR_get_error())) { + /* Just ignore the errors, since stdout is disabled */ + } +} +#define tls_show_errors(l, f, t) _tls_show_errors() + +#else /* CONFIG_NO_STDOUT_DEBUG */ + +static void tls_show_errors(int level, const char *func, const char *txt) +{ + unsigned long err; + + wpa_printf(level, "OpenSSL: %s - %s %s", + func, txt, ERR_error_string(ERR_get_error(), NULL)); + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", + ERR_error_string(err, NULL)); + } +} + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_NATIVE_WINDOWS + +/* Windows CryptoAPI and access to certificate stores */ +#include <wincrypt.h> + +#ifdef __MINGW32_VERSION +/* + * MinGW does not yet include all the needed definitions for CryptoAPI, so + * define here whatever extra is needed. + */ +#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) +#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16) +#define CERT_STORE_READONLY_FLAG 0x00008000 +#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 +#define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004 + +static BOOL WINAPI +(*CryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, + void *pvReserved, HCRYPTPROV *phCryptProv, + DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) += NULL; /* to be loaded from crypt32.dll */ + +static PCCERT_CONTEXT WINAPI +(*CertEnumCertificatesInStore)(HCERTSTORE hCertStore, + PCCERT_CONTEXT pPrevCertContext) += NULL; /* to be loaded from crypt32.dll */ + +static int mingw_load_crypto_func(void) +{ + HINSTANCE dll; + + /* MinGW does not yet have full CryptoAPI support, so load the needed + * function here. */ + + if (CryptAcquireCertificatePrivateKey) + return 0; + + dll = LoadLibrary("crypt32"); + if (dll == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " + "library"); + return -1; + } + + CryptAcquireCertificatePrivateKey = GetProcAddress( + dll, "CryptAcquireCertificatePrivateKey"); + if (CryptAcquireCertificatePrivateKey == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CryptAcquireCertificatePrivateKey() address from " + "crypt32 library"); + return -1; + } + + CertEnumCertificatesInStore = (void *) GetProcAddress( + dll, "CertEnumCertificatesInStore"); + if (CertEnumCertificatesInStore == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CertEnumCertificatesInStore() address from " + "crypt32 library"); + return -1; + } + + return 0; +} + +#else /* __MINGW32_VERSION */ + +static int mingw_load_crypto_func(void) +{ + return 0; +} + +#endif /* __MINGW32_VERSION */ + + +struct cryptoapi_rsa_data { + const CERT_CONTEXT *cert; + HCRYPTPROV crypt_prov; + DWORD key_spec; + BOOL free_crypt_prov; +}; + + +static void cryptoapi_error(const char *msg) +{ + wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u", + msg, (unsigned int) GetLastError()); +} + + +static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + struct cryptoapi_rsa_data *priv = + (struct cryptoapi_rsa_data *) rsa->meth->app_data; + HCRYPTHASH hash; + DWORD hash_size, len, i; + unsigned char *buf = NULL; + int ret = 0; + + if (priv == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (padding != RSA_PKCS1_PADDING) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_UNKNOWN_PADDING_TYPE); + return 0; + } + + if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) { + wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported", + __func__); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + return 0; + } + + if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) + { + cryptoapi_error("CryptCreateHash failed"); + return 0; + } + + len = sizeof(hash_size); + if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, + 0)) { + cryptoapi_error("CryptGetHashParam failed"); + goto err; + } + + if (hash_size != flen) { + wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)", + (unsigned) hash_size, flen); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + goto err; + } + if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { + cryptoapi_error("CryptSetHashParam failed"); + goto err; + } + + len = RSA_size(rsa); + buf = malloc(len); + if (buf == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) { + cryptoapi_error("CryptSignHash failed"); + goto err; + } + + for (i = 0; i < len; i++) + to[i] = buf[len - i - 1]; + ret = len; + +err: + free(buf); + CryptDestroyHash(hash); + + return ret; +} + + +static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv) +{ + if (priv == NULL) + return; + if (priv->crypt_prov && priv->free_crypt_prov) + CryptReleaseContext(priv->crypt_prov, 0); + if (priv->cert) + CertFreeCertificateContext(priv->cert); + free(priv); +} + + +static int cryptoapi_finish(RSA *rsa) +{ + cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data); + free((void *) rsa->meth); + rsa->meth = NULL; + return 1; +} + + +static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) +{ + HCERTSTORE cs; + const CERT_CONTEXT *ret = NULL; + + cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, + store | CERT_STORE_OPEN_EXISTING_FLAG | + CERT_STORE_READONLY_FLAG, L"MY"); + if (cs == NULL) { + cryptoapi_error("Failed to open 'My system store'"); + return NULL; + } + + if (strncmp(name, "cert://", 7) == 0) { + unsigned short wbuf[255]; + MultiByteToWideChar(CP_ACP, 0, name + 7, -1, + wbuf, sizeof(wbuf)); + ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_SUBJECT_STR, + wbuf, NULL); + } else if (strncmp(name, "hash://", 7) == 0) { + CRYPT_HASH_BLOB blob; + int len; + const char *hash = name + 7; + unsigned char *buf; + + len = strlen(hash) / 2; + buf = malloc(len); + if (buf && hexstr2bin(hash, buf, len) == 0) { + blob.cbData = len; + blob.pbData = buf; + ret = CertFindCertificateInStore(cs, + X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_HASH, + &blob, NULL); + } + free(buf); + } + + CertCloseStore(cs, 0); + + return ret; +} + + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + X509 *cert = NULL; + RSA *rsa = NULL, *pub_rsa; + struct cryptoapi_rsa_data *priv; + RSA_METHOD *rsa_meth; + + if (name == NULL || + (strncmp(name, "cert://", 7) != 0 && + strncmp(name, "hash://", 7) != 0)) + return -1; + + priv = malloc(sizeof(*priv)); + rsa_meth = malloc(sizeof(*rsa_meth)); + if (priv == NULL || rsa_meth == NULL) { + wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory " + "for CryptoAPI RSA method"); + free(priv); + free(rsa_meth); + return -1; + } + memset(priv, 0, sizeof(*priv)); + memset(rsa_meth, 0, sizeof(*rsa_meth)); + + priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER); + if (priv->cert == NULL) { + priv->cert = cryptoapi_find_cert( + name, CERT_SYSTEM_STORE_LOCAL_MACHINE); + } + if (priv->cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate " + "'%s'", name); + goto err; + } + + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, + priv->cert->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " + "encoding"); + goto err; + } + + if (mingw_load_crypto_func()) + goto err; + + if (!CryptAcquireCertificatePrivateKey(priv->cert, + CRYPT_ACQUIRE_COMPARE_KEY_FLAG, + NULL, &priv->crypt_prov, + &priv->key_spec, + &priv->free_crypt_prov)) { + cryptoapi_error("Failed to acquire a private key for the " + "certificate"); + goto err; + } + + rsa_meth->name = "Microsoft CryptoAPI RSA Method"; + rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc; + rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec; + rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc; + rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec; + rsa_meth->finish = cryptoapi_finish; + rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK; + rsa_meth->app_data = (char *) priv; + + rsa = RSA_new(); + if (rsa == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!SSL_use_certificate(ssl, cert)) + goto err; + pub_rsa = cert->cert_info->key->pkey->pkey.rsa; + X509_free(cert); + cert = NULL; + + rsa->n = BN_dup(pub_rsa->n); + rsa->e = BN_dup(pub_rsa->e); + if (!RSA_set_method(rsa, rsa_meth)) + goto err; + + if (!SSL_use_RSAPrivateKey(ssl, rsa)) + goto err; + RSA_free(rsa); + + return 0; + +err: + if (cert) + X509_free(cert); + if (rsa) + RSA_free(rsa); + else { + free(rsa_meth); + cryptoapi_free_data(priv); + } + return -1; +} + + +static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) +{ + HCERTSTORE cs; + PCCERT_CONTEXT ctx = NULL; + X509 *cert; + char buf[128]; + + if (mingw_load_crypto_func()) + return -1; + + if (name == NULL || strncmp(name, "cert_store://", 13) != 0) + return -1; + + cs = CertOpenSystemStore(0, name + 13); + if (cs == NULL) { + wpa_printf(MSG_DEBUG, "%s: failed to open system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + return -1; + } + + while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, + ctx->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process " + "X509 DER encoding for CA cert"); + continue; + } + + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " + "system certificate store: subject='%s'", buf); + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert to OpenSSL " + "certificate store"); + } + + X509_free(cert); + } + + if (!CertCloseStore(cs, 0)) { + wpa_printf(MSG_DEBUG, "%s: failed to close system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + } + + return 0; +} + + +#else /* CONFIG_NATIVE_WINDOWS */ + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + return -1; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + static void ssl_info_cb(const SSL *ssl, int where, int ret) { const char *str; @@ -50,10 +533,18 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) } else if (where & SSL_CB_ALERT) { wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? - "read (authentication server reported an error)" : + "read (remote end reported an error)" : "write (local SSL3 detected an error)", SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); + if ((ret >> 8) == SSL3_AL_FATAL) { + struct tls_connection *conn = + SSL_get_app_data((SSL *) ssl); + if (where & SSL_CB_READ) + conn->read_alerts++; + else + conn->write_alerts++; + } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", str, ret == 0 ? "failed" : "error", @@ -62,18 +553,160 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) } -void * tls_init(void) +#ifndef OPENSSL_NO_ENGINE +/** + * tls_engine_load_dynamic_generic - load any openssl engine + * @pre: an array of commands and values that load an engine initialized + * in the engine specific function + * @post: an array of commands and values that initialize an already loaded + * engine (or %NULL if not required) + * @id: the engine id of the engine to load (only required if post is not %NULL + * + * This function is a generic function that loads any openssl engine. + * + * Returns: 0 on success, -1 on failure + */ +static int tls_engine_load_dynamic_generic(const char *pre[], + const char *post[], const char *id) +{ + ENGINE *engine; + const char *dynamic_id = "dynamic"; + + engine = ENGINE_by_id(id); + if (engine) { + ENGINE_free(engine); + wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " + "available", id); + return 0; + } + ERR_clear_error(); + + engine = ENGINE_by_id(dynamic_id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + dynamic_id, + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + /* Perform the pre commands. This will load the engine. */ + while (pre && pre[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]); + if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) { + wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: " + "%s %s [%s]", pre[0], pre[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_free(engine); + return -1; + } + pre += 2; + } + + /* + * Free the reference to the "dynamic" engine. The loaded engine can + * now be looked up using ENGINE_by_id(). + */ + ENGINE_free(engine); + + engine = ENGINE_by_id(id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + id, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + while (post && post[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); + if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { + wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:" + " %s %s [%s]", post[0], post[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_remove(engine); + ENGINE_free(engine); + return -1; + } + post += 2; + } + ENGINE_free(engine); + + return 0; +} + + +/** + * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc + * @pkcs11_so_path: pksc11_so_path from the configuration + * @pcks11_module_path: pkcs11_module_path from the configuration + */ +static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, + const char *pkcs11_module_path) +{ + char *engine_id = "pkcs11"; + const char *pre_cmd[] = { + "SO_PATH", pkcs11_so_path, + "ID", engine_id, + "LIST_ADD", "1", + /* "NO_VCHECK", "1", */ + "LOAD", NULL, + NULL, NULL + }; + const char *post_cmd[] = { + "MODULE_PATH", pkcs11_module_path, + NULL, NULL + }; + + if (!pkcs11_so_path || !pkcs11_module_path) + return 0; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", + pkcs11_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id); +} + + +/** + * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc + * @opensc_so_path: opensc_so_path from the configuration + */ +static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) +{ + char *engine_id = "opensc"; + const char *pre_cmd[] = { + "SO_PATH", opensc_so_path, + "ID", engine_id, + "LIST_ADD", "1", + "LOAD", NULL, + NULL, NULL + }; + + if (!opensc_so_path) + return 0; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", + opensc_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id); +} +#endif /* OPENSSL_NO_ENGINE */ + + +void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; - SSL_load_error_strings(); - SSL_library_init(); - /* TODO: if /dev/urandom is available, PRNG is seeded automatically. - * If this is not the case, random data should be added here. */ + if (tls_openssl_ref_count == 0) { + SSL_load_error_strings(); + SSL_library_init(); + /* TODO: if /dev/urandom is available, PRNG is seeded + * automatically. If this is not the case, random data should + * be added here. */ #ifdef PKCS12_FUNCS - PKCS12_PBE_add(); + PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ + } + tls_openssl_ref_count++; ssl = SSL_CTX_new(TLSv1_method()); if (ssl == NULL) @@ -81,6 +714,23 @@ void * tls_init(void) SSL_CTX_set_info_callback(ssl, ssl_info_cb); +#ifndef OPENSSL_NO_ENGINE + if (conf && + (conf->opensc_engine_path || conf->pkcs11_engine_path || + conf->pkcs11_module_path)) { + wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); + ERR_load_ENGINE_strings(); + ENGINE_load_dynamic(); + + if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || + tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, + conf->pkcs11_module_path)) { + tls_deinit(ssl); + return NULL; + } + } +#endif /* OPENSSL_NO_ENGINE */ + return ssl; } @@ -89,8 +739,98 @@ void tls_deinit(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; SSL_CTX_free(ssl); - ERR_free_strings(); - EVP_cleanup(); + + tls_openssl_ref_count--; + if (tls_openssl_ref_count == 0) { +#ifndef OPENSSL_NO_ENGINE + ENGINE_cleanup(); +#endif /* OPENSSL_NO_ENGINE */ + ERR_free_strings(); + EVP_cleanup(); + } +} + + +static int tls_engine_init(struct tls_connection *conn, const char *engine_id, + const char *pin, const char *key_id) +{ +#ifndef OPENSSL_NO_ENGINE + int ret = -1; + if (engine_id == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); + return -1; + } + if (pin == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); + return -1; + } + if (key_id == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); + return -1; + } + + ERR_clear_error(); + conn->engine = ENGINE_by_id(engine_id); + if (!conn->engine) { + wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", + engine_id, ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + if (ENGINE_init(conn->engine) != 1) { + wpa_printf(MSG_ERROR, "ENGINE: engine init failed " + "(engine: %s) [%s]", engine_id, + ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); + + if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { + wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", + ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + conn->private_key = ENGINE_load_private_key(conn->engine, + key_id, NULL, NULL); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" + " '%s' [%s]", key_id, + ERR_error_string(ERR_get_error(), NULL)); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } + return 0; + +err: + if (conn->engine) { + ENGINE_free(conn->engine); + conn->engine = NULL; + } + + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + + return ret; +#else /* OPENSSL_NO_ENGINE */ + return 0; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static void tls_engine_deinit(struct tls_connection *conn) +{ +#ifndef OPENSSL_NO_ENGINE + wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + if (conn->engine) { + ENGINE_finish(conn->engine); + conn->engine = NULL; + } +#endif /* OPENSSL_NO_ENGINE */ } @@ -119,22 +859,21 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) memset(conn, 0, sizeof(*conn)); conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { - wpa_printf(MSG_INFO, "TLS: Failed to initialize new SSL " - "connection: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to initialize new SSL connection"); free(conn); return NULL; } + SSL_set_app_data(conn->ssl, conn); SSL_set_options(conn->ssl, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE); conn->ssl_in = BIO_new(BIO_s_mem()); if (!conn->ssl_in) { - wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for " - "ssl_in: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_in"); SSL_free(conn->ssl); free(conn); return NULL; @@ -142,9 +881,8 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) conn->ssl_out = BIO_new(BIO_s_mem()); if (!conn->ssl_out) { - wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for " - "ssl_out: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_out"); SSL_free(conn->ssl); BIO_free(conn->ssl_in); free(conn); @@ -161,8 +899,11 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; + free(conn->pre_shared_secret); SSL_free(conn->ssl); + tls_engine_deinit(conn); free(conn->subject_match); + free(conn->altsubject_match); free(conn); } @@ -187,6 +928,55 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) } +static int tls_match_altsubject(X509 *cert, const char *match) +{ + GENERAL_NAME *gen; + char *field, *tmp; + void *ext; + int i, found = 0; + size_t len; + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + switch (gen->type) { + case GEN_EMAIL: + field = "EMAIL"; + break; + case GEN_DNS: + field = "DNS"; + break; + case GEN_URI: + field = "URI"; + break; + default: + field = NULL; + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " + "unsupported type=%d", gen->type); + break; + } + + if (!field) + continue; + + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", + field, gen->d.ia5->data); + len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + + 1; + tmp = malloc(len); + if (tmp == NULL) + continue; + snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); + if (strstr(tmp, match)) + found++; + free(tmp); + } + + return found; +} + + static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { char buf[256]; @@ -194,17 +984,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) int err, depth; SSL *ssl; struct tls_connection *conn; - char *match; + char *match, *altmatch; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); err = X509_STORE_CTX_get_error(x509_ctx); depth = X509_STORE_CTX_get_error_depth(x509_ctx); ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); conn = SSL_get_app_data(ssl); match = conn ? conn->subject_match : NULL; + altmatch = conn ? conn->altsubject_match : NULL; if (!preverify_ok) { wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," @@ -219,6 +1010,11 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); preverify_ok = 0; + } else if (depth == 0 && altmatch && + !tls_match_altsubject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, "TLS: altSubjectName match " + "'%s' not found", altmatch); + preverify_ok = 0; } } @@ -226,34 +1022,102 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) } -int tls_connection_ca_cert(void *ssl_ctx, struct tls_connection *conn, - const char *ca_cert, const char *subject_match) +#ifndef OPENSSL_NO_STDIO +static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) { - if (conn == NULL) + SSL_CTX *ssl_ctx = _ssl_ctx; + X509_LOOKUP *lookup; + int ret = 0; + + lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, + X509_LOOKUP_file()); + if (lookup == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed add lookup for X509 store"); return -1; + } - free(conn->subject_match); - conn->subject_match = NULL; - if (subject_match) { - conn->subject_match = strdup(subject_match); - if (conn->subject_match == NULL) - return -1; + if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed load CA in DER format"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " + "cert already in hash table error", + __func__); + } else + ret = -1; } - if (ca_cert) { - if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) - { - wpa_printf(MSG_WARNING, "TLS: Failed to load root " - "certificates: %s", - ERR_error_string(ERR_get_error(), NULL)); + return ret; +} +#endif /* OPENSSL_NO_STDIO */ + + +static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, + const char *ca_cert, const u8 *ca_cert_blob, + size_t ca_cert_blob_len, const char *ca_path) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + + if (ca_cert_blob) { + X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, + ca_cert_blob_len); + if (cert == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob"); + return -1; + } + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert_blob to " + "certificate store"); + X509_free(cert); return -1; + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " + "to certificate store", __func__); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } + +#ifdef CONFIG_NATIVE_WINDOWS + if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == + 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from " + "system certificate store"); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + if (ca_cert || ca_path) { +#ifndef OPENSSL_NO_STDIO + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) != + 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + if (ca_cert && + tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " + "DER format CA certificate", + __func__); + } else + return -1; } else { wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); tls_get_errors(ssl_ctx); } - SSL_set_app_data(conn->ssl, conn); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", + __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ } else { /* No ca_cert configured - do not try to verify server * certificate */ @@ -270,26 +1134,51 @@ int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { - wpa_printf(MSG_WARNING, "TLS: Failed to load root " - "certificates: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); return -1; - } else { - wpa_printf(MSG_DEBUG, "TLS: Trusted root " - "certificate(s) loaded"); } + + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + +#ifndef OPENSSL_NO_STDIO + /* Add the same CAs to the client certificate requests */ + SSL_CTX_set_client_CA_list(ssl_ctx, + SSL_load_client_CA_file(ca_cert)); +#endif /* OPENSSL_NO_STDIO */ } return 0; } -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer, const char *subject_match) +int tls_global_set_verify(void *ssl_ctx, int check_crl) { - if (conn == NULL) - return -1; + int flags; + + if (check_crl) { + X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + if (cs == NULL) { + tls_show_errors(MSG_INFO, __func__, "Failed to get " + "certificate store when enabling " + "check_crl"); + return -1; + } + flags = X509_V_FLAG_CRL_CHECK; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + X509_STORE_set_flags(cs, flags); + } + return 0; +} + +static int tls_connection_set_subject_match(void *ssl_ctx, + struct tls_connection *conn, + const char *subject_match, + const char *altsubject_match) +{ free(conn->subject_match); conn->subject_match = NULL; if (subject_match) { @@ -298,8 +1187,25 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, return -1; } + free(conn->altsubject_match); + conn->altsubject_match = NULL; + if (altsubject_match) { + conn->altsubject_match = strdup(altsubject_match); + if (conn->altsubject_match == NULL) + return -1; + } + + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + if (conn == NULL) + return -1; + if (verify_peer) { - SSL_set_app_data(conn->ssl, conn); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); @@ -313,29 +1219,60 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_client_cert(void *ssl_ctx, struct tls_connection *conn, - const char *client_cert) +static int tls_connection_client_cert(void *ssl_ctx, + struct tls_connection *conn, + const char *client_cert, + const u8 *client_cert_blob, + size_t client_cert_blob_len) { - if (client_cert == NULL) + if (client_cert == NULL && client_cert_blob == NULL) return 0; - if (conn == NULL) + + if (client_cert_blob && + SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, + client_cert_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> " + "OK"); + return 0; + } else if (client_cert_blob) { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_ASN1 failed"); + } + + if (client_cert == NULL) return -1; +#ifndef OPENSSL_NO_STDIO if (SSL_use_certificate_file(conn->ssl, client_cert, - SSL_FILETYPE_ASN1) != 1 && - SSL_use_certificate_file(conn->ssl, client_cert, - SSL_FILETYPE_PEM) != 1) { - wpa_printf(MSG_INFO, "TLS: Failed to load client " - "certificate: %s", - ERR_error_string(ERR_get_error(), NULL)); - return -1; + SSL_FILETYPE_ASN1) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)" + " --> OK"); + return 0; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file (DER) failed"); } - return 0; + + if (SSL_use_certificate_file(conn->ssl, client_cert, + SSL_FILETYPE_PEM) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)" + " --> OK"); + return 0; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file (PEM) failed"); + } +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); +#endif /* OPENSSL_NO_STDIO */ + + return -1; } int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) { +#ifndef OPENSSL_NO_STDIO SSL_CTX *ssl_ctx = _ssl_ctx; if (client_cert == NULL) return 0; @@ -344,12 +1281,17 @@ int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) SSL_FILETYPE_ASN1) != 1 && SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_PEM) != 1) { - wpa_printf(MSG_INFO, "TLS: Failed to load client " - "certificate: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to load client certificate"); return -1; } return 0; +#else /* OPENSSL_NO_STDIO */ + if (client_cert == NULL) + return 0; + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ } @@ -364,42 +1306,31 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) } -static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, - const char *passwd) -{ #ifdef PKCS12_FUNCS - FILE *f; - PKCS12 *p12; +static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, + const char *passwd) +{ EVP_PKEY *pkey; X509 *cert; + STACK_OF(X509) *certs; int res = 0; - - f = fopen(private_key, "r"); - if (f == NULL) - return -1; - - p12 = d2i_PKCS12_fp(f, NULL); - if (p12 == NULL) { - wpa_printf(MSG_DEBUG, "TLS: Failed to read PKCS12 file '%s'", - private_key); - fclose(f); - return -1; - } - fclose(f); + char buf[256]; pkey = NULL; cert = NULL; - if (!PKCS12_parse(p12, passwd, &pkey, &cert, NULL)) { - wpa_printf(MSG_DEBUG, "TLS: Failed to parse PKCS12 file '%s': " - "%s", private_key, - ERR_error_string(ERR_get_error(), NULL)); + certs = NULL; + if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to parse PKCS12 file"); return -1; } - wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 file '%s'", - private_key); + wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data"); if (cert) { - wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12"); + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: " + "subject='%s'", buf); if (ssl) { if (SSL_use_certificate(ssl, cert) != 1) res = -1; @@ -422,9 +1353,56 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, EVP_PKEY_free(pkey); } + if (certs) { + while ((cert = sk_X509_pop(certs)) != NULL) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: additional certificate" + " from PKCS12: subject='%s'", buf); + /* + * There is no SSL equivalent for the chain cert - so + * always add it to the context... + */ + if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { + res = -1; + break; + } + } + sk_X509_free(certs); + } + PKCS12_free(p12); + if (res < 0) + tls_get_errors(ssl_ctx); + return res; +} +#endif /* PKCS12_FUNCS */ + + +static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, + const char *passwd) +{ +#ifdef PKCS12_FUNCS + FILE *f; + PKCS12 *p12; + + f = fopen(private_key, "r"); + if (f == NULL) + return -1; + + p12 = d2i_PKCS12_fp(f, NULL); + fclose(f); + + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 file"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " "p12/pfx files"); @@ -433,17 +1411,65 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, } -int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn, - const char *private_key, - const char *private_key_passwd) +static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, + const u8 *blob, size_t len, const char *passwd) +{ +#ifdef PKCS12_FUNCS + PKCS12 *p12; + + p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 blob"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " + "p12/pfx blobs"); + return -1; +#endif /* PKCS12_FUNCS */ +} + + +static int tls_connection_engine_private_key(void *_ssl_ctx, + struct tls_connection *conn) +{ +#ifndef OPENSSL_NO_ENGINE + if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { + tls_show_errors(MSG_ERROR, __func__, + "ENGINE: cannot use private key for TLS"); + return -1; + } + if (!SSL_check_private_key(conn->ssl)) { + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); + return -1; + } + return 0; +#else /* OPENSSL_NO_ENGINE */ + wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but " + "engine support was not compiled in"); + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_private_key(void *_ssl_ctx, + struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len) { SSL_CTX *ssl_ctx = _ssl_ctx; char *passwd; + int ok; - if (private_key == NULL) + if (private_key == NULL && private_key_blob == NULL) return 0; - if (conn == NULL) - return -1; if (private_key_passwd) { passwd = strdup(private_key_passwd); @@ -454,28 +1480,123 @@ int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn, SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_ASN1) != 1 && - SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_PEM) != 1 && - tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)) { - wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s", - ERR_error_string(ERR_get_error(), NULL)); + + ok = 0; + while (private_key_blob) { + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "ASN1(EVP_PKEY_RSA) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)" + " failed"); + } + + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "ASN1(EVP_PKEY_DSA) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)" + " failed"); + } + + if (SSL_use_RSAPrivateKey_ASN1(conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_RSAPrivateKey_ASN1 --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_RSAPrivateKey_ASN1 failed"); + } + + if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, + private_key_blob_len, passwd) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " + "OK"); + ok = 1; + break; + } + + break; + } + + while (!ok && private_key) { +#ifndef OPENSSL_NO_STDIO + if (SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_ASN1) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_PrivateKey_File (DER) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_File (DER) " + "failed"); + } + + if (SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_PEM) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_PrivateKey_File (PEM) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_File (PEM) " + "failed"); + } +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", + __func__); +#endif /* OPENSSL_NO_STDIO */ + + if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) + == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " + "--> OK"); + ok = 1; + break; + } + + if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to " + "access certificate store --> OK"); + ok = 1; + break; + } + + break; + } + + if (!ok) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); free(passwd); ERR_clear_error(); return -1; } ERR_clear_error(); - free(passwd); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + free(passwd); if (!SSL_check_private_key(conn->ssl)) { - wpa_printf(MSG_INFO, "SSL: Private key failed " - "verification: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, "Private key failed " + "verification"); return -1; } + wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully"); return 0; } @@ -498,13 +1619,16 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + if ( +#ifndef OPENSSL_NO_STDIO + SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_ASN1) != 1 && SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1 && +#endif /* OPENSSL_NO_STDIO */ tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { - wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); free(passwd); ERR_clear_error(); return -1; @@ -514,9 +1638,8 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); if (!SSL_CTX_check_private_key(ssl_ctx)) { - wpa_printf(MSG_INFO, "SSL: Private key failed " - "verification: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); return -1; } @@ -524,7 +1647,7 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, } -int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, +static int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, const char *dh_file) { #ifdef OPENSSL_NO_DH @@ -537,6 +1660,7 @@ int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, DH *dh; BIO *bio; + /* TODO: add support for dh_blob */ if (dh_file == NULL) return 0; if (conn == NULL) @@ -609,6 +1733,7 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) return -1; + memset(keys, 0, sizeof(*keys)); keys->master_key = ssl->session->master_key; keys->master_key_len = ssl->session->master_key_length; keys->client_random = ssl->s3->client_random; @@ -627,13 +1752,18 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, int res; u8 *out_data; + /* + * Give TLS handshake data from the server (if available) to OpenSSL + * for processing. + */ if (in_data && BIO_write(conn->ssl_in, in_data, in_len) < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_write"); return NULL; } + /* Initiate TLS handshake or continue the existing handshake */ res = SSL_connect(conn->ssl); if (res != 1) { int err = SSL_get_error(conn->ssl, res); @@ -644,27 +1774,33 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to " "write"); else { - wpa_printf(MSG_INFO, "SSL: SSL_connect: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NULL; + tls_show_errors(MSG_INFO, __func__, "SSL_connect"); + conn->failed++; } } + /* Get the TLS handshake data to be sent to the server */ res = BIO_ctrl_pending(conn->ssl_out); wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); out_data = malloc(res == 0 ? 1 : res); if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); - BIO_reset(conn->ssl_out); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); - BIO_reset(conn->ssl_out); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } @@ -684,8 +1820,8 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, if (in_data && BIO_write(conn->ssl_in, in_data, in_len) < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_write"); return NULL; } @@ -701,15 +1837,21 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); - BIO_reset(conn->ssl_out); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); - BIO_reset(conn->ssl_out); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } @@ -719,7 +1861,7 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { int res; @@ -727,19 +1869,24 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL) return -1; - BIO_reset(conn->ssl_in); - BIO_reset(conn->ssl_out); + /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */ + if ((res = BIO_reset(conn->ssl_in)) < 0 || + (res = BIO_reset(conn->ssl_out)) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); + return res; + } res = SSL_write(conn->ssl, in_data, in_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Encryption failed - SSL_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - SSL_write"); return res; } + /* Read encrypted data to be sent to the server */ res = BIO_read(conn->ssl_out, out_data, out_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Encryption failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - BIO_read"); return res; } @@ -748,23 +1895,28 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { int res; + /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */ res = BIO_write(conn->ssl_in, in_data, in_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Decryption failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - BIO_write"); + return res; + } + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); return res; } - BIO_reset(conn->ssl_out); + /* Read decrypted data for further processing */ res = SSL_read(conn->ssl, out_data, out_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Decryption failed - SSL_read: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - SSL_read"); return res; } @@ -778,22 +1930,54 @@ int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) } +#ifdef EAP_FAST +/* Pre-shared secred requires a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ + +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + SSL_CIPHER **cipher, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->pre_shared_secret == 0) + return 0; + + memcpy(secret, conn->pre_shared_secret, conn->pre_shared_secret_len); + *secret_len = conn->pre_shared_secret_len; + + return 1; +} + + int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, const u8 *key, size_t key_len) { - SSL *ssl; - - if (conn == NULL || key == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) - return -1; - ssl = conn->ssl; - if (ssl == NULL || ssl->session == NULL) + if (conn == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) return -1; - memcpy(ssl->session->master_key, key, key_len); - ssl->session->master_key_length = key_len; + free(conn->pre_shared_secret); + conn->pre_shared_secret = NULL; + conn->pre_shared_secret_len = 0; + + if (key) { + conn->pre_shared_secret = malloc(key_len); + if (conn->pre_shared_secret) { + memcpy(conn->pre_shared_secret, key, key_len); + conn->pre_shared_secret_len = key_len; + } + if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; + } else { + if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; + } return 0; } +#endif /* EAP_FAST */ int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) @@ -802,8 +1986,8 @@ int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) return -1; if (SSL_set_cipher_list(conn->ssl, "ADH-AES128-SHA") != 1) { - wpa_printf(MSG_INFO, "TLS: Anon DH configuration failed - %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Anon DH configuration failed"); return -1; } @@ -844,36 +2028,116 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { - struct tls_ext_hdr { - u16 extensions_len; - u16 extension_type; - u16 extension_len; - } *hdr; - if (conn == NULL || conn->ssl == NULL) return -1; - OPENSSL_free(conn->ssl->hello_extension); - if (data == NULL) { - conn->ssl->hello_extension = NULL; - conn->ssl->hello_extension_len = 0; - return 0; + + if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, + data_len) != 1) + return -1; + + return 0; +} +#endif /* EAP_FAST */ + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + int ret; + unsigned long err; + + if (conn == NULL) + return -1; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", + __func__, ERR_error_string(err, NULL)); } - if (data_len == 0) { - conn->ssl->hello_extension = OPENSSL_malloc(1); - conn->ssl->hello_extension_len = 0; - return 0; + + if (tls_connection_set_subject_match(tls_ctx, conn, + params->subject_match, + params->altsubject_match)) + return -1; + if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path)) + return -1; + if (tls_connection_client_cert(tls_ctx, conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) + return -1; + + if (params->engine) { + wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); + ret = tls_engine_init(conn, params->engine_id, params->pin, + params->key_id); + if (ret) + return ret; + if (tls_connection_engine_private_key(tls_ctx, conn)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_private_key(tls_ctx, conn, + params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", + params->private_key); + return -1; } - conn->ssl->hello_extension = OPENSSL_malloc(sizeof(*hdr) + data_len); - if (conn->ssl->hello_extension == NULL) + + if (tls_connection_dh(tls_ctx, conn, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + params->dh_file); return -1; + } - hdr = (struct tls_ext_hdr *) conn->ssl->hello_extension; - hdr->extensions_len = host_to_be16(sizeof(*hdr) - 2 + data_len); - hdr->extension_type = host_to_be16(ext_type); - hdr->extension_len = host_to_be16(data_len); - memcpy(hdr + 1, data, data_len); - conn->ssl->hello_extension_len = sizeof(*hdr) + data_len; + tls_get_errors(tls_ctx); return 0; } -#endif /* EAP_FAST */ + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + const EVP_CIPHER *c; + const EVP_MD *h; + + if (conn == NULL || conn->ssl == NULL || + conn->ssl->enc_read_ctx == NULL || + conn->ssl->enc_read_ctx->cipher == NULL || + conn->ssl->read_hash == NULL) + return -1; + + c = conn->ssl->enc_read_ctx->cipher; + h = conn->ssl->read_hash; + + return 2 * (EVP_CIPHER_key_length(c) + + EVP_MD_size(h) + + EVP_CIPHER_iv_length(c)); +} diff --git a/contrib/wpa_supplicant/tls_schannel.c b/contrib/wpa_supplicant/tls_schannel.c new file mode 100644 index 0000000..d06a1c3 --- /dev/null +++ b/contrib/wpa_supplicant/tls_schannel.c @@ -0,0 +1,738 @@ +/* + * WPA Supplicant / SSL/TLS interface functions for Microsoft Schannel + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.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. + */ + +/* + * FIX: Go through all SSPI functions and verify what needs to be freed + * FIX: session resumption + * TODO: add support for server cert chain validation + * TODO: add support for CA cert validation + * TODO: add support for EAP-TLS (client cert/key conf) + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <windows.h> +#include <wincrypt.h> +#include <schannel.h> +#define SECURITY_WIN32 +#include <security.h> +#include <sspi.h> + +#include "common.h" +#include "tls.h" + + +struct tls_global { + HMODULE hsecurity; + PSecurityFunctionTable sspi; + HCERTSTORE my_cert_store; +}; + +struct tls_connection { + int established, start; + int failed, read_alerts, write_alerts; + + SCHANNEL_CRED schannel_cred; + CredHandle creds; + CtxtHandle context; + + u8 eap_tls_prf[128]; + int eap_tls_prf_set; +}; + + +static int schannel_load_lib(struct tls_global *global) +{ + INIT_SECURITY_INTERFACE pInitSecurityInterface; + + global->hsecurity = LoadLibrary("Secur32.dll"); + if (global->hsecurity == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", + __func__, (unsigned int) GetLastError()); + return -1; + } + + pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( + global->hsecurity, "InitSecurityInterfaceA"); + if (pInitSecurityInterface == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not find " + "InitSecurityInterfaceA from Secur32.dll", + __func__); + FreeLibrary(global->hsecurity); + global->hsecurity = NULL; + return -1; + } + + global->sspi = pInitSecurityInterface(); + if (global->sspi == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not read security " + "interface - 0x%x", + __func__, (unsigned int) GetLastError()); + FreeLibrary(global->hsecurity); + global->hsecurity = NULL; + return -1; + } + + return 0; +} + + +void * tls_init(const struct tls_config *conf) +{ + struct tls_global *global; + + global = malloc(sizeof(*global)); + if (global == NULL) + return NULL; + memset(global, 0, sizeof(*global)); + if (schannel_load_lib(global)) { + free(global); + return NULL; + } + return global; +} + + +void tls_deinit(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + + if (global->my_cert_store) + CertCloseStore(global->my_cert_store, 0); + FreeLibrary(global->hsecurity); + free(global); +} + + +int tls_get_errors(void *ssl_ctx) +{ + return 0; +} + + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + struct tls_connection *conn; + + conn = malloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + memset(conn, 0, sizeof(*conn)); + conn->start = 1; + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + + free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->established : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + struct tls_global *global = ssl_ctx; + if (conn == NULL) + return -1; + + conn->eap_tls_prf_set = 0; + conn->established = conn->failed = 0; + conn->read_alerts = conn->write_alerts = 0; + global->sspi->DeleteSecurityContext(&conn->context); + /* FIX: what else needs to be reseted? */ + + return 0; +} + + +int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) +{ + return -1; +} + + +int tls_global_set_verify(void *ssl_ctx, int check_crl) +{ + return -1; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + return -1; +} + + +int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) +{ + return -1; +} + + +int tls_global_private_key(void *_ssl_ctx, const char *private_key, + const char *private_key_passwd) +{ + return -1; +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ + if (conn == NULL || keys == NULL || !conn->eap_tls_prf_set) + return -1; + + memset(keys, 0, sizeof(*keys)); + + /* + * Cannot get master_key from Schannel, but EapKeyBlock can be used to + * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and + * EAP-TTLS cannot use this, though, since they are using different + * labels. The only option could be to implement TLSv1 completely here + * and just use Schannel or CryptoAPI for low-level crypto + * functionality.. + */ + keys->eap_tls_prf = conn->eap_tls_prf; + keys->eap_tls_prf_len = sizeof(conn->eap_tls_prf); + + return 0; +} + + +static u8 * tls_conn_hs_clienthello(struct tls_global *global, + struct tls_connection *conn, + size_t *out_len) +{ + DWORD sspi_flags, sspi_flags_out; + SecBufferDesc outbuf; + SecBuffer outbufs[1]; + SECURITY_STATUS status; + TimeStamp ts_expiry; + + sspi_flags = ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_MANUAL_CRED_VALIDATION; + + wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__); + + outbufs[0].pvBuffer = NULL; + outbufs[0].BufferType = SECBUFFER_TOKEN; + outbufs[0].cbBuffer = 0; + + outbuf.cBuffers = 1; + outbuf.pBuffers = outbufs; + outbuf.ulVersion = SECBUFFER_VERSION; + + status = global->sspi->InitializeSecurityContextA( + &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, + SECURITY_NATIVE_DREP, NULL, 0, &conn->context, + &outbuf, &sspi_flags_out, &ts_expiry); + if (status != SEC_I_CONTINUE_NEEDED) { + wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " + "failed - 0x%x", + __func__, (unsigned int) status); + return NULL; + } + + if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { + u8 *buf; + wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", + outbufs[0].pvBuffer, outbufs[0].cbBuffer); + conn->start = 0; + *out_len = outbufs[0].cbBuffer; + buf = malloc(*out_len); + if (buf == NULL) + return NULL; + memcpy(buf, outbufs[0].pvBuffer, *out_len); + global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); + return buf; + } + + wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello"); + + return NULL; +} + + +#ifndef SECPKG_ATTR_EAP_KEY_BLOCK +#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b + +typedef struct _SecPkgContext_EapKeyBlock { + BYTE rgbKeys[128]; + BYTE rgbIVs[64]; +} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock; +#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */ + +static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) +{ + SECURITY_STATUS status; + SecPkgContext_EapKeyBlock kb; + + /* Note: Windows NT and Windows Me/98/95 do not support getting + * EapKeyBlock */ + + status = global->sspi->QueryContextAttributes( + &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb); + if (status != SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes(" + "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)", + __func__, (int) status); + return -1; + } + + wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys", + kb.rgbKeys, sizeof(kb.rgbKeys)); + wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs", + kb.rgbIVs, sizeof(kb.rgbIVs)); + + memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); + conn->eap_tls_prf_set = 1; +} + + +u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + struct tls_global *global = ssl_ctx; + DWORD sspi_flags, sspi_flags_out; + SecBufferDesc inbuf, outbuf; + SecBuffer inbufs[2], outbufs[1]; + SECURITY_STATUS status; + TimeStamp ts_expiry; + u8 *out_buf = NULL; + + if (conn->start) { + return tls_conn_hs_clienthello(global, conn, out_len); + } + + wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", + in_len); + + sspi_flags = ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_MANUAL_CRED_VALIDATION; + + /* Input buffer for Schannel */ + inbufs[0].pvBuffer = (u8 *) in_data; + inbufs[0].cbBuffer = in_len; + inbufs[0].BufferType = SECBUFFER_TOKEN; + + /* Place for leftover data from Schannel */ + inbufs[1].pvBuffer = NULL; + inbufs[1].cbBuffer = 0; + inbufs[1].BufferType = SECBUFFER_EMPTY; + + inbuf.cBuffers = 2; + inbuf.pBuffers = inbufs; + inbuf.ulVersion = SECBUFFER_VERSION; + + /* Output buffer for Schannel */ + outbufs[0].pvBuffer = NULL; + outbufs[0].cbBuffer = 0; + outbufs[0].BufferType = SECBUFFER_TOKEN; + + outbuf.cBuffers = 1; + outbuf.pBuffers = outbufs; + outbuf.ulVersion = SECBUFFER_VERSION; + + status = global->sspi->InitializeSecurityContextA( + &conn->creds, &conn->context, NULL, sspi_flags, 0, + SECURITY_NATIVE_DREP, &inbuf, 0, NULL, + &outbuf, &sspi_flags_out, &ts_expiry); + + wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContextA -> " + "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " + "intype[1]=%d outlen[0]=%d", + (int) status, (int) inbufs[0].cbBuffer, + (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, + (int) inbufs[1].BufferType, + (int) outbufs[0].cbBuffer); + if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || + (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { + if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { + wpa_hexdump(MSG_MSGDUMP, "SChannel - output", + outbufs[0].pvBuffer, outbufs[0].cbBuffer); + *out_len = outbufs[0].cbBuffer; + out_buf = malloc(*out_len); + if (out_buf == NULL) + return NULL; + memcpy(out_buf, outbufs[0].pvBuffer, *out_len); + global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); + outbufs[0].pvBuffer = NULL; + } + } + + switch (status) { + case SEC_E_INCOMPLETE_MESSAGE: + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); + break; + case SEC_I_CONTINUE_NEEDED: + wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); + break; + case SEC_E_OK: + /* TODO: verify server certificate chain */ + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " + "completed successfully"); + conn->established = 1; + tls_get_eap(global, conn); + + /* Need to return something to get final TLS ACK. */ + if (out_buf == NULL) + out_buf = malloc(1); + + if (inbufs[1].BufferType == SECBUFFER_EXTRA) { + wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " + "application data", + inbufs[1].pvBuffer, inbufs[1].cbBuffer); + /* FIX: need to fix TLS API to allow this data to be + * passed to the caller */ + global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); + inbufs[1].pvBuffer = NULL; + } + break; + case SEC_I_INCOMPLETE_CREDENTIALS: + wpa_printf(MSG_DEBUG, + "Schannel: SEC_I_INCOMPLETE_CREDENTIALS"); + break; + case SEC_E_WRONG_PRINCIPAL: + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL"); + break; + case SEC_E_INTERNAL_ERROR: + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR"); + break; + } + + if (FAILED(status)) { + wpa_printf(MSG_DEBUG, "Schannel: Handshake failed " + "(out_buf=%p)", out_buf); + conn->failed++; + global->sspi->DeleteSecurityContext(&conn->context); + return out_buf; + } + + if (inbufs[1].BufferType == SECBUFFER_EXTRA) { + /* TODO: Can this happen? What to do with this data? */ + wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data", + inbufs[1].pvBuffer, inbufs[1].cbBuffer); + global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); + inbufs[1].pvBuffer = NULL; + } + + return out_buf; +} + + +u8 * tls_connection_server_handshake(void *ssl_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + return NULL; +} + + +int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + struct tls_global *global = ssl_ctx; + SECURITY_STATUS status; + SecBufferDesc buf; + SecBuffer bufs[4]; + SecPkgContext_StreamSizes sizes; + int i; + size_t total_len; + + status = global->sspi->QueryContextAttributes(&conn->context, + SECPKG_ATTR_STREAM_SIZES, + &sizes); + if (status != SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", + __func__); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", + __func__, + (unsigned int) sizes.cbHeader, + (unsigned int) sizes.cbTrailer); + + total_len = sizes.cbHeader + in_len + sizes.cbTrailer; + + if (out_len < total_len) { + wpa_printf(MSG_DEBUG, "%s: too short out_data (out_len=%lu " + "in_len=%lu total_len=%lu)", __func__, + (unsigned long) out_len, (unsigned long) in_len, + (unsigned long) total_len); + return -1; + } + + memset(&bufs, 0, sizeof(bufs)); + bufs[0].pvBuffer = out_data; + bufs[0].cbBuffer = sizes.cbHeader; + bufs[0].BufferType = SECBUFFER_STREAM_HEADER; + + memcpy(out_data + sizes.cbHeader, in_data, in_len); + bufs[1].pvBuffer = out_data + sizes.cbHeader; + bufs[1].cbBuffer = in_len; + bufs[1].BufferType = SECBUFFER_DATA; + + bufs[2].pvBuffer = out_data + sizes.cbHeader + in_len; + bufs[2].cbBuffer = sizes.cbTrailer; + bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; + + buf.ulVersion = SECBUFFER_VERSION; + buf.cBuffers = 3; + buf.pBuffers = bufs; + + status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0); + + wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> " + "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " + "len[2]=%d type[2]=%d", + (int) status, + (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, + (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, + (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); + wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " + "out_data=%p bufs %p %p %p", + out_data, bufs[0].pvBuffer, bufs[1].pvBuffer, + bufs[2].pvBuffer); + + for (i = 0; i < 3; i++) { + if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY) + { + wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs", + bufs[i].pvBuffer, bufs[i].cbBuffer); + } + } + + if (status == SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); + wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Encrypted data from " + "EncryptMessage", out_data, total_len); + return total_len; + } + + wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", + __func__, (int) status); + return -1; +} + + +int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + struct tls_global *global = ssl_ctx; + SECURITY_STATUS status; + SecBufferDesc buf; + SecBuffer bufs[4]; + int i; + + if (out_len < in_len) { + wpa_printf(MSG_DEBUG, "%s: out_len=%lu < in_len=%lu", __func__, + (unsigned long) out_len, (unsigned long) in_len); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "Schannel: Encrypted data to DecryptMessage", + in_data, in_len); + memset(&bufs, 0, sizeof(bufs)); + memcpy(out_data, in_data, in_len); + bufs[0].pvBuffer = out_data; + bufs[0].cbBuffer = in_len; + bufs[0].BufferType = SECBUFFER_DATA; + + bufs[1].BufferType = SECBUFFER_EMPTY; + bufs[2].BufferType = SECBUFFER_EMPTY; + bufs[3].BufferType = SECBUFFER_EMPTY; + + buf.ulVersion = SECBUFFER_VERSION; + buf.cBuffers = 4; + buf.pBuffers = bufs; + + status = global->sspi->DecryptMessage(&conn->context, &buf, 0, + NULL); + wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> " + "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " + "len[2]=%d type[2]=%d len[3]=%d type[3]=%d", + (int) status, + (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, + (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, + (int) bufs[2].cbBuffer, (int) bufs[2].BufferType, + (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); + wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " + "out_data=%p bufs %p %p %p %p", + out_data, bufs[0].pvBuffer, bufs[1].pvBuffer, + bufs[2].pvBuffer, bufs[3].pvBuffer); + + switch (status) { + case SEC_E_INCOMPLETE_MESSAGE: + wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE", + __func__); + break; + case SEC_E_OK: + wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); + for (i = 0; i < 4; i++) { + if (bufs[i].BufferType == SECBUFFER_DATA) + break; + } + if (i == 4) { + wpa_printf(MSG_DEBUG, "%s: No output data from " + "DecryptMessage", __func__); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " + "DecryptMessage", + bufs[i].pvBuffer, bufs[i].cbBuffer); + if (bufs[i].cbBuffer > out_len) { + wpa_printf(MSG_DEBUG, "%s: Too long output data", + __func__); + return -1; + } + memmove(out_data, bufs[i].pvBuffer, bufs[i].cbBuffer); + return bufs[i].cbBuffer; + } + + wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", + __func__, (int) status); + return -1; +} + + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) +{ + return 0; +} + + +#ifdef EAP_FAST +int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, + const u8 *key, size_t key_len) +{ + return -1; +} +#endif /* EAP_FAST */ + + +int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) +{ + return -1; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + return -1; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + return 0; +} + + +#ifdef EAP_FAST +/* ClientHello TLS extensions require a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + return -1; +} +#endif /* EAP_FAST */ + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + struct tls_global *global = tls_ctx; + ALG_ID algs[1]; + SECURITY_STATUS status; + TimeStamp ts_expiry; + + if (conn == NULL) + return -1; + + if (global->my_cert_store == NULL && + (global->my_cert_store = CertOpenSystemStore(0, "MY")) == NULL) { + wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", + __func__, (unsigned int) GetLastError()); + return -1; + } + + memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); + conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; + algs[0] = CALG_RSA_KEYX; + conn->schannel_cred.cSupportedAlgs = 1; + conn->schannel_cred.palgSupportedAlgs = algs; + conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; + status = global->sspi->AcquireCredentialsHandleA( + NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, + &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); + if (status != SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " + "0x%x", __func__, (unsigned int) status); + return -1; + } + + return 0; +} diff --git a/contrib/wpa_supplicant/todo.txt b/contrib/wpa_supplicant/todo.txt index 52f3349..5ecda1d 100644 --- a/contrib/wpa_supplicant/todo.txt +++ b/contrib/wpa_supplicant/todo.txt @@ -1,21 +1,9 @@ To do: -- add WPA support to Linux Wireless Extensions -- add support for other drivers -- implement GUI for WPA Supplicant/Xsupplicant/iwconfig/iwlist - (easy to use configuration and network stats, etc.) -- add support for opportunistic PMKSA caching - hostap: try other roaming modes NOTE: current mode (manual roaming) does not really roam at all.. Firmware did not notice the current AP disappearing.. -- EAP-MSCHAPv2: add support for password changing - add support for WPA with ap_scan=0 (update selected cipher etc. based on AssocInfo; make sure these match with configuration) -- add driver interface for using wpa_supplicant with wired interface - (or a separate program using EAPOL library) -- wpa_supplicant.conf g+rw so that frontend can change wpa_supplicant.conf - and RECONFIG wpa_supplicant (?) - (or wpa_supplicant changes .conf and ctrl interface gets support for - changing config?) - optional security separation (build time option): run EAPOL state machines as non-root (need to add something like socketpair between privileged root process and non-root handler; send EAPOL packets between processes @@ -29,9 +17,6 @@ To do: auth) - EAP-AKA: AT_CHECKCODE - EAP-SIM/AKA: AT_RESULT_IND -- abort auth if EAP method initialization fails and there no other - accepted methods (i.e., do not send NAK for the same method that just - failed) - on disconnect event, could try to associate with another AP if one is present in scan results; would need to update scan results periodically.. - add flag scan_requested and only try to re-associate if this is set when @@ -40,15 +25,31 @@ To do: - 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 -- read CA certs from PFX file - EAP-SIM/AKA: if SIM reader initialization fails, do not start authentication - 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) + 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.. -- update developer.txt to match with current implementation - (driver API updates, EAP methods) -- driver_wext.c and driver that does not support WPA -> fix plaintext, WEP, and - IEEE 802.1X operation (e.g., use capabilities to report no support for WPA) +- -Dwired: if ssid is set in network block, authentication gets "stuck" since + driver_wired.c only reports empty SSID and association is not assumed to be + ok +- EAP-PAX with PAX_SEC +- EAP: extended nak, vendor method; go through rfc + RFC 3748 + * Expanded Type (Sect. 5.7) + * Experimental Type + * Expanded Nak (Sect. 5.3.2) + * 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?) +- 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? +- test wait-for-interface and daemonize combinations with number of driver + interfaces diff --git a/contrib/wpa_supplicant/version.h b/contrib/wpa_supplicant/version.h index b030f34..8f8eff8 100644 --- a/contrib/wpa_supplicant/version.h +++ b/contrib/wpa_supplicant/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.3.9" +#define VERSION_STR "0.4.8" #endif /* VERSION_H */ diff --git a/contrib/wpa_supplicant/wpa.c b/contrib/wpa_supplicant/wpa.c index c70f556..91e1f2a 100644 --- a/contrib/wpa_supplicant/wpa.c +++ b/contrib/wpa_supplicant/wpa.c @@ -1,5 +1,5 @@ /* - * WPA Supplicant + * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -14,12 +14,10 @@ #include <stdlib.h> #include <stdio.h> -#include <sys/time.h> #ifndef CONFIG_NATIVE_WINDOWS #include <netinet/in.h> #endif /* CONFIG_NATIVE_WINDOWS */ #include <string.h> -#include <time.h> #include "common.h" #include "md5.h" @@ -27,23 +25,14 @@ #include "rc4.h" #include "aes_wrap.h" #include "wpa.h" -#include "driver.h" #include "eloop.h" #include "wpa_supplicant.h" #include "config.h" #include "l2_packet.h" #include "eapol_sm.h" -#include "wpa_supplicant_i.h" +#include "preauth.h" +#include "wpa_i.h" -static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s); - -#define PMKID_CANDIDATE_PRIO_SCAN 1000 - -/* TODO: make these configurable */ -static const int dot11RSNAConfigPMKLifetime = 43200; -static const int dot11RSNAConfigPMKReauthThreshold = 70; -static const int dot11RSNAConfigSATimeout = 60; -static const int pmksa_cache_max_entries = 32; static const int WPA_SELECTOR_LEN = 4; static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; @@ -68,7 +57,7 @@ static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; * Authenticated Key Management Suite Count (2 octets, little endian) * (default: 1) * Authenticated Key Management Suite List (4 * n octets) - * (default: unspec 802.1x) + * (default: unspec 802.1X) * WPA Capabilities (2 octets, little endian) (default: 0) */ @@ -77,7 +66,7 @@ struct wpa_ie_hdr { u8 len; u8 oui[3]; u8 oui_type; - u16 version; + u8 version[2]; } __attribute__ ((packed)); @@ -117,7 +106,7 @@ static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; * Authenticated Key Management Suite Count (2 octets, little endian) * (default: 1) * Authenticated Key Management Suite List (4 * n octets) - * (default: unspec 802.1x) + * (default: unspec 802.1X) * RSN Capabilities (2 octets, little endian) (default: 0) * PMKID Count (2 octets) (default: 0) * PMKID List (16 * n octets) @@ -126,11 +115,93 @@ static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; struct rsn_ie_hdr { u8 elem_id; /* WLAN_EID_RSN */ u8 len; - u16 version; + u8 version[2]; +} __attribute__ ((packed)); + + +struct wpa_eapol_key { + u8 type; + /* Note: key_info, key_length, and key_data_length are unaligned */ + u8 key_info[2]; + u8 key_length[2]; + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + u8 key_nonce[WPA_NONCE_LEN]; + u8 key_iv[16]; + u8 key_rsc[8]; + u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ + u8 key_mic[16]; + u8 key_data_length[2]; + /* followed by key_data_length bytes of key_data */ } __attribute__ ((packed)); +#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) +#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) +#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ +/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ +#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) +#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 +#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ +#define WPA_KEY_INFO_TXRX BIT(6) /* group */ +#define WPA_KEY_INFO_ACK BIT(7) +#define WPA_KEY_INFO_MIC BIT(8) +#define WPA_KEY_INFO_SECURE BIT(9) +#define WPA_KEY_INFO_ERROR BIT(10) +#define WPA_KEY_INFO_REQUEST BIT(11) +#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ + + + +/** + * wpa_cipher_txt - Convert cipher suite to a text string + * @cipher: Cipher suite (WPA_CIPHER_* enum) + * Returns: Pointer to a text string of the cipher suite name + */ +static const char * wpa_cipher_txt(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return "NONE"; + case WPA_CIPHER_WEP40: + return "WEP-40"; + case WPA_CIPHER_WEP104: + return "WEP-104"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_CCMP: + return "CCMP"; + default: + return "UNKNOWN"; + } +} + -static int wpa_selector_to_bitfield(u8 *s) +/** + * wpa_key_mgmt_txt - Convert key management suite to a text string + * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum) + * @proto: WPA/WPA2 version (WPA_PROTO_*) + * Returns: Pointer to a text string of the key management suite name + */ +static const char * wpa_key_mgmt_txt(int key_mgmt, int proto) +{ + switch (key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + return proto == WPA_PROTO_RSN ? + "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; + case WPA_KEY_MGMT_PSK: + return proto == WPA_PROTO_RSN ? + "WPA2-PSK" : "WPA-PSK"; + case WPA_KEY_MGMT_NONE: + return "NONE"; + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: + return "IEEE 802.1X (no WPA)"; + default: + return "UNKNOWN"; + } +} + + +static int wpa_selector_to_bitfield(const u8 *s) { if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0) return WPA_CIPHER_NONE; @@ -146,7 +217,7 @@ static int wpa_selector_to_bitfield(u8 *s) } -static int wpa_key_mgmt_to_bitfield(u8 *s) +static int wpa_key_mgmt_to_bitfield(const u8 *s) { if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0) return WPA_KEY_MGMT_IEEE8021X; @@ -159,7 +230,7 @@ static int wpa_key_mgmt_to_bitfield(u8 *s) } -static int rsn_selector_to_bitfield(u8 *s) +static int rsn_selector_to_bitfield(const u8 *s) { if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0) return WPA_CIPHER_NONE; @@ -175,7 +246,7 @@ static int rsn_selector_to_bitfield(u8 *s) } -static int rsn_key_mgmt_to_bitfield(u8 *s) +static int rsn_key_mgmt_to_bitfield(const u8 *s) { if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0) return WPA_KEY_MGMT_IEEE8021X; @@ -186,217 +257,11 @@ static int rsn_key_mgmt_to_bitfield(u8 *s) } -static void rsn_pmkid(u8 *pmk, u8 *aa, u8 *spa, u8 *pmkid) -{ - char *title = "PMK Name"; - const unsigned char *addr[3]; - const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA1_MAC_LEN]; - - addr[0] = (unsigned char *) title; - addr[1] = aa; - addr[2] = spa; - - hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash); - memcpy(pmkid, hash, PMKID_LEN); -} - - -static void pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s); - - -static void pmksa_cache_free_entry(struct wpa_supplicant *wpa_s, - struct rsn_pmksa_cache *entry) -{ - free(entry); - wpa_s->pmksa_count--; - if (wpa_s->cur_pmksa == entry) { - wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry"); - /* TODO: should drop PMK and PTK and trigger new key - * negotiation */ - wpa_s->cur_pmksa = NULL; - } -} - - -static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) { - struct wpa_supplicant *wpa_s = eloop_ctx; - time_t now; - - time(&now); - while (wpa_s->pmksa && wpa_s->pmksa->expiration <= now) { - struct rsn_pmksa_cache *entry = wpa_s->pmksa; - wpa_s->pmksa = entry->next; - wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " - MACSTR, MAC2STR(entry->aa)); - pmksa_cache_free_entry(wpa_s, entry); - } - - pmksa_cache_set_expiration(wpa_s); -} - - -static void pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s) -{ - int sec; - eloop_cancel_timeout(pmksa_cache_expire, wpa_s, NULL); - if (wpa_s->pmksa == NULL) - return; - sec = wpa_s->pmksa->expiration - time(NULL); - if (sec < 0) - sec = 0; - eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, wpa_s, NULL); -} - - -static void pmksa_cache_add(struct wpa_supplicant *wpa_s, u8 *pmk, - size_t pmk_len, u8 *aa, u8 *spa) -{ - struct rsn_pmksa_cache *entry, *pos, *prev; - - if (wpa_s->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN) - return; - - entry = malloc(sizeof(*entry)); - if (entry == NULL) - return; - memset(entry, 0, sizeof(*entry)); - memcpy(entry->pmk, pmk, pmk_len); - entry->pmk_len = pmk_len; - rsn_pmkid(pmk, aa, spa, entry->pmkid); - entry->expiration = time(NULL) + dot11RSNAConfigPMKLifetime; - entry->akmp = WPA_KEY_MGMT_IEEE8021X; - memcpy(entry->aa, aa, ETH_ALEN); - - /* Replace an old entry for the same Authenticator (if found) with the - * new entry */ - pos = wpa_s->pmksa; - prev = NULL; - while (pos) { - if (memcmp(aa, pos->aa, ETH_ALEN) == 0) { - if (prev == NULL) - wpa_s->pmksa = pos->next; - else - prev->next = pos->next; - pmksa_cache_free_entry(wpa_s, pos); - break; - } - prev = pos; - pos = pos->next; - } - - if (wpa_s->pmksa_count >= pmksa_cache_max_entries && wpa_s->pmksa) { - /* Remove the oldest entry to make room for the new entry */ - pos = wpa_s->pmksa; - wpa_s->pmksa = pos->next; - wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " - "entry (for " MACSTR ") to make room for new one", - MAC2STR(pos->aa)); - wpa_drv_remove_pmkid(wpa_s, pos->aa, pos->pmkid); - pmksa_cache_free_entry(wpa_s, pos); - } - - /* Add the new entry; order by expiration time */ - pos = wpa_s->pmksa; - prev = NULL; - while (pos) { - if (pos->expiration > entry->expiration) - break; - prev = pos; - pos = pos->next; - } - if (prev == NULL) { - entry->next = wpa_s->pmksa; - wpa_s->pmksa = entry; - } else { - entry->next = prev->next; - prev->next = entry; - } - wpa_s->pmksa_count++; - wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, - MAC2STR(entry->aa)); - wpa_drv_add_pmkid(wpa_s, entry->aa, entry->pmkid); -} - - -void pmksa_cache_free(struct wpa_supplicant *wpa_s) -{ - struct rsn_pmksa_cache *entry, *prev; - - entry = wpa_s->pmksa; - wpa_s->pmksa = NULL; - while (entry) { - prev = entry; - entry = entry->next; - free(prev); - } - pmksa_cache_set_expiration(wpa_s); - wpa_s->cur_pmksa = NULL; -} - - -struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_supplicant *wpa_s, - u8 *aa, u8 *pmkid) -{ - struct rsn_pmksa_cache *entry = wpa_s->pmksa; - while (entry) { - if ((aa == NULL || memcmp(entry->aa, aa, ETH_ALEN) == 0) && - (pmkid == NULL || - memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) - return entry; - entry = entry->next; - } - return NULL; -} - - -int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len) -{ - int i, j; - char *pos = buf; - struct rsn_pmksa_cache *entry; - time_t now; - - time(&now); - pos += snprintf(pos, buf + len - pos, - "Index / AA / PMKID / expiration (in seconds)\n"); - i = 0; - entry = wpa_s->pmksa; - while (entry) { - i++; - pos += snprintf(pos, buf + len - pos, "%d " MACSTR " ", - i, MAC2STR(entry->aa)); - for (j = 0; j < PMKID_LEN; j++) - pos += snprintf(pos, buf + len - pos, "%02x", - entry->pmkid[j]); - pos += snprintf(pos, buf + len - pos, " %d\n", - (int) (entry->expiration - now)); - entry = entry->next; - } - return pos - buf; -} - - -void pmksa_candidate_free(struct wpa_supplicant *wpa_s) -{ - struct rsn_pmksa_candidate *entry, *prev; - - entry = wpa_s->pmksa_candidates; - wpa_s->pmksa_candidates = NULL; - while (entry) { - prev = entry; - entry = entry->next; - free(prev); - } -} - - -static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie, - size_t wpa_ie_len, struct wpa_ie_data *data) -{ - struct wpa_ie_hdr *hdr; - u8 *pos; + const struct wpa_ie_hdr *hdr; + const u8 *pos; int left; int i, count; @@ -419,18 +284,18 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie, return -1; } - hdr = (struct wpa_ie_hdr *) wpa_ie; + hdr = (const struct wpa_ie_hdr *) wpa_ie; if (hdr->elem_id != GENERIC_INFO_ELEM || hdr->len != wpa_ie_len - 2 || memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 || - le_to_host16(hdr->version) != WPA_VERSION) { + WPA_GET_LE16(hdr->version) != WPA_VERSION) { wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", __func__); return -1; } - pos = (u8 *) (hdr + 1); + pos = (const u8 *) (hdr + 1); left = wpa_ie_len - sizeof(*hdr); if (left >= WPA_SELECTOR_LEN) { @@ -445,7 +310,7 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie, if (left >= 2) { data->pairwise_cipher = 0; - count = pos[0] | (pos[1] << 8); + count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * WPA_SELECTOR_LEN) { @@ -466,7 +331,7 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie, if (left >= 2) { data->key_mgmt = 0; - count = pos[0] | (pos[1] << 8); + count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * WPA_SELECTOR_LEN) { @@ -486,7 +351,7 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie, } if (left >= 2) { - data->capabilities = pos[0] | (pos[1] << 8); + data->capabilities = WPA_GET_LE16(pos); pos += 2; left -= 2; } @@ -501,11 +366,11 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie, } -static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie, - size_t rsn_ie_len, struct wpa_ie_data *data) +static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data) { - struct rsn_ie_hdr *hdr; - u8 *pos; + const struct rsn_ie_hdr *hdr; + const u8 *pos; int left; int i, count; @@ -528,17 +393,17 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie, return -1; } - hdr = (struct rsn_ie_hdr *) rsn_ie; + hdr = (const struct rsn_ie_hdr *) rsn_ie; if (hdr->elem_id != RSN_INFO_ELEM || hdr->len != rsn_ie_len - 2 || - le_to_host16(hdr->version) != RSN_VERSION) { + WPA_GET_LE16(hdr->version) != RSN_VERSION) { wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", __func__); return -1; } - pos = (u8 *) (hdr + 1); + pos = (const u8 *) (hdr + 1); left = rsn_ie_len - sizeof(*hdr); if (left >= RSN_SELECTOR_LEN) { @@ -553,7 +418,7 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie, if (left >= 2) { data->pairwise_cipher = 0; - count = pos[0] | (pos[1] << 8); + count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * RSN_SELECTOR_LEN) { @@ -574,7 +439,7 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie, if (left >= 2) { data->key_mgmt = 0; - count = pos[0] | (pos[1] << 8); + count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * RSN_SELECTOR_LEN) { @@ -594,13 +459,13 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie, } if (left >= 2) { - data->capabilities = pos[0] | (pos[1] << 8); + data->capabilities = WPA_GET_LE16(pos); pos += 2; left -= 2; } if (left >= 2) { - data->num_pmkid = pos[0] | (pos[1] << 8); + data->num_pmkid = WPA_GET_LE16(pos); pos += 2; left -= 2; if (left < data->num_pmkid * PMKID_LEN) { @@ -624,69 +489,84 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie, } -int wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie, - size_t wpa_ie_len, struct wpa_ie_data *data) +/** + * wpa_parse_wpa_ie - Parse WPA/RSN IE + * @wpa_ie: Pointer to WPA or RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 on failure + * + * Parse the contents of WPA or RSN IE and write the parsed data into data. + */ +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) { if (wpa_ie_len >= 1 && wpa_ie[0] == RSN_INFO_ELEM) - return wpa_parse_wpa_ie_rsn(wpa_s, wpa_ie, wpa_ie_len, data); + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); else - return wpa_parse_wpa_ie_wpa(wpa_s, wpa_ie, wpa_ie_len, data); + return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); } -static int wpa_gen_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie) +static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt) { u8 *pos; struct wpa_ie_hdr *hdr; + if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) + return -1; + hdr = (struct wpa_ie_hdr *) wpa_ie; hdr->elem_id = GENERIC_INFO_ELEM; memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN); - hdr->version = host_to_le16(WPA_VERSION); + WPA_PUT_LE16(hdr->version, WPA_VERSION); pos = (u8 *) (hdr + 1); - if (wpa_s->group_cipher == WPA_CIPHER_CCMP) { + if (group_cipher == WPA_CIPHER_CCMP) { memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) { + } else if (group_cipher == WPA_CIPHER_TKIP) { memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) { + } else if (group_cipher == WPA_CIPHER_WEP104) { memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); - } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) { + } else if (group_cipher == WPA_CIPHER_WEP40) { memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN); } else { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", - wpa_s->group_cipher); + group_cipher); return -1; } pos += WPA_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; - if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) { + if (pairwise_cipher == WPA_CIPHER_CCMP) { memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) { + } else if (pairwise_cipher == WPA_CIPHER_TKIP) { memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) { + } else if (pairwise_cipher == WPA_CIPHER_NONE) { memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN); } else { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", - wpa_s->pairwise_cipher); + pairwise_cipher); return -1; } pos += WPA_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; - if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN); - } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) { + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN); - } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { memcpy(pos, WPA_AUTH_KEY_MGMT_NONE, WPA_SELECTOR_LEN); } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", - wpa_s->key_mgmt); + key_mgmt); return -1; } pos += WPA_SELECTOR_LEN; @@ -695,60 +575,69 @@ static int wpa_gen_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie) hdr->len = (pos - wpa_ie) - 2; + WPA_ASSERT(pos - wpa_ie <= wpa_ie_len); + return pos - wpa_ie; } -static int wpa_gen_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie) +static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt, struct wpa_sm *sm) { u8 *pos; struct rsn_ie_hdr *hdr; + if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + + (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) + return -1; + hdr = (struct rsn_ie_hdr *) rsn_ie; hdr->elem_id = RSN_INFO_ELEM; - hdr->version = host_to_le16(RSN_VERSION); + WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); - if (wpa_s->group_cipher == WPA_CIPHER_CCMP) { + if (group_cipher == WPA_CIPHER_CCMP) { memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) { + } else if (group_cipher == WPA_CIPHER_TKIP) { memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) { + } else if (group_cipher == WPA_CIPHER_WEP104) { memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); - } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) { + } else if (group_cipher == WPA_CIPHER_WEP40) { memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN); } else { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", - wpa_s->group_cipher); + group_cipher); return -1; } pos += RSN_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; - if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) { + if (pairwise_cipher == WPA_CIPHER_CCMP) { memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) { + } else if (pairwise_cipher == WPA_CIPHER_TKIP) { memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) { + } else if (pairwise_cipher == WPA_CIPHER_NONE) { memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN); } else { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", - wpa_s->pairwise_cipher); + pairwise_cipher); return -1; } pos += RSN_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; - if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN); - } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) { + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN); } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", - wpa_s->key_mgmt); + key_mgmt); return -1; } pos += RSN_SELECTOR_LEN; @@ -757,39 +646,67 @@ static int wpa_gen_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie) *pos++ = 0; *pos++ = 0; - if (wpa_s->cur_pmksa) { + if (sm->cur_pmksa) { /* PMKID Count (2 octets, little endian) */ *pos++ = 1; *pos++ = 0; /* PMKID */ - memcpy(pos, wpa_s->cur_pmksa->pmkid, PMKID_LEN); + memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN); pos += PMKID_LEN; } hdr->len = (pos - rsn_ie) - 2; + WPA_ASSERT(pos - rsn_ie <= rsn_ie_len); + return pos - rsn_ie; } -int wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie) +/** + * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE + * @wpa_ie_len: Maximum length of the generated WPA/RSN IE + * Returns: Length of the generated WPA/RSN IE or -1 on failure + */ +static int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) { - if (wpa_s->proto == WPA_PROTO_RSN) - return wpa_gen_wpa_ie_rsn(wpa_s, wpa_ie); + if (sm->proto == WPA_PROTO_RSN) + return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt, sm); else - return wpa_gen_wpa_ie_wpa(wpa_s, wpa_ie); + return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt); } -static void wpa_pmk_to_ptk(u8 *pmk, size_t pmk_len, u8 *addr1, u8 *addr2, - u8 *nonce1, u8 *nonce2, u8 *ptk, size_t ptk_len) +/** + * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces + * @pmk: Pairwise master key + * @addr1: AA or SA + * @addr2: SA or AA + * @nonce1: ANonce or SNonce + * @nonce2: SNonce or ANonce + * @ptk: Buffer for pairwise transient key + * @ptk_len: Length of PTK + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PTK = PRF-X(PMK, "Pairwise key expansion", + * Min(AA, SA) || Max(AA, SA) || + * Min(ANonce, SNonce) || Max(ANonce, SNonce)) + */ +static void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len) { u8 data[2 * ETH_ALEN + 2 * 32]; - /* PTK = PRF-X(PMK, "Pairwise key expansion", - * Min(AA, SA) || Max(AA, SA) || - * Min(ANonce, SNonce) || Max(ANonce, SNonce)) */ - if (memcmp(addr1, addr2, ETH_ALEN) < 0) { memcpy(data, addr1, ETH_ALEN); memcpy(data + ETH_ALEN, addr2, ETH_ALEN); @@ -814,39 +731,25 @@ static void wpa_pmk_to_ptk(u8 *pmk, size_t pmk_len, u8 *addr1, u8 *addr2, } -struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) -{ - struct wpa_ssid *entry; - u8 ssid[MAX_SSID_LEN]; - int ssid_len; - u8 bssid[ETH_ALEN]; - - ssid_len = wpa_drv_get_ssid(wpa_s, ssid); - if (ssid_len < 0) { - wpa_printf(MSG_WARNING, "Could not read SSID from driver."); - return NULL; - } - - if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { - wpa_printf(MSG_WARNING, "Could not read BSSID from driver."); - return NULL; - } - - entry = wpa_s->conf->ssid; - while (entry) { - if (ssid_len == entry->ssid_len && - memcmp(ssid, entry->ssid, ssid_len) == 0 && - (!entry->bssid_set || - memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) - return entry; - entry = entry->next; - } - - return NULL; -} - - -static void wpa_eapol_key_mic(u8 *key, int ver, u8 *buf, size_t len, u8 *mic) +/** + * wpa_eapol_key_mic - Calculate EAPOL-Key MIC + * @key: EAPOL-Key Key Confirmation Key (KCK) + * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*) + * @buf: Pointer to the beginning of the EAPOL header (version field) + * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame) + * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written + * + * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has + * to be cleared (all zeroes) when calling this function. + * + * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the + * description of the Key MIC calculation. It includes packet data from the + * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change + * happened during final editing of the standard and the correct behavior is + * defined in the last draft (IEEE 802.11i/D10). + */ +static void wpa_eapol_key_mic(const u8 *key, int ver, + const u8 *buf, size_t len, u8 *mic) { if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { hmac_md5(key, 16, buf, len, mic); @@ -858,200 +761,185 @@ static void wpa_eapol_key_mic(u8 *key, int ver, u8 *buf, size_t len, u8 *mic) } -void wpa_supplicant_key_request(struct wpa_supplicant *wpa_s, - int error, int pairwise) +static void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) { - int rlen; - struct ieee802_1x_hdr *hdr; + if (key_mic) { + wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic); + } + wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); + wpa_sm_ether_send(sm, dest, proto, msg, msg_len); + eapol_sm_notify_tx_eapol_key(sm->eapol); + free(msg); +} + + +/** + * wpa_sm_key_request - Send EAPOL-Key Request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @error: Indicate whether this is an Michael MIC error report + * @pairwise: 1 = error report for pairwise packet, 0 = for group packet + * Returns: Pointer to the current network structure or %NULL on failure + * + * Send an EAPOL-Key Request to the current authenticator. This function is + * used to request rekeying and it is usually called when a local Michael MIC + * failure is detected. + */ +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) +{ + size_t rlen; struct wpa_eapol_key *reply; - unsigned char *rbuf; - struct l2_ethhdr *ethhdr; int key_info, ver; - u8 bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN], *rbuf; - if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) + if (sm->pairwise_cipher == WPA_CIPHER_CCMP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + if (wpa_sm_get_bssid(sm, bssid) < 0) { wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " "request"); return; } - rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply); - rbuf = malloc(rlen); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); if (rbuf == NULL) return; - memset(rbuf, 0, rlen); - ethhdr = (struct l2_ethhdr *) rbuf; - memcpy(ethhdr->h_dest, bssid, ETH_ALEN); - memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_EAPOL); - - hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - hdr->version = wpa_s->conf->eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(sizeof(*reply)); - - reply = (struct wpa_eapol_key *) (hdr + 1); - reply->type = wpa_s->proto == WPA_PROTO_RSN ? + reply->type = sm->proto == WPA_PROTO_RSN ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; - if (wpa_s->ptk_set) + if (sm->ptk_set) key_info |= WPA_KEY_INFO_MIC; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) key_info |= WPA_KEY_INFO_KEY_TYPE; - reply->key_info = host_to_be16(key_info); - reply->key_length = 0; - memcpy(reply->replay_counter, wpa_s->request_counter, + WPA_PUT_BE16(reply->key_info, key_info); + WPA_PUT_BE16(reply->key_length, 0); + memcpy(reply->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); - inc_byte_array(wpa_s->request_counter, WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - reply->key_data_length = host_to_be16(0); - - if (key_info & WPA_KEY_INFO_MIC) { - wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr, - rlen - sizeof(*ethhdr), reply->key_mic); - } + WPA_PUT_BE16(reply->key_data_length, 0); wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " - "pairwise=%d ptk_set=%d len=%d)", - error, pairwise, wpa_s->ptk_set, rlen); - wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key Request", rbuf, rlen); - l2_packet_send(wpa_s->l2, rbuf, rlen); - eapol_sm_notify_tx_eapol_key(wpa_s->eapol); - free(rbuf); + "pairwise=%d ptk_set=%d len=%lu)", + error, pairwise, sm->ptk_set, (unsigned long) rlen); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, + rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? + reply->key_mic : NULL); } -static void wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s, - unsigned char *src_addr, - struct wpa_eapol_key *key, int ver) -{ - int rlen; - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *reply; - unsigned char *rbuf; - struct l2_ethhdr *ethhdr; - struct wpa_ssid *ssid; - struct wpa_ptk *ptk; - u8 buf[8], wpa_ie_buf[80], *wpa_ie, *pmkid = NULL; - int wpa_ie_len; - int abort_cached = 0; +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; +}; - wpa_s->wpa_state = WPA_4WAY_HANDSHAKE; - wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(src_addr), ver); - ssid = wpa_supplicant_get_ssid(wpa_s); - if (ssid == NULL) { - wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of " - "4)."); - return; +/** + * wpa_supplicant_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_supplicant_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + memcmp(pos + 2, WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0 && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; } - if (wpa_s->proto == WPA_PROTO_RSN) { - /* RSN: msg 1/4 should contain PMKID for the selected PMK */ - u8 *pos = (u8 *) (key + 1); - u8 *end = pos + be_to_host16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", - pos, be_to_host16(key->key_data_length)); - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) { - wpa_printf(MSG_DEBUG, "RSN: key data " - "underflow (ie=%d len=%d)", - pos[0], pos[1]); - break; - } - if (pos[0] == GENERIC_INFO_ELEM && - pos + 1 + RSN_SELECTOR_LEN < end && - pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && - memcmp(pos + 2, RSN_KEY_DATA_PMKID, - RSN_SELECTOR_LEN) == 0) { - pmkid = pos + 2 + RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " - "Authenticator", pmkid, PMKID_LEN); - break; - } else if (pos[0] == GENERIC_INFO_ELEM && - pos[1] == 0) - break; - pos += 2 + pos[1]; - } + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + memcmp(pos + 2, RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN) == 0) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; } - if (wpa_s->assoc_wpa_ie) { - /* The driver reported a WPA IE that may be different from the - * one that the Supplicant would use. Message 2/4 has to use - * the exact copy of the WPA IE from the Association Request, - * so use the value reported by the driver. */ - wpa_ie = wpa_s->assoc_wpa_ie; - wpa_ie_len = wpa_s->assoc_wpa_ie_len; - } else { - wpa_ie = wpa_ie_buf; - wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie); - if (wpa_ie_len < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to generate " - "WPA IE (for msg 2 of 4)."); - return; - } - wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", - wpa_ie, wpa_ie_len); + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN) == 0) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; } - rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply) + wpa_ie_len; - rbuf = malloc(rlen); - if (rbuf == NULL) - return; - - memset(rbuf, 0, rlen); - ethhdr = (struct l2_ethhdr *) rbuf; - memcpy(ethhdr->h_dest, src_addr, ETH_ALEN); - memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_EAPOL); - - hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - hdr->version = wpa_s->conf->eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(sizeof(*reply) + wpa_ie_len); + return 0; +} - reply = (struct wpa_eapol_key *) (hdr + 1); - reply->type = wpa_s->proto == WPA_PROTO_RSN ? - EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE | - WPA_KEY_INFO_MIC); - reply->key_length = key->key_length; - memcpy(reply->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - reply->key_data_length = host_to_be16(wpa_ie_len); - memcpy(reply + 1, wpa_ie, wpa_ie_len); +/** + * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +static int wpa_supplicant_parse_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; - if (wpa_s->renew_snonce) { - if (hostapd_get_rand(wpa_s->snonce, WPA_NONCE_LEN)) { - wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to get " - "random data for SNonce"); - free(rbuf); - return; + memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d)", pos[0], pos[1]); + ret = -1; + break; + } + if (*pos == RSN_INFO_ELEM) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; + } else if (*pos == GENERIC_INFO_ELEM) { + ret = wpa_supplicant_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); } - wpa_s->renew_snonce = 0; - wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", - wpa_s->snonce, WPA_NONCE_LEN); } - memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN); - ptk = &wpa_s->tptk; - memcpy(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN); - if (pmkid && !wpa_s->cur_pmksa) { + + return ret; +} + + +static int wpa_supplicant_get_pmk(struct wpa_sm *sm, + const unsigned char *src_addr, + const u8 *pmkid) +{ + int abort_cached = 0; + + if (pmkid && !sm->cur_pmksa) { /* When using drivers that generate RSN IE, wpa_supplicant may * not have enough time to get the association information * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ - wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, src_addr, pmkid); - if (wpa_s->cur_pmksa) { + sm->cur_pmksa = pmksa_cache_get(sm, src_addr, pmkid); + if (sm->cur_pmksa) { wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from " "PMKSA cache"); } else { @@ -1060,125 +948,241 @@ static void wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s, } } - if (pmkid && wpa_s->cur_pmksa && - memcmp(pmkid, wpa_s->cur_pmksa->pmkid, PMKID_LEN) == 0) { + if (pmkid && sm->cur_pmksa && + memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); - memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, PMK_LEN); + wpa_sm_set_pmk_from_pmksa(sm); wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", - wpa_s->pmk, PMK_LEN); - eapol_sm_notify_cached(wpa_s->eapol); - } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X && wpa_s->eapol) { + sm->pmk, sm->pmk_len); + eapol_sm_notify_cached(sm->eapol); + } else if (sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X && sm->eapol) { int res, pmk_len; pmk_len = PMK_LEN; - res = eapol_sm_get_key(wpa_s->eapol, wpa_s->pmk, PMK_LEN); + res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); #ifdef EAP_LEAP if (res) { - res = eapol_sm_get_key(wpa_s->eapol, wpa_s->pmk, 16); + res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); pmk_len = 16; } #endif /* EAP_LEAP */ if (res == 0) { wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " - "machines", wpa_s->pmk, pmk_len); - wpa_s->pmk_len = pmk_len; - pmksa_cache_add(wpa_s, wpa_s->pmk, pmk_len, src_addr, - wpa_s->own_addr); - if (!wpa_s->cur_pmksa && pmkid && - pmksa_cache_get(wpa_s, src_addr, pmkid)) { + "machines", sm->pmk, pmk_len); + sm->pmk_len = pmk_len; + pmksa_cache_add(sm, sm->pmk, pmk_len, src_addr, + sm->own_addr, sm->cur_ssid); + if (!sm->cur_pmksa && pmkid && + pmksa_cache_get(sm, src_addr, pmkid)) { wpa_printf(MSG_DEBUG, "RSN: the new PMK " "matches with the PMKID"); abort_cached = 0; } } else { - wpa_msg(wpa_s, MSG_WARNING, + wpa_msg(sm->ctx->ctx, MSG_WARNING, "WPA: Failed to get master session key from " "EAPOL state machines"); - wpa_msg(wpa_s, MSG_WARNING, + wpa_msg(sm->ctx->ctx, MSG_WARNING, "WPA: Key handshake aborted"); - if (wpa_s->cur_pmksa) { + if (sm->cur_pmksa) { wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA " "caching attempt"); - wpa_s->cur_pmksa = NULL; + sm->cur_pmksa = NULL; abort_cached = 1; } else { - free(rbuf); - return; + return -1; } } -#ifdef CONFIG_XSUPPLICANT_IFACE - } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X && - !wpa_s->ext_pmk_received) { - wpa_printf(MSG_INFO, "WPA: Master session has not yet " - "been received from the external IEEE " - "802.1X Supplicant - ignoring WPA " - "EAPOL-Key frame"); - free(rbuf); - return; -#endif /* CONFIG_XSUPPLICANT_IFACE */ } - if (abort_cached && wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + if (abort_cached && sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X) { /* Send EAPOL-Start to trigger full EAP authentication. */ + u8 *buf; + size_t buflen; + wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger " - "full EAP authenication"); - wpa_eapol_send(wpa_s, IEEE802_1X_TYPE_EAPOL_START, - (u8 *) "", 0); - free(rbuf); - return; + "full EAP authentication"); + buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, + NULL, 0, &buflen, NULL); + if (buf) { + wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL, + buf, buflen); + free(buf); + } + + return -1; + } + + return 0; +} + + +static int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int ver) +{ + size_t rlen; + struct wpa_eapol_key *reply; + struct wpa_ptk *ptk; + u8 buf[8], *rbuf, *wpa_ie; + int wpa_ie_len; + + if (sm->assoc_wpa_ie == NULL) { + wpa_printf(MSG_WARNING, "WPA: No assoc_wpa_ie set - cannot " + "generate msg 2/4"); + return -1; + } + + wpa_ie = sm->assoc_wpa_ie; + wpa_ie_len = sm->assoc_wpa_ie_len; + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, + NULL, sizeof(*reply) + wpa_ie_len, + &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + WPA_PUT_BE16(reply->key_info, + ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + memcpy(reply->key_length, key->key_length, 2); + memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); + memcpy(reply + 1, wpa_ie, wpa_ie_len); + + if (sm->renew_snonce) { + if (hostapd_get_rand(sm->snonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "WPA: Failed to get random data for SNonce"); + free(rbuf); + return -1; + } + sm->renew_snonce = 0; + wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", + sm->snonce, WPA_NONCE_LEN); } + memcpy(reply->key_nonce, sm->snonce, WPA_NONCE_LEN); - wpa_pmk_to_ptk(wpa_s->pmk, wpa_s->pmk_len, wpa_s->own_addr, src_addr, - wpa_s->snonce, key->key_nonce, + /* Calculate PTK which will be stored as a temporary PTK until it has + * been verified when processing message 3/4. */ + ptk = &sm->tptk; + wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, src_addr, + sm->snonce, key->key_nonce, (u8 *) ptk, sizeof(*ptk)); /* Supplicant: swap tx/rx Mic keys */ memcpy(buf, ptk->u.auth.tx_mic_key, 8); memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); memcpy(ptk->u.auth.rx_mic_key, buf, 8); - wpa_s->tptk_set = 1; - wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, (u8 *) hdr, - rlen - sizeof(*ethhdr), reply->key_mic); - wpa_hexdump(MSG_DEBUG, "WPA: EAPOL-Key MIC", reply->key_mic, 16); + sm->tptk_set = 1; wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); - wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/4", rbuf, rlen); - l2_packet_send(wpa_s->l2, rbuf, rlen); - eapol_sm_notify_tx_eapol_key(wpa_s->eapol); - free(rbuf); + wpa_eapol_key_send(sm, ptk->kck, ver, src_addr, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + +static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse ie; + + if (wpa_sm_get_ssid(sm) == NULL) { + wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of " + "4)."); + return; + } + + wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); + wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + + memset(&ie, 0, sizeof(ie)); + + if (sm->proto == WPA_PROTO_RSN) { + /* RSN: msg 1/4 should contain PMKID for the selected PMK */ + const u8 *buf = (const u8 *) (key + 1); + size_t len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", buf, len); + wpa_supplicant_parse_ies(buf, len, &ie); + if (ie.pmkid) { + wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " + "Authenticator", ie.pmkid, PMKID_LEN); + } + } + + if (wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid)) + return; + + if (wpa_supplicant_send_2_of_4(sm, src_addr, key, ver)) + return; + + memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); } -static void wpa_supplicant_key_neg_complete(struct wpa_supplicant *wpa_s, - u8 *addr, int secure) +static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) { - wpa_msg(wpa_s, MSG_INFO, "WPA: Key negotiation completed with " + struct wpa_sm *sm = eloop_ctx; + rsn_preauth_candidate_process(sm); +} + + +static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, + const u8 *addr, int secure) +{ + wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Key negotiation completed with " MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), - wpa_cipher_txt(wpa_s->pairwise_cipher), - wpa_cipher_txt(wpa_s->group_cipher)); - eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); - wpa_supplicant_cancel_auth_timeout(wpa_s); - wpa_s->wpa_state = WPA_COMPLETED; + wpa_cipher_txt(sm->pairwise_cipher), + wpa_cipher_txt(sm->group_cipher)); + eloop_cancel_timeout(sm->ctx->scan, sm->ctx->ctx, NULL); + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(sm, WPA_COMPLETED); if (secure) { /* MLME.SETPROTECTION.request(TA, Tx_Rx) */ - eapol_sm_notify_portValid(wpa_s->eapol, TRUE); - if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) - eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); - rsn_preauth_candidate_process(wpa_s); + eapol_sm_notify_portValid(sm->eapol, TRUE); + if (sm->key_mgmt == WPA_KEY_MGMT_PSK) + eapol_sm_notify_eap_success(sm->eapol, TRUE); + /* + * Start preauthentication after a short wait to avoid a + * possible race condition between the data receive and key + * configuration after the 4-Way Handshake. This increases the + * likelyhood of the first preauth EAPOL-Start frame getting to + * the target AP. + */ + eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); + } + + if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { + wpa_printf(MSG_DEBUG, "RSN: Authenticator accepted " + "opportunistic PMKSA entry - marking it valid"); + sm->cur_pmksa->opportunistic = 0; } } -static int wpa_supplicant_install_ptk(struct wpa_supplicant *wpa_s, - unsigned char *src_addr, - struct wpa_eapol_key *key) +static int wpa_supplicant_install_ptk(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key) { int alg, keylen, rsclen; - u8 *key_rsc; + const u8 *key_rsc; u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver."); - switch (wpa_s->pairwise_cipher) { + switch (sm->pairwise_cipher) { case WPA_CIPHER_CCMP: alg = WPA_ALG_CCMP; keylen = 16; @@ -1195,20 +1199,19 @@ static int wpa_supplicant_install_ptk(struct wpa_supplicant *wpa_s, return 0; default: wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d", - wpa_s->pairwise_cipher); + sm->pairwise_cipher); return -1; } - if (wpa_s->proto == WPA_PROTO_RSN) { + if (sm->proto == WPA_PROTO_RSN) { key_rsc = null_rsc; } else { key_rsc = key->key_rsc; wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); } - wpa_s->keys_cleared = 0; - if (wpa_drv_set_key(wpa_s, alg, src_addr, 0, 1, key_rsc, rsclen, - (u8 *) &wpa_s->ptk.tk1, keylen) < 0) { + if (wpa_sm_set_key(sm, alg, src_addr, 0, 1, key_rsc, rsclen, + (u8 *) &sm->ptk.tk1, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the " "driver."); return -1; @@ -1217,91 +1220,99 @@ static int wpa_supplicant_install_ptk(struct wpa_supplicant *wpa_s, } -static int wpa_supplicant_check_group_cipher(struct wpa_supplicant *wpa_s, +static int wpa_supplicant_check_group_cipher(int group_cipher, int keylen, int maxkeylen, int *key_rsc_len, int *alg) { - switch (wpa_s->group_cipher) { + int ret = 0; + + switch (group_cipher) { case WPA_CIPHER_CCMP: if (keylen != 16 || maxkeylen < 16) { - wpa_printf(MSG_WARNING, "WPA: Unsupported CCMP Group " - "Cipher key length %d (%d).", - keylen, maxkeylen); - return -1; + ret = -1; + break; } *key_rsc_len = 6; *alg = WPA_ALG_CCMP; break; case WPA_CIPHER_TKIP: if (keylen != 32 || maxkeylen < 32) { - wpa_printf(MSG_WARNING, "WPA: Unsupported TKIP Group " - "Cipher key length %d (%d).", - keylen, maxkeylen); - return -1; + ret = -1; + break; } *key_rsc_len = 6; *alg = WPA_ALG_TKIP; break; case WPA_CIPHER_WEP104: if (keylen != 13 || maxkeylen < 13) { - wpa_printf(MSG_WARNING, "WPA: Unsupported WEP104 Group" - " Cipher key length %d (%d).", - keylen, maxkeylen); - return -1; + ret = -1; + break; } *key_rsc_len = 0; *alg = WPA_ALG_WEP; break; case WPA_CIPHER_WEP40: if (keylen != 5 || maxkeylen < 5) { - wpa_printf(MSG_WARNING, "WPA: Unsupported WEP40 Group " - "Cipher key length %d (%d).", - keylen, maxkeylen); - return -1; + ret = -1; + break; } *key_rsc_len = 0; *alg = WPA_ALG_WEP; break; default: - wpa_printf(MSG_WARNING, "WPA: Unsupport Group Cipher %d", - wpa_s->group_cipher); + wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", + group_cipher); return -1; } - return 0; + if (ret < 0 ) { + wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key " + "length %d (%d).", + wpa_cipher_txt(group_cipher), keylen, maxkeylen); + } + + return ret; } -static int wpa_supplicant_install_gtk(struct wpa_supplicant *wpa_s, - struct wpa_eapol_key *key, int alg, - u8 *gtk, int gtk_len, int keyidx, - int key_rsc_len, int tx) +struct wpa_gtk_data { + int alg, tx, key_rsc_len, keyidx; + u8 gtk[32]; + int gtk_len; +}; + + +static int wpa_supplicant_install_gtk(struct wpa_sm *sm, + const struct wpa_gtk_data *gd, + const u8 *key_rsc) { - wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gtk, gtk_len); + const u8 *_gtk = gd->gtk; + u8 gtk_buf[32]; + + wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver " - "(keyidx=%d tx=%d).", keyidx, tx); - wpa_hexdump(MSG_DEBUG, "WPA: RSC", key->key_rsc, key_rsc_len); - if (wpa_s->group_cipher == WPA_CIPHER_TKIP) { + "(keyidx=%d tx=%d).", gd->keyidx, gd->tx); + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); + if (sm->group_cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ - u8 tmpbuf[8]; - memcpy(tmpbuf, gtk + 16, 8); - memcpy(gtk + 16, gtk + 24, 8); - memcpy(gtk + 24, tmpbuf, 8); - } - wpa_s->keys_cleared = 0; - if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) { - if (wpa_drv_set_key(wpa_s, alg, - (u8 *) "\xff\xff\xff\xff\xff\xff", - keyidx, 1, key->key_rsc, key_rsc_len, - gtk, gtk_len) < 0) { + memcpy(gtk_buf, gd->gtk, 16); + memcpy(gtk_buf + 16, gd->gtk + 24, 8); + memcpy(gtk_buf + 24, gd->gtk + 16, 8); + _gtk = gtk_buf; + } + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + if (wpa_sm_set_key(sm, gd->alg, + (u8 *) "\xff\xff\xff\xff\xff\xff", + gd->keyidx, 1, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set " "GTK to the driver (Group only)."); return -1; } - } else if (wpa_drv_set_key(wpa_s, alg, - (u8 *) "\xff\xff\xff\xff\xff\xff", - keyidx, tx, key->key_rsc, key_rsc_len, - gtk, gtk_len) < 0) { + } else if (wpa_sm_set_key(sm, gd->alg, + (u8 *) "\xff\xff\xff\xff\xff\xff", + gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to " "the driver."); return -1; @@ -1311,61 +1322,83 @@ static int wpa_supplicant_install_gtk(struct wpa_supplicant *wpa_s, } -static int wpa_supplicant_pairwise_gtk(struct wpa_supplicant *wpa_s, - unsigned char *src_addr, - struct wpa_eapol_key *key, - u8 *gtk, int gtk_len, int key_info) +static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, + int tx) { - int keyidx, tx, key_rsc_len = 0, alg; - - wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", - gtk, gtk_len); - - keyidx = gtk[0] & 0x3; - tx = !!(gtk[0] & BIT(2)); - if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) { - /* Ignore Tx bit in GTK IE if a pairwise key is used. One AP + if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) { + /* Ignore Tx bit for GTK if a pairwise key is used. One AP * seemed to set this bit (incorrectly, since Tx is only when * doing Group Key only APs) and without this workaround, the * data connection does not work because wpa_supplicant * configured non-zero keyidx to be used for unicast. */ - wpa_printf(MSG_INFO, "RSN: Tx bit set for GTK IE, but " - "pairwise keys are used - ignore Tx bit"); - tx = 0; + wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but pairwise " + "keys are used - ignore Tx bit"); + return 0; } + return tx; +} + + +static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + const u8 *gtk, int gtk_len, + int key_info) +{ + struct wpa_gtk_data gd; + + /* + * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x + * GTK KDE format: + * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7] + * Reserved [bits 0-7] + * GTK + */ + + memset(&gd, 0, sizeof(gd)); + wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", + gtk, gtk_len); + + if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk)) + return -1; + + gd.keyidx = gtk[0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(gtk[0] & BIT(2))); gtk += 2; gtk_len -= 2; - if (wpa_supplicant_check_group_cipher(wpa_s, gtk_len, gtk_len, - &key_rsc_len, &alg)) { - return -1; - } + memcpy(gd.gtk, gtk, gtk_len); + gd.gtk_len = gtk_len; - if (wpa_supplicant_install_gtk(wpa_s, key, alg, gtk, gtk_len, keyidx, - key_rsc_len, tx)) { + if (wpa_supplicant_check_group_cipher(sm->group_cipher, + gtk_len, gtk_len, + &gd.key_rsc_len, &gd.alg) || + wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) { + wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK"); return -1; } - wpa_supplicant_key_neg_complete(wpa_s, src_addr, + wpa_supplicant_key_neg_complete(sm, src_addr, key_info & WPA_KEY_INFO_SECURE); return 0; } -static void wpa_report_ie_mismatch(struct wpa_supplicant *wpa_s, +static void wpa_report_ie_mismatch(struct wpa_sm *sm, const char *reason, const u8 *src_addr, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *rsn_ie, size_t rsn_ie_len) { - wpa_msg(wpa_s, MSG_WARNING, "WPA: %s (src=" MACSTR ")", + wpa_msg(sm->ctx->ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", reason, MAC2STR(src_addr)); - if (wpa_s->ap_wpa_ie) { + if (sm->ap_wpa_ie) { wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", - wpa_s->ap_wpa_ie, wpa_s->ap_wpa_ie_len); + sm->ap_wpa_ie, sm->ap_wpa_ie_len); } if (wpa_ie) { - if (!wpa_s->ap_wpa_ie) { + if (!sm->ap_wpa_ie) { wpa_printf(MSG_INFO, "WPA: No WPA IE in " "Beacon/ProbeResp"); } @@ -1373,12 +1406,12 @@ static void wpa_report_ie_mismatch(struct wpa_supplicant *wpa_s, wpa_ie, wpa_ie_len); } - if (wpa_s->ap_rsn_ie) { + if (sm->ap_rsn_ie) { wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", - wpa_s->ap_rsn_ie, wpa_s->ap_rsn_ie_len); + sm->ap_rsn_ie, sm->ap_rsn_ie_len); } if (rsn_ie) { - if (!wpa_s->ap_rsn_ie) { + if (!sm->ap_rsn_ie) { wpa_printf(MSG_INFO, "WPA: No RSN IE in " "Beacon/ProbeResp"); } @@ -1386,73 +1419,21 @@ static void wpa_report_ie_mismatch(struct wpa_supplicant *wpa_s, rsn_ie, rsn_ie_len); } - wpa_supplicant_disassociate(wpa_s, REASON_IE_IN_4WAY_DIFFERS); - wpa_supplicant_req_scan(wpa_s, 0, 0); + wpa_sm_disassociate(sm, REASON_IE_IN_4WAY_DIFFERS); + wpa_sm_req_scan(sm, 0, 0); } -static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s, - unsigned char *src_addr, - struct wpa_eapol_key *key, - int extra_len, int ver) +static int wpa_supplicant_validate_ie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie) { - int rlen; - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *reply; - unsigned char *rbuf; - struct l2_ethhdr *ethhdr; - int key_info, wpa_ie_len = 0, rsn_ie_len = 0, keylen, gtk_len = 0; - u8 *wpa_ie = NULL, *rsn_ie = NULL, *gtk = NULL; - u8 *pos, *end; - u16 len; - struct wpa_ssid *ssid = wpa_s->current_ssid; - - wpa_s->wpa_state = WPA_4WAY_HANDSHAKE; - wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(src_addr), ver); - - key_info = be_to_host16(key->key_info); + struct wpa_ssid *ssid = sm->cur_ssid; - pos = (u8 *) (key + 1); - len = be_to_host16(key->key_data_length); - end = pos + len; - wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) { - wpa_printf(MSG_DEBUG, "WPA: key data underflow (ie=%d " - "len=%d)", pos[0], pos[1]); - break; - } - if (*pos == RSN_INFO_ELEM) { - rsn_ie = pos; - rsn_ie_len = pos[1] + 2; - } else if (*pos == GENERIC_INFO_ELEM && pos[1] >= 6 && - memcmp(pos + 2, WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0 - && pos[2 + WPA_SELECTOR_LEN] == 1 && - pos[2 + WPA_SELECTOR_LEN + 1] == 0) { - wpa_ie = pos; - wpa_ie_len = pos[1] + 2; - } else if (pos[0] == GENERIC_INFO_ELEM && - pos[1] > RSN_SELECTOR_LEN + 2 && - memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, - RSN_SELECTOR_LEN) == 0) { - if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: GTK IE in " - "unencrypted key data"); - return; - } - gtk = pos + 2 + RSN_SELECTOR_LEN; - gtk_len = pos[1] - RSN_SELECTOR_LEN; - } else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0) - break; - - pos += 2 + pos[1]; - } - - if (wpa_s->ap_wpa_ie == NULL && wpa_s->ap_rsn_ie == NULL) { + if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) { wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " "Trying to get from scan results"); - if (wpa_supplicant_get_beacon_ie(wpa_s) < 0) { + if (wpa_sm_get_beacon_ie(sm) < 0) { wpa_printf(MSG_WARNING, "WPA: Could not find AP from " "the scan results"); } else { @@ -1461,40 +1442,107 @@ static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s, } } - if ((wpa_ie && wpa_s->ap_wpa_ie && - (wpa_ie_len != wpa_s->ap_wpa_ie_len || - memcmp(wpa_ie, wpa_s->ap_wpa_ie, wpa_ie_len) != 0)) || - (rsn_ie && wpa_s->ap_rsn_ie && - (rsn_ie_len != wpa_s->ap_rsn_ie_len || - memcmp(rsn_ie, wpa_s->ap_rsn_ie, rsn_ie_len) != 0))) { - wpa_report_ie_mismatch(wpa_s, "IE in 3/4 msg does not match " + if ((ie->wpa_ie && sm->ap_wpa_ie && + (ie->wpa_ie_len != sm->ap_wpa_ie_len || + memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) || + (ie->rsn_ie && sm->ap_rsn_ie && + (ie->rsn_ie_len != sm->ap_rsn_ie_len || + memcmp(ie->rsn_ie, sm->ap_rsn_ie, ie->rsn_ie_len) != 0))) { + wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " "with IE in Beacon/ProbeResp", - src_addr, wpa_ie, wpa_ie_len, - rsn_ie, rsn_ie_len); - return; + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); + return -1; } - if (wpa_s->proto == WPA_PROTO_WPA && - rsn_ie && wpa_s->ap_rsn_ie == NULL && + if (sm->proto == WPA_PROTO_WPA && + ie->rsn_ie && sm->ap_rsn_ie == NULL && ssid && (ssid->proto & WPA_PROTO_RSN)) { - wpa_report_ie_mismatch(wpa_s, "Possible downgrade attack " + wpa_report_ie_mismatch(sm, "Possible downgrade attack " "detected - RSN was enabled and RSN IE " "was in msg 3/4, but not in " "Beacon/ProbeResp", - src_addr, wpa_ie, wpa_ie_len, - rsn_ie, rsn_ie_len); + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + u16 ver, u16 key_info) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info &= WPA_KEY_INFO_SECURE; + key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC; + WPA_PUT_BE16(reply->key_info, key_info); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + memcpy(reply->key_length, key->key_length, 2); + memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + +static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int extra_len, u16 ver) +{ + u16 key_info, keylen, len; + const u8 *pos; + struct wpa_eapol_ie_parse ie; + + wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); + wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + + key_info = WPA_GET_BE16(key->key_info); + + pos = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); + wpa_supplicant_parse_ies(pos, len, &ie); + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); return; } - if (memcmp(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + if (wpa_supplicant_validate_ie(sm, src_addr, &ie) < 0) + return; + + if (memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way " "Handshake differs from 3 of 4-Way Handshake - drop" " packet (src=" MACSTR ")", MAC2STR(src_addr)); return; } - keylen = be_to_host16(key->key_length); - switch (wpa_s->pairwise_cipher) { + keylen = WPA_GET_BE16(key->key_length); + switch (sm->pairwise_cipher) { case WPA_CIPHER_CCMP: if (keylen != 16) { wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length " @@ -1513,273 +1561,250 @@ static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s, break; } - rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply); - rbuf = malloc(rlen); - if (rbuf == NULL) + if (wpa_supplicant_send_4_of_4(sm, src_addr, key, ver, key_info)) return; - memset(rbuf, 0, rlen); - ethhdr = (struct l2_ethhdr *) rbuf; - memcpy(ethhdr->h_dest, src_addr, ETH_ALEN); - memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_EAPOL); - - hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - hdr->version = wpa_s->conf->eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(sizeof(*reply)); - - reply = (struct wpa_eapol_key *) (hdr + 1); - reply->type = wpa_s->proto == WPA_PROTO_RSN ? - EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE | - WPA_KEY_INFO_MIC | - (key_info & WPA_KEY_INFO_SECURE)); - reply->key_length = key->key_length; - memcpy(reply->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - - reply->key_data_length = host_to_be16(0); - - memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN); - wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr, - rlen - sizeof(*ethhdr), reply->key_mic); - - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); - wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 4/4", rbuf, rlen); - l2_packet_send(wpa_s->l2, rbuf, rlen); - eapol_sm_notify_tx_eapol_key(wpa_s->eapol); - free(rbuf); - /* SNonce was successfully used in msg 3/4, so mark it to be renewed * for the next 4-Way Handshake. If msg 3 is received again, the old * SNonce will still be used to avoid changing PTK. */ - wpa_s->renew_snonce = 1; + sm->renew_snonce = 1; if (key_info & WPA_KEY_INFO_INSTALL) { - wpa_supplicant_install_ptk(wpa_s, src_addr, key); + wpa_supplicant_install_ptk(sm, src_addr, key); } if (key_info & WPA_KEY_INFO_SECURE) { /* MLME.SETPROTECTION.request(TA, Tx_Rx) */ - eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_portValid(sm->eapol, TRUE); } - wpa_s->wpa_state = WPA_GROUP_HANDSHAKE; + wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); - if (gtk) { - wpa_supplicant_pairwise_gtk(wpa_s, src_addr, key, - gtk, gtk_len, key_info); + if (ie.gtk && + wpa_supplicant_pairwise_gtk(sm, src_addr, key, + ie.gtk, ie.gtk_len, key_info) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to configure GTK"); } } -static void wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s, - unsigned char *src_addr, - struct wpa_eapol_key *key, - int extra_len, int ver) +static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, + const u8 *keydata, + size_t keydatalen, + int key_info, + struct wpa_gtk_data *gd) { - int rlen; - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *reply; - unsigned char *rbuf; - struct l2_ethhdr *ethhdr; - int key_info, keylen, keydatalen, maxkeylen, keyidx, key_rsc_len = 0; - int alg, tx, rekey; - u8 ek[32], gtk[32]; - u8 *gtk_ie = NULL; - size_t gtk_ie_len = 0; - - rekey = wpa_s->wpa_state == WPA_COMPLETED; - wpa_s->wpa_state = WPA_GROUP_HANDSHAKE; - wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from " - MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + int maxkeylen; + struct wpa_eapol_ie_parse ie; - key_info = be_to_host16(key->key_info); - keydatalen = be_to_host16(key->key_data_length); - - if (wpa_s->proto == WPA_PROTO_RSN) { - u8 *pos = (u8 *) (key + 1); - u8 *end = pos + keydatalen; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) { - wpa_printf(MSG_DEBUG, "RSN: key data " - "underflow (ie=%d len=%d)", - pos[0], pos[1]); - break; - } - if (pos[0] == GENERIC_INFO_ELEM && - pos + 1 + RSN_SELECTOR_LEN < end && - pos[1] > RSN_SELECTOR_LEN + 2 && - memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, - RSN_SELECTOR_LEN) == 0) { - if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: GTK IE " - "in unencrypted key data"); - return; - } - gtk_ie = pos + 2 + RSN_SELECTOR_LEN; - gtk_ie_len = pos[1] - RSN_SELECTOR_LEN; - break; - } else if (pos[0] == GENERIC_INFO_ELEM && - pos[1] == 0) - break; + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); + wpa_supplicant_parse_ies(keydata, keydatalen, &ie); + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); + return -1; + } + if (ie.gtk == NULL) { + wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2"); + return -1; + } + maxkeylen = gd->gtk_len = ie.gtk_len - 2; - pos += 2 + pos[1]; - } + if (wpa_supplicant_check_group_cipher(sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; - if (gtk_ie == NULL) { - wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key " - "message 1/2"); - return; - } - maxkeylen = keylen = gtk_ie_len - 2; - } else { - keylen = be_to_host16(key->key_length); - maxkeylen = keydatalen; - if (keydatalen > extra_len) { - wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:" - " key_data_length=%d > extra_len=%d", - keydatalen, extra_len); - return; - } - if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) - maxkeylen -= 8; + wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", + ie.gtk, ie.gtk_len); + gd->keyidx = ie.gtk[0] & 0x3; + gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(ie.gtk[0] & BIT(2))); + if (ie.gtk_len - 2 > sizeof(gd->gtk)) { + wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE " + "(len=%lu)", (unsigned long) ie.gtk_len - 2); + return -1; } + memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2); - if (wpa_supplicant_check_group_cipher(wpa_s, keylen, maxkeylen, - &key_rsc_len, &alg)) { - return; + return 0; +} + + +static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + size_t keydatalen, int key_info, + int extra_len, u16 ver, + struct wpa_gtk_data *gd) +{ + int maxkeylen; + u8 ek[32]; + + gd->gtk_len = WPA_GET_BE16(key->key_length); + maxkeylen = keydatalen; + if (keydatalen > extra_len) { + wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:" + " key_data_length=%lu > extra_len=%d", + (unsigned long) keydatalen, extra_len); + return -1; } + if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) + maxkeylen -= 8; - if (wpa_s->proto == WPA_PROTO_RSN) { - wpa_hexdump(MSG_DEBUG, - "RSN: received GTK in group key handshake", - gtk_ie, gtk_ie_len); - keyidx = gtk_ie[0] & 0x3; - tx = !!(gtk_ie[0] & BIT(2)); - if (gtk_ie_len - 2 > sizeof(gtk)) { - wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE " - "(len=%lu)", - (unsigned long) gtk_ie_len - 2); - return; + if (wpa_supplicant_check_group_cipher(sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> + WPA_KEY_INFO_KEY_INDEX_SHIFT; + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + memcpy(ek, key->key_iv, 16); + memcpy(ek + 16, sm->ptk.kek, 16); + if (keydatalen > sizeof(gd->gtk)) { + wpa_printf(MSG_WARNING, "WPA: RC4 key data " + "too long (%lu)", + (unsigned long) keydatalen); + return -1; } - memcpy(gtk, gtk_ie + 2, gtk_ie_len - 2); - } else { - keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> - WPA_KEY_INFO_KEY_INDEX_SHIFT; - if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { - memcpy(ek, key->key_iv, 16); - memcpy(ek + 16, wpa_s->ptk.encr_key, 16); - rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen); - memcpy(gtk, key + 1, keylen); - } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - if (keydatalen % 8) { - wpa_printf(MSG_WARNING, "WPA: Unsupported " - "AES-WRAP len %d", keydatalen); - return; - } - if (aes_unwrap(wpa_s->ptk.encr_key, maxkeylen / 8, - (u8 *) (key + 1), gtk)) { - wpa_printf(MSG_WARNING, "WPA: AES unwrap " - "failed - could not decrypt GTK"); - return; - } + memcpy(gd->gtk, key + 1, keydatalen); + rc4_skip(ek, 32, 256, gd->gtk, keydatalen); + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + if (keydatalen % 8) { + wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP " + "len %lu", (unsigned long) keydatalen); + return -1; + } + if (maxkeylen > sizeof(gd->gtk)) { + wpa_printf(MSG_WARNING, "WPA: AES-WRAP key data " + "too long (keydatalen=%lu maxkeylen=%lu)", + (unsigned long) keydatalen, + (unsigned long) maxkeylen); + return -1; + } + if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, + (const u8 *) (key + 1), gd->gtk)) { + wpa_printf(MSG_WARNING, "WPA: AES unwrap " + "failed - could not decrypt GTK"); + return -1; } - tx = !!(key_info & WPA_KEY_INFO_TXRX); } + gd->tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(key_info & WPA_KEY_INFO_TXRX)); + return 0; +} - if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) { - /* Ignore Tx bit in Group Key message if a pairwise key - * is used. Some APs seem to setting this bit - * (incorrectly, since Tx is only when doing Group Key - * only APs) and without this workaround, the data - * connection does not work because wpa_supplicant - * configured non-zero keyidx to be used for unicast. - */ - wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but " - "pairwise keys are used - ignore Tx bit"); - tx = 0; - } - wpa_supplicant_install_gtk(wpa_s, key, alg, gtk, keylen, keyidx, - key_rsc_len, tx); +static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int ver, u16 key_info) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; - rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply); - rbuf = malloc(rlen); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); if (rbuf == NULL) - return; - - memset(rbuf, 0, rlen); - ethhdr = (struct l2_ethhdr *) rbuf; - memcpy(ethhdr->h_dest, src_addr, ETH_ALEN); - memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_EAPOL); - - hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - hdr->version = wpa_s->conf->eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(sizeof(*reply)); + return -1; - reply = (struct wpa_eapol_key *) (hdr + 1); - reply->type = wpa_s->proto == WPA_PROTO_RSN ? + reply->type = sm->proto == WPA_PROTO_RSN ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - reply->key_info = - host_to_be16(ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE | - (key_info & WPA_KEY_INFO_KEY_INDEX_MASK)); - reply->key_length = key->key_length; + key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; + key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(reply->key_info, key_info); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + memcpy(reply->key_length, key->key_length, 2); memcpy(reply->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - - reply->key_data_length = host_to_be16(0); + WPA_REPLAY_COUNTER_LEN); - wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr, - rlen - sizeof(*ethhdr), reply->key_mic); + WPA_PUT_BE16(reply->key_data_length, 0); wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); - wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/2", rbuf, rlen); - l2_packet_send(wpa_s->l2, rbuf, rlen); - eapol_sm_notify_tx_eapol_key(wpa_s->eapol); - free(rbuf); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + +static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int extra_len, u16 ver) +{ + u16 key_info, keydatalen; + int rekey; + struct wpa_gtk_data gd; + + memset(&gd, 0, sizeof(gd)); + + rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; + wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); + wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from " + MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + + key_info = WPA_GET_BE16(key->key_info); + keydatalen = WPA_GET_BE16(key->key_data_length); + + if (sm->proto == WPA_PROTO_RSN) { + if (wpa_supplicant_process_1_of_2_rsn(sm, + (const u8 *) (key + 1), + keydatalen, key_info, + &gd)) + return; + } else { + if (wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen, + key_info, extra_len, + ver, &gd)) + return; + } + + if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || + wpa_supplicant_send_2_of_2(sm, src_addr, key, ver, key_info)) + return; if (rekey) { - wpa_msg(wpa_s, MSG_INFO, "WPA: Group rekeying completed with " - MACSTR " [GTK=%s]", MAC2STR(src_addr), - wpa_cipher_txt(wpa_s->group_cipher)); - wpa_s->wpa_state = WPA_COMPLETED; + wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Group rekeying " + "completed with " MACSTR " [GTK=%s]", + MAC2STR(src_addr), wpa_cipher_txt(sm->group_cipher)); + wpa_sm_set_state(sm, WPA_COMPLETED); } else { - wpa_supplicant_key_neg_complete(wpa_s, src_addr, + wpa_supplicant_key_neg_complete(sm, src_addr, key_info & WPA_KEY_INFO_SECURE); } } -static int wpa_supplicant_verify_eapol_key_mic(struct wpa_supplicant *wpa_s, +static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_eapol_key *key, - int ver, u8 *buf, size_t len) + u16 ver, + const u8 *buf, size_t len) { u8 mic[16]; int ok = 0; memcpy(mic, key->key_mic, 16); - if (wpa_s->tptk_set) { + if (sm->tptk_set) { memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, buf, len, + wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, key->key_mic); if (memcmp(mic, key->key_mic, 16) != 0) { wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); } else { ok = 1; - wpa_s->tptk_set = 0; - wpa_s->ptk_set = 1; - memcpy(&wpa_s->ptk, &wpa_s->tptk, sizeof(wpa_s->ptk)); + sm->tptk_set = 0; + sm->ptk_set = 1; + memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); } } - if (!ok && wpa_s->ptk_set) { + if (!ok && sm->ptk_set) { memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, buf, len, + wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, key->key_mic); if (memcmp(mic, key->key_mic, 16) != 0) { wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " @@ -1795,22 +1820,22 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_supplicant *wpa_s, return -1; } - memcpy(wpa_s->rx_replay_counter, key->replay_counter, + memcpy(sm->rx_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); - wpa_s->rx_replay_counter_set = 1; + sm->rx_replay_counter_set = 1; return 0; } /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ -static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s, - struct wpa_eapol_key *key, int ver) +static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, + struct wpa_eapol_key *key, u16 ver) { - int keydatalen = be_to_host16(key->key_data_length); + u16 keydatalen = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", (u8 *) (key + 1), keydatalen); - if (!wpa_s->ptk_set) { + if (!sm->ptk_set) { wpa_printf(MSG_WARNING, "WPA: PTK not available, " "cannot decrypt EAPOL-Key key data."); return -1; @@ -1821,7 +1846,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s, if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { u8 ek[32]; memcpy(ek, key->key_iv, 16); - memcpy(ek + 16, wpa_s->ptk.encr_key, 16); + memcpy(ek + 16, sm->ptk.kek, 16); rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen); } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { u8 *buf; @@ -1837,7 +1862,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s, "AES-UNWRAP buffer"); return -1; } - if (aes_unwrap(wpa_s->ptk.encr_key, keydatalen / 8, + if (aes_unwrap(sm->ptk.kek, keydatalen / 8, (u8 *) (key + 1), buf)) { free(buf); wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - " @@ -1846,7 +1871,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s, } memcpy(key + 1, buf, keydatalen); free(buf); - key->key_data_length = host_to_be16(keydatalen); + WPA_PUT_BE16(key->key_data_length, keydatalen); } wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", (u8 *) (key + 1), keydatalen); @@ -1854,50 +1879,73 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s, } -static void wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s, - unsigned char *src_addr, unsigned char *buf, - size_t len) +/** + * wpa_sm_rx_eapol - Process received WPA EAPOL frames + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @src_addr: Source MAC address of the EAPOL packet + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) + * @len: Length of the EAPOL frame + * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure + * + * This function is called for each received EAPOL frame. Other than EAPOL-Key + * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is + * only processing WPA and WPA2 EAPOL-Key frames. + * + * The received EAPOL-Key packets are validated and valid packets are replied + * to. In addition, key material (PTK, GTK) is configured at the end of a + * successful key handshake. + */ +int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) { size_t plen, data_len, extra_len; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - int key_info, ver; + u16 key_info, ver; + u8 *tmp; + int ret = -1; - hdr = (struct ieee802_1x_hdr *) buf; - key = (struct wpa_eapol_key *) (hdr + 1); if (len < sizeof(*hdr) + sizeof(*key)) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short, len %lu, " - "expecting at least %lu", + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA " + "EAPOL-Key (len %lu, expecting at least %lu)", (unsigned long) len, (unsigned long) sizeof(*hdr) + sizeof(*key)); - return; + return 0; } + + tmp = malloc(len); + if (tmp == NULL) + return -1; + memcpy(tmp, buf, len); + + hdr = (struct ieee802_1x_hdr *) tmp; + key = (struct wpa_eapol_key *) (hdr + 1); plen = ntohs(hdr->length); data_len = plen + sizeof(*hdr); wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu", hdr->version, hdr->type, (unsigned long) plen); - wpa_drv_poll(wpa_s); - if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " "not a Key frame", hdr->type); - if (wpa_s->cur_pmksa) { + if (sm->cur_pmksa) { wpa_printf(MSG_DEBUG, "WPA: Cancelling PMKSA caching " "attempt - attempt full EAP " "authentication"); - eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 0); + eapol_sm_notify_pmkid_attempt(sm->eapol, 0); } - return; + ret = 0; + goto out; } if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu " "invalid (frame size %lu)", (unsigned long) plen, (unsigned long) len); - return; + ret = 0; + goto out; } wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type); @@ -1905,28 +1953,30 @@ static void wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s, { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, " "discarded", key->type); - return; + ret = 0; + goto out; } - wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len); + eapol_sm_notify_lower_layer_success(sm->eapol); + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); if (data_len < len) { wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE " "802.1X data", (unsigned long) len - data_len); } - key_info = be_to_host16(key->key_info); + key_info = WPA_GET_BE16(key->key_info); ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor " "version %d.", ver); - return; + goto out; } - if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP && + if (sm->pairwise_cipher == WPA_CIPHER_CCMP && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " "descriptor version (%d) is not 2.", ver); - if (wpa_s->group_cipher != WPA_CIPHER_CCMP && + if (sm->group_cipher != WPA_CIPHER_CCMP && !(key_info & WPA_KEY_INFO_KEY_TYPE)) { /* Earlier versions of IEEE 802.11i did not explicitly * require version 2 descriptor for all EAPOL-Key @@ -1936,115 +1986,78 @@ static void wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s, "allow invalid version for non-CCMP group " "keys"); } else - return; + goto out; } - if (wpa_s->rx_replay_counter_set && - memcmp(key->replay_counter, wpa_s->rx_replay_counter, + if (sm->rx_replay_counter_set && + memcmp(key->replay_counter, sm->rx_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not" " increase - dropping packet"); - return; + goto out; } if (!(key_info & WPA_KEY_INFO_ACK)) { wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info"); - return; + goto out; } if (key_info & WPA_KEY_INFO_REQUEST) { wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - " "dropped"); - return; + goto out; } if ((key_info & WPA_KEY_INFO_MIC) && - wpa_supplicant_verify_eapol_key_mic(wpa_s, key, ver, buf, - data_len)) - return; + wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) + goto out; extra_len = data_len - sizeof(*hdr) - sizeof(*key); - if (be_to_host16(key->key_data_length) > extra_len) { - wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid EAPOL-Key frame - " - "key_data overflow (%d > %lu)", - be_to_host16(key->key_data_length), + if (WPA_GET_BE16(key->key_data_length) > extra_len) { + wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " + "frame - key_data overflow (%d > %lu)", + WPA_GET_BE16(key->key_data_length), (unsigned long) extra_len); - return; + goto out; } - if (wpa_s->proto == WPA_PROTO_RSN && + if (sm->proto == WPA_PROTO_RSN && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && - wpa_supplicant_decrypt_key_data(wpa_s, key, ver)) - return; + wpa_supplicant_decrypt_key_data(sm, key, ver)) + goto out; if (key_info & WPA_KEY_INFO_KEY_TYPE) { if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key " "(Pairwise) with non-zero key index"); - return; + goto out; } if (key_info & WPA_KEY_INFO_MIC) { /* 3/4 4-Way Handshake */ - wpa_supplicant_process_3_of_4(wpa_s, src_addr, key, + wpa_supplicant_process_3_of_4(sm, src_addr, key, extra_len, ver); } else { /* 1/4 4-Way Handshake */ - wpa_supplicant_process_1_of_4(wpa_s, src_addr, key, + wpa_supplicant_process_1_of_4(sm, src_addr, key, ver); } } else { if (key_info & WPA_KEY_INFO_MIC) { /* 1/2 Group Key Handshake */ - wpa_supplicant_process_1_of_2(wpa_s, src_addr, key, + wpa_supplicant_process_1_of_2(sm, src_addr, key, extra_len, ver); } else { wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) " "without Mic bit - dropped"); } } -} + ret = 1; -void wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr, - unsigned char *buf, size_t len) -{ - struct wpa_supplicant *wpa_s = ctx; - - wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); - wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); - - if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { - wpa_printf(MSG_DEBUG, "Ignored received EAPOL frame since " - "no key management is configured"); - return; - } - - if (wpa_s->eapol_received == 0) { - /* Timeout for completing IEEE 802.1X and WPA authentication */ - wpa_supplicant_req_auth_timeout( - wpa_s, - (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X || - wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) ? - 70 : 10, 0); - } - wpa_s->eapol_received++; - - if (wpa_s->countermeasures) { - wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL " - "packet"); - return; - } - - /* 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. */ - - memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN); - eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len); - wpa_sm_rx_eapol(wpa_s, src_addr, buf, len); +out: + free(tmp); + return ret; } @@ -2065,16 +2078,16 @@ static int wpa_cipher_bits(int cipher) } -static const u8 * wpa_key_mgmt_suite(struct wpa_supplicant *wpa_s) +static const u8 * wpa_key_mgmt_suite(struct wpa_sm *sm) { static const u8 *dummy = (u8 *) "\x00\x00\x00\x00"; - switch (wpa_s->key_mgmt) { + switch (sm->key_mgmt) { case WPA_KEY_MGMT_IEEE8021X: - return (wpa_s->proto == WPA_PROTO_RSN ? + return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); case WPA_KEY_MGMT_PSK: - return (wpa_s->proto == WPA_PROTO_RSN ? + return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X : WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); case WPA_KEY_MGMT_WPA_NONE: @@ -2085,24 +2098,24 @@ static const u8 * wpa_key_mgmt_suite(struct wpa_supplicant *wpa_s) } -static const u8 * wpa_cipher_suite(struct wpa_supplicant *wpa_s, int cipher) +static const u8 * wpa_cipher_suite(struct wpa_sm *sm, int cipher) { static const u8 *dummy = (u8 *) "\x00\x00\x00\x00"; switch (cipher) { case WPA_CIPHER_CCMP: - return (wpa_s->proto == WPA_PROTO_RSN ? + return (sm->proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); case WPA_CIPHER_TKIP: - return (wpa_s->proto == WPA_PROTO_RSN ? + return (sm->proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); case WPA_CIPHER_WEP104: - return (wpa_s->proto == WPA_PROTO_RSN ? + return (sm->proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); case WPA_CIPHER_WEP40: - return (wpa_s->proto == WPA_PROTO_RSN ? + return (sm->proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); case WPA_CIPHER_NONE: - return (wpa_s->proto == WPA_PROTO_RSN ? + return (sm->proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); default: return dummy; @@ -2113,21 +2126,41 @@ static const u8 * wpa_cipher_suite(struct wpa_supplicant *wpa_s, int cipher) #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3] -int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +/** + * wpa_sm_get_mib - Dump text list of MIB entries + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for the list + * @buflen: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used fetch dot11 MIB variables. + */ +int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) { int len, i; char pmkid_txt[PMKID_LEN * 2 + 1]; + int rsna; - if (wpa_s->cur_pmksa) { + if (sm->cur_pmksa) { char *pos = pmkid_txt; for (i = 0; i < PMKID_LEN; i++) { - pos += sprintf(pos, "%02x", - wpa_s->cur_pmksa->pmkid[i]); + pos += sprintf(pos, "%02x", sm->cur_pmksa->pmkid[i]); } } else pmkid_txt[0] = '\0'; + if ((sm->key_mgmt == WPA_KEY_MGMT_PSK || + sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X) && + sm->proto == WPA_PROTO_RSN) + rsna = 1; + else + rsna = 0; + len = snprintf(buf, buflen, + "dot11RSNAOptionImplemented=TRUE\n" + "dot11RSNAPreauthenticationImplemented=TRUE\n" + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n" "dot11RSNAConfigVersion=%d\n" "dot11RSNAConfigPairwiseKeysSupported=5\n" "dot11RSNAConfigGroupCipherSize=%d\n" @@ -2142,316 +2175,504 @@ int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" - "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" + "dot11RSNA4WayHandshakeFailures=%u\n", + rsna ? "TRUE" : "FALSE", + rsna ? "TRUE" : "FALSE", RSN_VERSION, - wpa_cipher_bits(wpa_s->group_cipher), - dot11RSNAConfigPMKLifetime, - dot11RSNAConfigPMKReauthThreshold, - dot11RSNAConfigSATimeout, - RSN_SUITE_ARG(wpa_key_mgmt_suite(wpa_s)), - RSN_SUITE_ARG(wpa_cipher_suite(wpa_s, - wpa_s->pairwise_cipher)), - RSN_SUITE_ARG(wpa_cipher_suite(wpa_s, - wpa_s->group_cipher)), + wpa_cipher_bits(sm->group_cipher), + sm->dot11RSNAConfigPMKLifetime, + sm->dot11RSNAConfigPMKReauthThreshold, + sm->dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), + RSN_SUITE_ARG(wpa_cipher_suite(sm, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)), pmkid_txt, - RSN_SUITE_ARG(wpa_key_mgmt_suite(wpa_s)), - RSN_SUITE_ARG(wpa_cipher_suite(wpa_s, - wpa_s->pairwise_cipher)), - RSN_SUITE_ARG(wpa_cipher_suite(wpa_s, - wpa_s->group_cipher))); + RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), + RSN_SUITE_ARG(wpa_cipher_suite(sm, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)), + sm->dot11RSNA4WayHandshakeFailures); return len; } -#ifdef IEEE8021X_EAPOL +/** + * wpa_sm_init - Initialize WPA state machine + * @ctx: Context pointer for callbacks + * Returns: Pointer to the allocated WPA state machine data + * + * This function is used to allocate a new WPA state machine and the returned + * value is passed to all WPA state machine calls. + */ +struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) +{ + struct wpa_sm *sm; + + sm = malloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + memset(sm, 0, sizeof(*sm)); + sm->renew_snonce = 1; + sm->ctx = ctx; + + sm->dot11RSNAConfigPMKLifetime = 43200; + sm->dot11RSNAConfigPMKReauthThreshold = 70; + sm->dot11RSNAConfigSATimeout = 60; + + return sm; +} + -static void rsn_preauth_receive(void *ctx, unsigned char *src_addr, - unsigned char *buf, size_t len) +/** + * wpa_sm_deinit - Deinitialize WPA state machine + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void wpa_sm_deinit(struct wpa_sm *sm) { - struct wpa_supplicant *wpa_s = ctx; - - wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); - wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); - - if (wpa_s->preauth_eapol == NULL || - memcmp(wpa_s->preauth_bssid, "\x00\x00\x00\x00\x00\x00", - ETH_ALEN) == 0 || - memcmp(wpa_s->preauth_bssid, src_addr, ETH_ALEN) != 0) { - wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " - "unexpected source " MACSTR " - dropped", - MAC2STR(src_addr)); + if (sm == NULL) return; - } + eloop_cancel_timeout(wpa_sm_start_preauth, sm, 0); + free(sm->assoc_wpa_ie); + free(sm->ap_wpa_ie); + free(sm->ap_rsn_ie); + free(sm->ctx); + free(sm); +} - eapol_sm_rx_eapol(wpa_s->preauth_eapol, src_addr, buf, len); + +/** + * wpa_sm_notify_assoc - Notify WPA state machine about association + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @bssid: The BSSID of the new association + * + * This function is called to let WPA state machine know that the connection + * was established. + */ +void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) +{ + if (sm == NULL) + return; + + wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter"); + memcpy(sm->bssid, bssid, ETH_ALEN); + memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 0; + sm->renew_snonce = 1; + if (memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0) + rsn_preauth_deinit(sm); } -static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, - void *ctx) +/** + * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * This function is called to let WPA state machine know that the connection + * was lost. This will abort any existing pre-authentication session. + */ +void wpa_sm_notify_disassoc(struct wpa_sm *sm) { - struct wpa_supplicant *wpa_s = ctx; - u8 pmk[PMK_LEN]; + rsn_preauth_deinit(sm); + if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) + sm->dot11RSNA4WayHandshakeFailures++; +} - wpa_msg(wpa_s, MSG_INFO, "RSN: pre-authentication with " MACSTR - " %s", MAC2STR(wpa_s->preauth_bssid), - success ? "completed successfully" : "failed"); - if (success) { - int res, pmk_len; - pmk_len = PMK_LEN; - res = eapol_sm_get_key(eapol, pmk, PMK_LEN); -#ifdef EAP_LEAP - if (res) { - res = eapol_sm_get_key(eapol, pmk, 16); - pmk_len = 16; - } -#endif /* EAP_LEAP */ - if (res == 0) { - wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", - pmk, pmk_len); - wpa_s->pmk_len = pmk_len; - pmksa_cache_add(wpa_s, pmk, pmk_len, - wpa_s->preauth_bssid, wpa_s->own_addr); - } else { - wpa_msg(wpa_s, MSG_INFO, "RSN: failed to get master " - "session key from pre-auth EAPOL state " - "machines"); - } +/** + * wpa_sm_set_pmk - Set PMK + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmk: The new PMK + * @pmk_len: The length of the new PMK in bytes + * + * Configure the PMK for WPA state machine. + */ +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) +{ + if (sm == NULL) + return; + + sm->pmk_len = pmk_len; + memcpy(sm->pmk, pmk, pmk_len); +} + + +/** + * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK + * will be cleared. + */ +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + + if (sm->cur_pmksa) { + sm->pmk_len = sm->cur_pmksa->pmk_len; + memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); + } else { + sm->pmk_len = PMK_LEN; + memset(sm->pmk, 0, PMK_LEN); } +} - rsn_preauth_deinit(wpa_s); - rsn_preauth_candidate_process(wpa_s); + +/** + * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @fast_reauth: Whether fast reauthentication (EAP) is allowed + */ +void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) +{ + if (sm) + sm->fast_reauth = fast_reauth; } -static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx) +/** + * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @scard_ctx: Context pointer for smartcard related callback functions + */ +void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) { - struct wpa_supplicant *wpa_s = eloop_ctx; - wpa_msg(wpa_s, MSG_INFO, "RSN: pre-authentication with " MACSTR - " timed out", MAC2STR(wpa_s->preauth_bssid)); - rsn_preauth_deinit(wpa_s); - rsn_preauth_candidate_process(wpa_s); + if (sm == NULL) + return; + sm->scard_ctx = scard_ctx; + if (sm->preauth_eapol) + eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx); } -int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst) +/** + * wpa_sm_set_config - Notification of current configration change + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @config: Pointer to current network configuration + * + * Notify WPA state machine that configuration has changed. config will be + * stored as a backpointer to network configuration. This can be %NULL to clear + * the stored pointed. + */ +void wpa_sm_set_config(struct wpa_sm *sm, struct wpa_ssid *config) { - struct eapol_config eapol_conf; - struct eapol_ctx *ctx; + if (sm) + sm->cur_ssid = config; +} - if (wpa_s->preauth_eapol) - return -1; - wpa_msg(wpa_s, MSG_DEBUG, "RSN: starting pre-authentication with " - MACSTR, MAC2STR(dst)); +/** + * wpa_sm_set_own_addr - Set own MAC address + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @addr: Own MAC address + */ +void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) +{ + if (sm) + memcpy(sm->own_addr, addr, ETH_ALEN); +} - wpa_s->l2_preauth = l2_packet_init(wpa_s->ifname, - wpa_drv_get_mac_addr(wpa_s), - ETH_P_RSN_PREAUTH, - rsn_preauth_receive, wpa_s); - if (wpa_s->l2_preauth == NULL) { - wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " - "processing for pre-authentication"); - return -2; - } - ctx = malloc(sizeof(*ctx)); - if (ctx == NULL) { - wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); - return -4; - } - memset(ctx, 0, sizeof(*ctx)); - ctx->ctx = wpa_s; - ctx->msg_ctx = wpa_s; - ctx->preauth = 1; - ctx->cb = rsn_preauth_eapol_cb; - ctx->cb_ctx = wpa_s; - ctx->scard_ctx = wpa_s->scard; - ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done; - ctx->eapol_send = wpa_eapol_send_preauth; - - wpa_s->preauth_eapol = eapol_sm_init(ctx); - if (wpa_s->preauth_eapol == NULL) { - free(ctx); - wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " - "state machines for pre-authentication"); - return -3; - } - memset(&eapol_conf, 0, sizeof(eapol_conf)); - eapol_conf.accept_802_1x_keys = 0; - eapol_conf.required_keys = 0; - eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; - if (wpa_s->current_ssid) - eapol_conf.workaround = wpa_s->current_ssid->eap_workaround; - eapol_sm_notify_config(wpa_s->preauth_eapol, wpa_s->current_ssid, - &eapol_conf); - memcpy(wpa_s->preauth_bssid, dst, ETH_ALEN); - - eapol_sm_notify_portValid(wpa_s->preauth_eapol, TRUE); - /* 802.1X::portControl = Auto */ - eapol_sm_notify_portEnabled(wpa_s->preauth_eapol, TRUE); - - eloop_register_timeout(60, 0, rsn_preauth_timeout, wpa_s, NULL); +/** + * wpa_sm_set_ifname - Set network interface name + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ifname: Interface name + */ +void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname) +{ + if (sm) + sm->ifname = ifname; +} - return 0; + +/** + * wpa_sm_set_eapol - Set EAPOL state machine pointer + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init() + */ +void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) +{ + if (sm) + sm->eapol = eapol; } -void rsn_preauth_deinit(struct wpa_supplicant *wpa_s) +/** + * wpa_sm_set_param - Set WPA state machine parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @param: Parameter field + * @value: Parameter value + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, + unsigned int value) { - if (!wpa_s->preauth_eapol) - return; + int ret = 0; + + if (sm == NULL) + return -1; - eloop_cancel_timeout(rsn_preauth_timeout, wpa_s, NULL); - eapol_sm_deinit(wpa_s->preauth_eapol); - wpa_s->preauth_eapol = NULL; - memset(wpa_s->preauth_bssid, 0, ETH_ALEN); + switch (param) { + case RSNA_PMK_LIFETIME: + if (value > 0) + sm->dot11RSNAConfigPMKLifetime = value; + else + ret = -1; + break; + case RSNA_PMK_REAUTH_THRESHOLD: + if (value > 0 && value <= 100) + sm->dot11RSNAConfigPMKReauthThreshold = value; + else + ret = -1; + break; + case RSNA_SA_TIMEOUT: + if (value > 0) + sm->dot11RSNAConfigSATimeout = value; + else + ret = -1; + break; + case WPA_PARAM_PROTO: + sm->proto = value; + break; + case WPA_PARAM_PAIRWISE: + sm->pairwise_cipher = value; + break; + case WPA_PARAM_GROUP: + sm->group_cipher = value; + break; + case WPA_PARAM_KEY_MGMT: + sm->key_mgmt = value; + break; + } - l2_packet_deinit(wpa_s->l2_preauth); - wpa_s->l2_preauth = NULL; + return ret; } -static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s) +/** + * wpa_sm_get_param - Get WPA state machine parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @param: Parameter field + * Returns: Parameter value + */ +unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param) { - struct rsn_pmksa_candidate *candidate; + if (sm == NULL) + return 0; - if (wpa_s->pmksa_candidates == NULL) - return; + switch (param) { + case RSNA_PMK_LIFETIME: + return sm->dot11RSNAConfigPMKLifetime; + case RSNA_PMK_REAUTH_THRESHOLD: + return sm->dot11RSNAConfigPMKReauthThreshold; + case RSNA_SA_TIMEOUT: + return sm->dot11RSNAConfigSATimeout; + case WPA_PARAM_PROTO: + return sm->proto; + case WPA_PARAM_PAIRWISE: + return sm->pairwise_cipher; + case WPA_PARAM_GROUP: + return sm->group_cipher; + case WPA_PARAM_KEY_MGMT: + return sm->key_mgmt; + default: + return 0; + } +} - /* TODO: drop priority for old candidate entries */ - - wpa_msg(wpa_s, MSG_DEBUG, "RSN: processing PMKSA candidate list"); - if (wpa_s->preauth_eapol || - wpa_s->proto != WPA_PROTO_RSN || - wpa_s->wpa_state != WPA_COMPLETED || - wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X) { - wpa_msg(wpa_s, MSG_DEBUG, "RSN: not in suitable state for new " - "pre-authentication"); - return; /* invalid state for new pre-auth */ - } - - while (wpa_s->pmksa_candidates) { - struct rsn_pmksa_cache *p = NULL; - candidate = wpa_s->pmksa_candidates; - p = pmksa_cache_get(wpa_s, candidate->bssid, NULL); - if (memcmp(wpa_s->bssid, candidate->bssid, ETH_ALEN) != 0 && - p == NULL) { - wpa_msg(wpa_s, MSG_DEBUG, "RSN: PMKSA candidate " - MACSTR " selected for pre-authentication", - MAC2STR(candidate->bssid)); - wpa_s->pmksa_candidates = candidate->next; - rsn_preauth_init(wpa_s, candidate->bssid); - free(candidate); - return; - } - wpa_msg(wpa_s, MSG_DEBUG, "RSN: PMKSA candidate " MACSTR - " does not need pre-authentication anymore", - MAC2STR(candidate->bssid)); - /* Some drivers (e.g., NDIS) expect to get notified about the - * PMKIDs again, so report the existing data now. */ - if (p) - wpa_drv_add_pmkid(wpa_s, candidate->bssid, p->pmkid); - - wpa_s->pmksa_candidates = candidate->next; - free(candidate); - } - wpa_msg(wpa_s, MSG_DEBUG, "RSN: no more pending PMKSA candidates"); + +/** + * wpa_sm_get_status - Get WPA state machine + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query WPA state machine for status information. This function fills in + * a text area with current status information. If the buffer (buf) is not + * large enough, status information will be truncated to fit the buffer. + */ +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose) +{ + char *pos = buf, *end = buf + buflen; + + pos += snprintf(pos, end - pos, + "pairwise_cipher=%s\n" + "group_cipher=%s\n" + "key_mgmt=%s\n", + wpa_cipher_txt(sm->pairwise_cipher), + wpa_cipher_txt(sm->group_cipher), + wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); + return pos - buf; } -void pmksa_candidate_add(struct wpa_supplicant *wpa_s, const u8 *bssid, - int prio) +/** + * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @wpa_ie: Pointer to buffer for WPA/RSN IE + * @wpa_ie_len: Pointer to the length of the wpa_ie buffer + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA/RSN IE used in (Re)Association + * Request frame. The IE will be used to override the default value generated + * with wpa_sm_set_assoc_wpa_ie_default(). + */ +int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, + size_t *wpa_ie_len) { - struct rsn_pmksa_candidate *cand, *prev, *pos; - - /* If BSSID already on candidate list, update the priority of the old - * entry. Do not override priority based on normal scan results. */ - prev = NULL; - cand = wpa_s->pmksa_candidates; - while (cand) { - if (memcmp(cand->bssid, bssid, ETH_ALEN) == 0) { - if (prev) - prev->next = cand->next; - else - wpa_s->pmksa_candidates = cand->next; - break; - } - prev = cand; - cand = cand->next; + if (sm == NULL) + return -1; + + *wpa_ie_len = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); + if (*wpa_ie_len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default", + wpa_ie, *wpa_ie_len); + + if (sm->assoc_wpa_ie == NULL) { + /* + * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets + * the correct version of the IE even if PMKSA caching is + * aborted (which would remove PMKID from IE generation). + */ + sm->assoc_wpa_ie = malloc(*wpa_ie_len); + if (sm->assoc_wpa_ie == NULL) + return -1; + + memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len); + sm->assoc_wpa_ie_len = *wpa_ie_len; } - if (cand) { - if (prio < PMKID_CANDIDATE_PRIO_SCAN) - cand->priority = prio; + return 0; +} + + +/** + * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA/RSN IE used in (Re)Association + * Request frame. The IE will be used to override the default value generated + * with wpa_sm_set_assoc_wpa_ie_default(). + */ +int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + free(sm->assoc_wpa_ie); + if (ie == NULL || len == 0) { + wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE"); + sm->assoc_wpa_ie = NULL; + sm->assoc_wpa_ie_len = 0; } else { - cand = malloc(sizeof(*cand)); - if (cand == NULL) - return; - memset(cand, 0, sizeof(*cand)); - memcpy(cand->bssid, bssid, ETH_ALEN); - cand->priority = prio; - } + wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); + sm->assoc_wpa_ie = malloc(len); + if (sm->assoc_wpa_ie == NULL) + return -1; - /* Add candidate to the list; order by increasing priority value. i.e., - * highest priority (smallest value) first. */ - prev = NULL; - pos = wpa_s->pmksa_candidates; - while (pos) { - if (cand->priority <= pos->priority) - break; - prev = pos; - pos = pos->next; + memcpy(sm->assoc_wpa_ie, ie, len); + sm->assoc_wpa_ie_len = len; } - cand->next = pos; - if (prev) - prev->next = cand; - else - wpa_s->pmksa_candidates = cand; - wpa_msg(wpa_s, MSG_DEBUG, "RSN: added PMKSA cache " - "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); - rsn_preauth_candidate_process(wpa_s); + return 0; } -/* TODO: schedule periodic scans if current AP supports preauth */ -void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s, - struct wpa_scan_result *results, int count) +/** + * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA IE used in Beacon / Probe Response + * frame. + */ +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { - struct wpa_scan_result *r; - struct wpa_ie_data ie; - int i; + if (sm == NULL) + return -1; - if (wpa_s->current_ssid == NULL) - return; + free(sm->ap_wpa_ie); + if (ie == NULL || len == 0) { + wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE"); + sm->ap_wpa_ie = NULL; + sm->ap_wpa_ie_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len); + sm->ap_wpa_ie = malloc(len); + if (sm->ap_wpa_ie == NULL) + return -1; - pmksa_candidate_free(wpa_s); - - for (i = count - 1; i >= 0; i--) { - r = &results[i]; - if (r->ssid_len == wpa_s->current_ssid->ssid_len && - memcmp(r->ssid, wpa_s->current_ssid->ssid, r->ssid_len) == - 0 && - memcmp(r->bssid, wpa_s->bssid, ETH_ALEN) != 0 && - r->rsn_ie_len > 0 && - wpa_parse_wpa_ie(wpa_s, r->rsn_ie, r->rsn_ie_len, &ie) == - 0 && - (ie.capabilities & WPA_CAPABILITY_PREAUTH) && - pmksa_cache_get(wpa_s, r->bssid, NULL) == NULL) { - /* Give less priority to candidates found from normal - * scan results. */ - pmksa_candidate_add(wpa_s, r->bssid, - PMKID_CANDIDATE_PRIO_SCAN); - } + memcpy(sm->ap_wpa_ie, ie, len); + sm->ap_wpa_ie_len = len; } + + return 0; } -#else /* IEEE8021X_EAPOL */ -static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s) +/** + * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the RSN IE used in Beacon / Probe Response + * frame. + */ +int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { + if (sm == NULL) + return -1; + + free(sm->ap_rsn_ie); + if (ie == NULL || len == 0) { + wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE"); + sm->ap_rsn_ie = NULL; + sm->ap_rsn_ie_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len); + sm->ap_rsn_ie = malloc(len); + if (sm->ap_rsn_ie == NULL) + return -1; + + memcpy(sm->ap_rsn_ie, ie, len); + sm->ap_rsn_ie_len = len; + } + + return 0; } -#endif /* IEEE8021X_EAPOL */ + +/** + * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure + * + * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the + * parsed data into data. + */ +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) +{ + if (sm == NULL || sm->assoc_wpa_ie == NULL) { + wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE available from " + "association info"); + return -1; + } + if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data)) + return -2; + return 0; +} diff --git a/contrib/wpa_supplicant/wpa.h b/contrib/wpa_supplicant/wpa.h index a4f3f55..6b59d43 100644 --- a/contrib/wpa_supplicant/wpa.h +++ b/contrib/wpa_supplicant/wpa.h @@ -1,6 +1,22 @@ +/* + * wpa_supplicant - WPA definitions + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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_H #define WPA_H +#include "defs.h" + #define BIT(n) (1 << (n)) struct ieee802_1x_hdr { @@ -23,70 +39,6 @@ enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, EAPOL_KEY_TYPE_WPA = 254 }; -#define IEEE8021X_REPLAY_COUNTER_LEN 8 -#define IEEE8021X_KEY_SIGN_LEN 16 -#define IEEE8021X_KEY_IV_LEN 16 - -#define IEEE8021X_KEY_INDEX_FLAG 0x80 -#define IEEE8021X_KEY_INDEX_MASK 0x03 - -struct ieee802_1x_eapol_key { - u8 type; - u16 key_length; - /* does not repeat within the life of the keying material used to - * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ - u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; - u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as - * the key */ - u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} __attribute__ ((packed)); - - -#define WPA_NONCE_LEN 32 -#define WPA_REPLAY_COUNTER_LEN 8 - -struct wpa_eapol_key { - u8 type; - u16 key_info; - u16 key_length; - u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; - u8 key_nonce[WPA_NONCE_LEN]; - u8 key_iv[16]; - u8 key_rsc[8]; - u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ - u8 key_mic[16]; - u16 key_data_length; - /* followed by key_data_length bytes of key_data */ -} __attribute__ ((packed)); - -#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2)) -#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) -#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) -#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ -/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ -#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) -#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 -#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ -#define WPA_KEY_INFO_TXRX BIT(6) /* group */ -#define WPA_KEY_INFO_ACK BIT(7) -#define WPA_KEY_INFO_MIC BIT(8) -#define WPA_KEY_INFO_SECURE BIT(9) -#define WPA_KEY_INFO_ERROR BIT(10) -#define WPA_KEY_INFO_REQUEST BIT(11) -#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ - #define WPA_CAPABILITY_PREAUTH BIT(0) #define GENERIC_INFO_ELEM 0xdd @@ -109,4 +61,226 @@ enum { REASON_CIPHER_SUITE_REJECTED = 24 }; +#define PMKID_LEN 16 + + +struct wpa_sm; +struct wpa_ssid; +struct eapol_sm; +struct wpa_config_blob; + +struct wpa_sm_ctx { + void *ctx; /* pointer to arbitrary upper level context */ + + void (*set_state)(void *ctx, wpa_states state); + wpa_states (*get_state)(void *ctx); + void (*req_scan)(void *ctx, int sec, int usec); + void (*deauthenticate)(void * ctx, int reason_code); + void (*disassociate)(void *ctx, int reason_code); + int (*set_key)(void *ctx, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); + void (*scan)(void *eloop_ctx, void *timeout_ctx); + struct wpa_ssid * (*get_ssid)(void *ctx); + int (*get_bssid)(void *ctx, u8 *bssid); + int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf, + size_t len); + int (*get_beacon_ie)(void *ctx); + void (*cancel_auth_timeout)(void *ctx); + u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len, + size_t *msg_len, void **data_pos); + int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); +}; + + +enum wpa_sm_conf_params { + RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */, + RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */, + RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */, + WPA_PARAM_PROTO, + WPA_PARAM_PAIRWISE, + WPA_PARAM_GROUP, + WPA_PARAM_KEY_MGMT +}; + +struct wpa_ie_data { + int proto; + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int capabilities; + int num_pmkid; + const u8 *pmkid; +}; + +#ifndef CONFIG_NO_WPA + +struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx); +void wpa_sm_deinit(struct wpa_sm *sm); +void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); +void wpa_sm_notify_disassoc(struct wpa_sm *sm); +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len); +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); +void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth); +void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx); +void wpa_sm_set_config(struct wpa_sm *sm, struct wpa_ssid *config); +void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr); +void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname); +void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol); +int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, + size_t *wpa_ie_len); +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen); + +int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, + unsigned int value); +unsigned int wpa_sm_get_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param); + +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose); + +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); + +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data); + +int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len); +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); + +#else /* CONFIG_NO_WPA */ + +static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) +{ + return (struct wpa_sm *) 1; +} + +static inline void wpa_sm_deinit(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) +{ +} + +static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, + size_t pmk_len) +{ +} + +static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) +{ +} + +static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) +{ +} + +static inline void wpa_sm_set_config(struct wpa_sm *sm, + struct wpa_ssid *config) +{ +} + +static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) +{ +} + +static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname) +{ +} + +static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) +{ +} + +static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, + u8 *wpa_ie, + size_t *wpa_ie_len) +{ + return -1; +} + +static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) +{ + return 0; +} + +static inline int wpa_sm_set_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param, + unsigned int value) +{ + return -1; +} + +static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param) +{ + return 0; +} + +static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} + +static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, + int pairwise) +{ +} + +static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + return -1; +} + +static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + return -1; +} + +static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, + struct wpa_ie_data *data) +{ + return -1; +} + +#endif /* CONFIG_NO_WPA */ + #endif /* WPA_H */ diff --git a/contrib/wpa_supplicant/wpa_cli.c b/contrib/wpa_supplicant/wpa_cli.c index 8582fae..c274723 100644 --- a/contrib/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa_supplicant/wpa_cli.c @@ -18,6 +18,8 @@ #include <signal.h> #include <unistd.h> #include <dirent.h> +#include <errno.h> +#include <sys/time.h> #ifdef CONFIG_READLINE #include <readline/readline.h> #include <readline/history.h> @@ -104,7 +106,29 @@ static const char *commands_help = " preauthenticate <BSSID> = force preauthentication\n" " identity <network id> <identity> = configure identity for an SSID\n" " password <network id> <password> = configure password for an SSID\n" +" new_password <network id> <password> = change password for an SSID\n" +" pin <network id> <pin> = configure pin for an SSID\n" " otp <network id> <password> = configure one-time-password for an SSID\n" +" passphrase <network id> <passphrase> = configure private key passphrase\n" +" for an SSID\n" +" bssid <network id> <BSSID> = set preferred BSSID for an SSID\n" +" list_networks = list configured networks\n" +" select_network <network id> = select a network (disable others)\n" +" enable_network <network id> = enable a network\n" +" disable_network <network id> = disable a network\n" +" add_network = add a network\n" +" remove_network <network id> = remove a network\n" +" set_network <network id> <variable> <value> = set network variables " +"(shows\n" +" list of variables when run without arguments)\n" +" get_network <network id> <variable> = get network variables\n" +" save_config = save the current configuration\n" +" disconnect = disconnect and wait for reassociate command before " +"connecting\n" +" scan = request new BSS scan\n" +" scan_results = get latest scan results\n" +" get_capability <eap/pairwise/group/key_mgmt/proto/auth_alg> = " +"get capabilies\n" " terminate = terminate wpa_supplicant\n" " quit = exit wpa_cli\n"; @@ -113,14 +137,21 @@ static int wpa_cli_quit = 0; static int wpa_cli_attached = 0; static const char *ctrl_iface_dir = "/var/run/wpa_supplicant"; static char *ctrl_ifname = NULL; +static const char *pid_file = NULL; +static const char *action_file = NULL; static void usage(void) { - printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hv] " - "[command..]\n" + printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] " + "[-a<action file>] \\\n" + " [-P<pid file>] [-g<global ctrl>] [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: /var/run/wpa_supplicant\n" " default interface: first interface found in socket path\n" "%s", @@ -264,6 +295,12 @@ static void wpa_cli_show_variables(void) "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"); } @@ -356,7 +393,7 @@ static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[]) end = cmd + sizeof(cmd); pos = cmd; - pos += snprintf(pos, end - pos, "CTRL-RSP-IDENTITY-%s:%s", + pos += snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s", argv[0], argv[1]); for (i = 2; i < argc; i++) pos += snprintf(pos, end - pos, " %s", argv[i]); @@ -378,7 +415,7 @@ static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[]) end = cmd + sizeof(cmd); pos = cmd; - pos += snprintf(pos, end - pos, "CTRL-RSP-PASSWORD-%s:%s", + pos += snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s", argv[0], argv[1]); for (i = 2; i < argc; i++) pos += snprintf(pos, end - pos, " %s", argv[i]); @@ -387,6 +424,51 @@ static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256], *pos, *end; + int i; + + if (argc < 2) { + printf("Invalid NEW_PASSWORD command: needs two arguments " + "(network id and password)\n"); + return 0; + } + + end = cmd + sizeof(cmd); + pos = cmd; + pos += snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s", + argv[0], argv[1]); + for (i = 2; i < argc; i++) + pos += snprintf(pos, end - pos, " %s", argv[i]); + + 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; + + if (argc < 2) { + printf("Invalid PIN command: needs two arguments " + "(network id and pin)\n"); + return 0; + } + + end = cmd + sizeof(cmd); + pos = cmd; + pos += snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s", + argv[0], argv[1]); + for (i = 2; i < argc; i++) + pos += snprintf(pos, end - pos, " %s", argv[i]); + + 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; @@ -400,7 +482,7 @@ static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) end = cmd + sizeof(cmd); pos = cmd; - pos += snprintf(pos, end - pos, "CTRL-RSP-OTP-%s:%s", + pos += snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s", argv[0], argv[1]); for (i = 2; i < argc; i++) pos += snprintf(pos, end - pos, " %s", argv[i]); @@ -409,6 +491,246 @@ static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256], *pos, *end; + int i; + + if (argc < 2) { + printf("Invalid PASSPHRASE command: needs two arguments " + "(network id and passphrase)\n"); + return 0; + } + + end = cmd + sizeof(cmd); + pos = cmd; + pos += snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s", + argv[0], argv[1]); + for (i = 2; i < argc; i++) + pos += snprintf(pos, end - pos, " %s", argv[i]); + + 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; + + if (argc < 2) { + printf("Invalid BSSID command: needs two arguments (network " + "id and BSSID)\n"); + return 0; + } + + end = cmd + sizeof(cmd); + pos = cmd; + pos += snprintf(pos, end - pos, "BSSID"); + for (i = 0; i < argc; i++) + pos += snprintf(pos, end - pos, " %s", argv[i]); + + 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]; + + if (argc < 1) { + printf("Invalid SELECT_NETWORK command: needs one argument " + "(network id)\n"); + return 0; + } + + snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[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]; + + if (argc < 1) { + printf("Invalid ENABLE_NETWORK command: needs one argument " + "(network id)\n"); + return 0; + } + + snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[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]; + + if (argc < 1) { + printf("Invalid DISABLE_NETWORK command: needs one argument " + "(network id)\n"); + return 0; + } + + snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[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]; + + if (argc < 1) { + printf("Invalid REMOVE_NETWORK command: needs one argument " + "(network id)\n"); + return 0; + } + + snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[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]; + + 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 0; + } + + if (snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s", + argv[0], argv[1], argv[2]) >= sizeof(cmd) - 1) { + printf("Too long SET_NETWORK command.\n"); + return 0; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + + 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 0; + } + + if (snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s", + argv[0], argv[1]) >= sizeof(cmd) - 1) { + printf("Too long GET_NETWORK command.\n"); + return 0; + } + 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_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_get_capability(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[64]; + + if (argc != 1) { + printf("Invalid GET_CAPABILITY command: needs one argument\n"); + return 0; + } + + snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s", argv[0]); + + return wpa_ctrl_command(ctrl, cmd); +} + + static void wpa_cli_list_interfaces(struct wpa_ctrl *ctrl) { struct dirent *dent; @@ -473,6 +795,46 @@ static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + + 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\n"); + return 0; + } + + /* + * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB + * <driver_param> + */ + snprintf(cmd, sizeof(cmd), "INTERFACE_ADD %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] : ""); + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + + if (argc != 1) { + printf("Invalid INTERFACE_REMOVE command: needs one argument " + "(interface name)\n"); + return 0; + } + + snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]); + return wpa_ctrl_command(ctrl, cmd); +} + + struct wpa_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -495,9 +857,28 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "preauthenticate", wpa_cli_cmd_preauthenticate }, { "identity", wpa_cli_cmd_identity }, { "password", wpa_cli_cmd_password }, + { "new_password", wpa_cli_cmd_new_password }, + { "pin", wpa_cli_cmd_pin }, { "otp", wpa_cli_cmd_otp }, + { "passphrase", wpa_cli_cmd_passphrase }, + { "bssid", wpa_cli_cmd_bssid }, + { "list_networks", wpa_cli_cmd_list_networks }, + { "select_network", wpa_cli_cmd_select_network }, + { "enable_network", wpa_cli_cmd_enable_network }, + { "disable_network", wpa_cli_cmd_disable_network }, + { "add_network", wpa_cli_cmd_add_network }, + { "remove_network", wpa_cli_cmd_remove_network }, + { "set_network", wpa_cli_cmd_set_network }, + { "get_network", wpa_cli_cmd_get_network }, + { "save_config", wpa_cli_cmd_save_config }, + { "disconnect", wpa_cli_cmd_disconnect }, + { "scan", wpa_cli_cmd_scan }, + { "scan_results", wpa_cli_cmd_scan_results }, + { "get_capability", wpa_cli_cmd_get_capability }, { "reconfigure", wpa_cli_cmd_reconfigure }, { "terminate", wpa_cli_cmd_terminate }, + { "interface_add", wpa_cli_cmd_interface_add }, + { "interface_remove", wpa_cli_cmd_interface_remove }, { NULL, NULL } }; @@ -512,6 +893,11 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) while (cmd->cmd) { if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { match = cmd; + if (strcasecmp(cmd->cmd, argv[0]) == 0) { + /* we have an exact match */ + count = 1; + break; + } count++; } cmd++; @@ -536,7 +922,63 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) } -static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) +static int str_match(const char *a, const char *b) +{ + return strncmp(a, b, strlen(b)) == 0; +} + + +static int wpa_cli_exec(const char *program, const char *arg1, + const char *arg2) +{ + char *cmd; + size_t len; + + len = strlen(program) + strlen(arg1) + strlen(arg2) + 3; + cmd = malloc(len); + if (cmd == NULL) + return -1; + snprintf(cmd, len, "%s %s %s", program, arg1, arg2); + system(cmd); + free(cmd); + + return 0; +} + + +static void wpa_cli_action_process(const char *msg) +{ + const char *pos; + + pos = msg; + if (*pos == '<') { + /* skip priority */ + pos = strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + if (str_match(pos, WPA_EVENT_CONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED"); + } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED"); + } else if (str_match(pos, WPA_EVENT_TERMINATING)) { + printf("wpa_supplicant is terminating - stop monitoring\n"); + wpa_cli_quit = 1; + } +} + + +static void wpa_cli_action_cb(char *msg, size_t len) +{ + wpa_cli_action_process(msg); +} + + +static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, + int action_monitor) { int first = 1; if (ctrl_conn == NULL) @@ -546,10 +988,14 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) size_t len = sizeof(buf) - 1; if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { buf[len] = '\0'; - if (in_read && first) - printf("\n"); - first = 0; - printf("%s\n", buf); + if (action_monitor) + wpa_cli_action_process(buf); + else { + if (in_read && first) + printf("\n"); + first = 0; + printf("%s\n", buf); + } } else { printf("Could not read pending message.\n"); break; @@ -620,7 +1066,7 @@ static void wpa_cli_interactive(void) #endif /* CONFIG_READLINE */ do { - wpa_cli_recv_pending(ctrl_conn, 0); + wpa_cli_recv_pending(ctrl_conn, 0, 0); #ifndef CONFIG_NATIVE_WINDOWS alarm(1); #endif /* CONFIG_NATIVE_WINDOWS */ @@ -663,6 +1109,11 @@ static void wpa_cli_interactive(void) argc++; if (argc == max_args) break; + if (*pos == '"') { + char *pos2 = strrchr(pos, '"'); + if (pos2) + pos = pos2 + 1; + } while (*pos != '\0' && *pos != ' ') pos++; if (*pos == ' ') @@ -687,7 +1138,8 @@ static void wpa_cli_interactive(void) while (*p == ' ' || *p == '\t') p++; if (strncasecmp(p, "pa", 2) == 0 || - strncasecmp(p, "o", 1) == 0) { + strncasecmp(p, "o", 1) == 0 || + strncasecmp(p, "n", 1)) { h = remove_history(where_history()); if (h) { free(h->line); @@ -706,9 +1158,58 @@ static void wpa_cli_interactive(void) } -static void wpa_cli_terminate(int sig) +static void wpa_cli_action(struct wpa_ctrl *ctrl) +{ + fd_set rfds; + int fd, res; + struct timeval tv; + char buf[16]; + size_t len; + + fd = wpa_ctrl_get_fd(ctrl); + + while (!wpa_cli_quit) { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + tv.tv_sec = 2; + 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, 0, 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 || memcmp(buf, "PONG", 4) != 0) { + printf("wpa_supplicant did not reply to PING " + "command - exiting\n"); + break; + } + } + } +} + + +static void wpa_cli_cleanup(void) { wpa_cli_close_connection(); + if (pid_file) + unlink(pid_file); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ +} + +static void wpa_cli_terminate(int sig) +{ + wpa_cli_cleanup(); exit(0); } @@ -735,7 +1236,7 @@ static void wpa_cli_alarm(int sig) } } if (ctrl_conn) - wpa_cli_recv_pending(ctrl_conn, 1); + wpa_cli_recv_pending(ctrl_conn, 1, 0); alarm(1); } #endif /* CONFIG_NATIVE_WINDOWS */ @@ -746,6 +1247,9 @@ int main(int argc, char *argv[]) int interactive; int warning_displayed = 0; int c; + int daemonize = 0; + FILE *f; + const char *global = NULL; #ifdef CONFIG_NATIVE_WINDOWS WSADATA wsaData; @@ -756,10 +1260,19 @@ int main(int argc, char *argv[]) #endif /* CONFIG_NATIVE_WINDOWS */ for (;;) { - c = getopt(argc, argv, "hi:p:v"); + c = getopt(argc, argv, "a:Bg: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 'h': usage(); return 0; @@ -772,18 +1285,30 @@ int main(int argc, char *argv[]) case 'p': ctrl_iface_dir = optarg; break; + case 'P': + pid_file = optarg; + break; default: usage(); return -1; } } - interactive = argc == optind; + interactive = (argc == optind) && (action_file == NULL); if (interactive) printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license); - for (;;) { + if (global) { + ctrl_conn = wpa_ctrl_open(global); + if (ctrl_conn == NULL) { + perror("Failed to connect to wpa_supplicant - " + "wpa_ctrl_open"); + return -1; + } + } + + for (; !global;) { if (ctrl_ifname == NULL) { struct dirent *dent; DIR *dir = opendir(ctrl_iface_dir); @@ -828,23 +1353,39 @@ int main(int argc, char *argv[]) signal(SIGALRM, wpa_cli_alarm); #endif /* CONFIG_NATIVE_WINDOWS */ - if (interactive) { + if (interactive || action_file) { if (wpa_ctrl_attach(ctrl_conn) == 0) { wpa_cli_attached = 1; } else { printf("Warning: Failed to attach to " "wpa_supplicant.\n"); + if (!interactive) + return -1; + } + } + + if (daemonize && daemon(0, 0)) { + perror("daemon"); + return -1; + } + + if (pid_file) { + f = fopen(pid_file, "w"); + if (f) { + fprintf(f, "%u\n", getpid()); + fclose(f); } + } + + if (interactive) wpa_cli_interactive(); - } else + else if (action_file) + wpa_cli_action(ctrl_conn); + else wpa_request(ctrl_conn, argc - optind, &argv[optind]); free(ctrl_ifname); - wpa_cli_close_connection(); - -#ifdef CONFIG_NATIVE_WINDOWS - WSACleanup(); -#endif /* CONFIG_NATIVE_WINDOWS */ + wpa_cli_cleanup(); return 0; } diff --git a/contrib/wpa_supplicant/wpa_ctrl.h b/contrib/wpa_supplicant/wpa_ctrl.h index 48b088a..c8fa48d 100644 --- a/contrib/wpa_supplicant/wpa_ctrl.h +++ b/contrib/wpa_supplicant/wpa_ctrl.h @@ -1,15 +1,185 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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_CTRL_H #define WPA_CTRL_H +#ifdef __cplusplus +extern "C" { +#endif + +/* wpa_supplicant control interface - fixed message prefixes */ + +/** Interactive request for identity/password/pin */ +#define WPA_CTRL_REQ "CTRL-REQ-" + +/** Response to identity/password/pin request */ +#define WPA_CTRL_RSP "CTRL-RSP-" + +/* Event messages with fixed prefix */ +/** Authentication completed successfully and data connection enabled */ +#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " +/** Disconnected, data connection is not available */ +#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** wpa_supplicant is exiting */ +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " +/** Password change was completed successfully */ +#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " +/** EAP-Request/Notification received */ +#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " +/** EAP authentication started (EAP-Request/Identity received) */ +#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " +/** EAP method selected */ +#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +/** EAP authentication failed (EAP-Failure received) */ +#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " + + +/* wpa_supplicant/hostapd control interface access */ + +/** + * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd. + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path + * is configured in wpa_supplicant/hostapd and other programs using the control + * interface need to use matching path configuration. + */ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); + + +/** + * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * + * This function is used to close a control interface. + */ void wpa_ctrl_close(struct wpa_ctrl *ctrl); -int wpa_ctrl_request(struct wpa_ctrl *ctrl, char *cmd, size_t cmd_len, + + +/** + * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * @cmd: Command; usually, ASCII text, e.g., "PING" + * @cmd_len: Length of the cmd in bytes + * @reply: Buffer for the response + * @reply_len: Reply buffer length + * @msg_cb: Callback function for unsolicited messages or %NULL if not used + * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout + * + * This function is used to send commands to wpa_supplicant/hostapd. Received + * response will be written to reply and reply_len is set to the actual length + * of the reply. This function will block for up to two seconds while waiting + * for the reply. If unsolicited messages are received, the blocking time may + * be longer. + * + * msg_cb can be used to register a callback function that will be called for + * unsolicited messages received while waiting for the command response. These + * messages may be received if wpa_ctrl_request() is called at the same time as + * wpa_supplicant/hostapd is sending such a message. This can happen only if + * the program has used wpa_ctrl_attach() to register itself as a monitor for + * event messages. Alternatively to msg_cb, programs can register two control + * interface connections and use one of them for commands and the other one for + * receiving event messages, in other words, call wpa_ctrl_attach() only for + * the control interface connection that will be used for event messages. + */ +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)); + + +/** + * wpa_ctrl_attach - Register as an event monitor for the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function registers the control interface connection as a monitor for + * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the + * control interface connection starts receiving event messages that can be + * read with wpa_ctrl_recv(). + */ int wpa_ctrl_attach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_detach - Unregister event monitor from the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function unregisters the control interface connection as a monitor for + * wpa_supplicant/hostapd events, i.e., cancels the registration done with + * wpa_ctrl_attach(). + */ int wpa_ctrl_detach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_recv - Receive a pending control interface message + * @ctrl: Control interface data from wpa_ctrl_open() + * @reply: Buffer for the message data + * @reply_len: Length of the reply buffer + * Returns: 0 on success, -1 on failure + * + * This function will receive a pending control interface message. This + * function will block if no messages are available. The received response will + * be written to reply and reply_len is set to the actual length of the reply. + * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() + * must have been used to register the control interface as an event monitor. + */ int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); + + +/** + * wpa_ctrl_pending - Check whether there are pending event messages + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: Non-zero if there are pending messages + * + * This function will check whether there are any pending control interface + * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is + * only used for event messages, i.e., wpa_ctrl_attach() must have been used to + * register the control interface as an event monitor. + */ int wpa_ctrl_pending(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_get_fd - Get file descriptor used by the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: File descriptor used for the connection + * + * This function can be used to get the file descriptor that is used for the + * control interface connection. The returned value can be used, e.g., with + * select() while waiting for multiple events. + * + * The returned file descriptor must not be used directly for sending or + * receiving packets; instead, the library functions wpa_ctrl_request() and + * wpa_ctrl_recv() must be used for this. + */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); +#ifdef CONFIG_CTRL_IFACE_UDP +#define WPA_CTRL_IFACE_PORT 9877 +#define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +#ifdef __cplusplus +} +#endif + #endif /* WPA_CTRL_H */ diff --git a/contrib/wpa_supplicant/wpa_gui-qt4/eventhistory.ui b/contrib/wpa_supplicant/wpa_gui-qt4/eventhistory.ui new file mode 100644 index 0000000..3735fb7 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/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/contrib/wpa_supplicant/wpa_gui-qt4/eventhistory.ui.h b/contrib/wpa_supplicant/wpa_gui-qt4/eventhistory.ui.h new file mode 100644 index 0000000..cb2caab --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/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/contrib/wpa_supplicant/wpa_gui-qt4/main.cpp b/contrib/wpa_supplicant/wpa_gui-qt4/main.cpp new file mode 100644 index 0000000..a78473a --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/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/contrib/wpa_supplicant/wpa_gui-qt4/networkconfig.ui b/contrib/wpa_supplicant/wpa_gui-qt4/networkconfig.ui new file mode 100644 index 0000000..1f98372 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/networkconfig.ui @@ -0,0 +1,455 @@ +<!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>413</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>textLabel1</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>textLabel2</cstring> + </property> + <property name="text"> + <string>Authentication</string> + </property> + </widget> + <widget class="QComboBox" row="1" 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="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Encryption</string> + </property> + </widget> + <widget class="QComboBox" row="2" 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="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>PSK</string> + </property> + </widget> + <widget class="QLineEdit" row="3" 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="4" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>EAP method</string> + </property> + </widget> + <widget class="QComboBox" row="4" column="1"> + <property name="name"> + <cstring>eapSelect</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Identity</string> + </property> + </widget> + <widget class="QLineEdit" row="5" 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="6" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Password</string> + </property> + </widget> + <widget class="QLineEdit" row="6" 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="7" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>CA certificate</string> + </property> + </widget> + <widget class="QLineEdit" row="7" column="1"> + <property name="name"> + <cstring>cacertEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QButtonGroup" row="8" 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>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/contrib/wpa_supplicant/wpa_gui-qt4/networkconfig.ui.h b/contrib/wpa_supplicant/wpa_gui-qt4/networkconfig.ui.h new file mode 100644 index 0000000..5e87462 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/networkconfig.ui.h @@ -0,0 +1,513 @@ +/**************************************************************************** +** 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. +*****************************************************************************/ + + +enum { + AUTH_NONE = 0, + AUTH_IEEE8021X = 1, + AUTH_WPA_PSK = 2, + AUTH_WPA_EAP = 3, + AUTH_WPA2_PSK = 4, + AUTH_WPA2_EAP = 5 +}; + +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); + + 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()) + 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()) + 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(); + 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); +} + + +void NetworkConfig::paramsFromConfig( int network_id ) +{ + int i; + + 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); + 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); + 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); + 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); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "CCMP")) + 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); + 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'; + pskEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id); + reply_len = sizeof(reply); + 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); + 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'; + passwordEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id); + reply_len = sizeof(reply); + 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); + 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++) { + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", network_id, i); + reply_len = sizeof(reply); + 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'; + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + + switch (i) { + case 0: + wep0Edit->setText(reply + 1); + break; + case 1: + wep1Edit->setText(reply + 1); + break; + case 2: + wep2Edit->setText(reply + 1); + break; + case 3: + wep3Edit->setText(reply + 1); + break; + } + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id); + reply_len = sizeof(reply); + 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/contrib/wpa_supplicant/wpa_gui-qt4/scanresults.ui b/contrib/wpa_supplicant/wpa_gui-qt4/scanresults.ui new file mode 100644 index 0000000..66c8b4b --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/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">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/contrib/wpa_supplicant/wpa_gui-qt4/scanresults.ui.h b/contrib/wpa_supplicant/wpa_gui-qt4/scanresults.ui.h new file mode 100644 index 0000000..530d2e6 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/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/contrib/wpa_supplicant/wpa_gui-qt4/setup-mingw-cross-compiling b/contrib/wpa_supplicant/wpa_gui-qt4/setup-mingw-cross-compiling new file mode 100755 index 0000000..e173b00 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/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/contrib/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui b/contrib/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui new file mode 100644 index 0000000..c3d545f --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/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">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/contrib/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui.h b/contrib/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui.h new file mode 100644 index 0000000..4b47ccd --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** 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. +*****************************************************************************/ + +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/contrib/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro b/contrib/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro new file mode 100644 index 0000000..a1ad0ee --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro @@ -0,0 +1,41 @@ +TEMPLATE = app +LANGUAGE = C++ + +CONFIG += qt warn_on release + +win32 { + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_UDP +} else:win32-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_UDP +} + +INCLUDEPATH += . .. + +HEADERS += wpamsg.h + +SOURCES += main.cpp \ + ../wpa_ctrl.c + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.ui + + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} + + + +#The following line was inserted by qt3to4 +QT += qt3support +#The following line was inserted by qt3to4 +CONFIG += uic3 + diff --git a/contrib/wpa_supplicant/wpa_gui-qt4/wpagui.ui b/contrib/wpa_supplicant/wpa_gui-qt4/wpagui.ui new file mode 100644 index 0000000..7097bfa --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/wpagui.ui @@ -0,0 +1,464 @@ +<!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> +</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">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> +</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/contrib/wpa_supplicant/wpa_gui-qt4/wpagui.ui.h b/contrib/wpa_supplicant/wpa_gui-qt4/wpagui.ui.h new file mode 100644 index 0000000..58760e7 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/wpagui.ui.h @@ -0,0 +1,651 @@ +/**************************************************************************** +** 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 + + +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; + + 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"); +#else /* CONFIG_CTRL_IFACE_UDP */ + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + free(ctrl_iface); + ctrl_iface = NULL; + if (dir) { + while ((dent = readdir(dir))) { + 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_UDP */ + } + + if (ctrl_iface == NULL) + return -1; + + 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); + + 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; + } + + msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn), + QSocketNotifier::Read, this); + connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs())); + + adapterSelect->clear(); + adapterSelect->insertItem(ctrl_iface); + adapterSelect->setCurrentItem(0); + + 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 if (group_cipher) { + encr.append(group_cipher); + encr.append(" [group key only]"); + } else { + encr.append("?"); + } + 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-2005,\n" + "Jouni Malinen <jkmaline@cc.hut.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; + + 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 (wpa_ctrl_pending(monitor_conn)) { + 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(); +} diff --git a/contrib/wpa_supplicant/wpa_gui-qt4/wpamsg.h b/contrib/wpa_supplicant/wpa_gui-qt4/wpamsg.h new file mode 100644 index 0000000..0808e1f --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui-qt4/wpamsg.h @@ -0,0 +1,28 @@ +#ifndef WPAMSG_H +#define WPAMSG_H + +#include <QDateTime> +#include <QLinkedList> + +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; +}; + +typedef QLinkedList<WpaMsg> WpaMsgList; + +#endif /* WPAMSG_H */ diff --git a/contrib/wpa_supplicant/wpa_gui/eventhistory.ui b/contrib/wpa_supplicant/wpa_gui/eventhistory.ui new file mode 100644 index 0000000..3735fb7 --- /dev/null +++ b/contrib/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/contrib/wpa_supplicant/wpa_gui/eventhistory.ui.h b/contrib/wpa_supplicant/wpa_gui/eventhistory.ui.h new file mode 100644 index 0000000..8d8fa48 --- /dev/null +++ b/contrib/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) +{ + QListViewItem *item; + item = new QListViewItem(eventListView, + msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"), + msg.getMsg()); + if (item == NULL) + return; + eventListView->setSelected(item, false); +} diff --git a/contrib/wpa_supplicant/wpa_gui/main.cpp b/contrib/wpa_supplicant/wpa_gui/main.cpp new file mode 100644 index 0000000..a78473a --- /dev/null +++ b/contrib/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/contrib/wpa_supplicant/wpa_gui/networkconfig.ui b/contrib/wpa_supplicant/wpa_gui/networkconfig.ui new file mode 100644 index 0000000..1f98372 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui/networkconfig.ui @@ -0,0 +1,455 @@ +<!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>413</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>textLabel1</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>textLabel2</cstring> + </property> + <property name="text"> + <string>Authentication</string> + </property> + </widget> + <widget class="QComboBox" row="1" 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="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Encryption</string> + </property> + </widget> + <widget class="QComboBox" row="2" 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="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>PSK</string> + </property> + </widget> + <widget class="QLineEdit" row="3" 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="4" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>EAP method</string> + </property> + </widget> + <widget class="QComboBox" row="4" column="1"> + <property name="name"> + <cstring>eapSelect</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Identity</string> + </property> + </widget> + <widget class="QLineEdit" row="5" 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="6" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Password</string> + </property> + </widget> + <widget class="QLineEdit" row="6" 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="7" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>CA certificate</string> + </property> + </widget> + <widget class="QLineEdit" row="7" column="1"> + <property name="name"> + <cstring>cacertEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QButtonGroup" row="8" 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>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/contrib/wpa_supplicant/wpa_gui/networkconfig.ui.h b/contrib/wpa_supplicant/wpa_gui/networkconfig.ui.h new file mode 100644 index 0000000..c03260b --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui/networkconfig.ui.h @@ -0,0 +1,513 @@ +/**************************************************************************** +** 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. +*****************************************************************************/ + + +enum { + AUTH_NONE = 0, + AUTH_IEEE8021X = 1, + AUTH_WPA_PSK = 2, + AUTH_WPA_EAP = 3, + AUTH_WPA2_PSK = 4, + AUTH_WPA2_EAP = 5 +}; + +void NetworkConfig::init() +{ + wpagui = NULL; + new_network = false; +} + +void NetworkConfig::paramsFromScanResults(QListViewItem *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); + + 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()) + 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()) + 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(); + 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); +} + + +void NetworkConfig::paramsFromConfig( int network_id ) +{ + int i; + + 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); + 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); + 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); + 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); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "CCMP")) + 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); + 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'; + pskEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id); + reply_len = sizeof(reply); + 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); + 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'; + passwordEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id); + reply_len = sizeof(reply); + 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); + 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++) { + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", network_id, i); + reply_len = sizeof(reply); + 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'; + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + + switch (i) { + case 0: + wep0Edit->setText(reply + 1); + break; + case 1: + wep1Edit->setText(reply + 1); + break; + case 2: + wep2Edit->setText(reply + 1); + break; + case 3: + wep3Edit->setText(reply + 1); + break; + } + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id); + reply_len = sizeof(reply); + 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/contrib/wpa_supplicant/wpa_gui/scanresults.ui b/contrib/wpa_supplicant/wpa_gui/scanresults.ui new file mode 100644 index 0000000..66c8b4b --- /dev/null +++ b/contrib/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">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/contrib/wpa_supplicant/wpa_gui/scanresults.ui.h b/contrib/wpa_supplicant/wpa_gui/scanresults.ui.h new file mode 100644 index 0000000..61555b5 --- /dev/null +++ b/contrib/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 QListViewItem(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( QListViewItem * sel ) +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(wpagui); + nc->paramsFromScanResults(sel); + nc->show(); + nc->exec(); + } diff --git a/contrib/wpa_supplicant/wpa_gui/userdatarequest.ui b/contrib/wpa_supplicant/wpa_gui/userdatarequest.ui new file mode 100644 index 0000000..c3d545f --- /dev/null +++ b/contrib/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">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/contrib/wpa_supplicant/wpa_gui/userdatarequest.ui.h b/contrib/wpa_supplicant/wpa_gui/userdatarequest.ui.h new file mode 100644 index 0000000..4b47ccd --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui/userdatarequest.ui.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** 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. +*****************************************************************************/ + +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/contrib/wpa_supplicant/wpa_gui/wpa_gui.pro b/contrib/wpa_supplicant/wpa_gui/wpa_gui.pro new file mode 100644 index 0000000..7134c7d --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui/wpa_gui.pro @@ -0,0 +1,31 @@ +TEMPLATE = app +LANGUAGE = C++ + +CONFIG += qt warn_on release + +# For Windows build: +#LIBS += -lws2_32 -static +#DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_UDP + +INCLUDEPATH += .. + +HEADERS += wpamsg.h + +SOURCES += main.cpp \ + ../wpa_ctrl.c + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.ui + + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} + + + diff --git a/contrib/wpa_supplicant/wpa_gui/wpagui.ui b/contrib/wpa_supplicant/wpa_gui/wpagui.ui new file mode 100644 index 0000000..7097bfa --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui/wpagui.ui @@ -0,0 +1,464 @@ +<!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> +</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">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> +</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/contrib/wpa_supplicant/wpa_gui/wpagui.ui.h b/contrib/wpa_supplicant/wpa_gui/wpagui.ui.h new file mode 100644 index 0000000..58760e7 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui/wpagui.ui.h @@ -0,0 +1,651 @@ +/**************************************************************************** +** 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 + + +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; + + 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"); +#else /* CONFIG_CTRL_IFACE_UDP */ + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + free(ctrl_iface); + ctrl_iface = NULL; + if (dir) { + while ((dent = readdir(dir))) { + 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_UDP */ + } + + if (ctrl_iface == NULL) + return -1; + + 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); + + 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; + } + + msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn), + QSocketNotifier::Read, this); + connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs())); + + adapterSelect->clear(); + adapterSelect->insertItem(ctrl_iface); + adapterSelect->setCurrentItem(0); + + 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 if (group_cipher) { + encr.append(group_cipher); + encr.append(" [group key only]"); + } else { + encr.append("?"); + } + 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-2005,\n" + "Jouni Malinen <jkmaline@cc.hut.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; + + 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 (wpa_ctrl_pending(monitor_conn)) { + 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(); +} diff --git a/contrib/wpa_supplicant/wpa_gui/wpamsg.h b/contrib/wpa_supplicant/wpa_gui/wpamsg.h new file mode 100644 index 0000000..2984017 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_gui/wpamsg.h @@ -0,0 +1,27 @@ +#ifndef WPAMSG_H +#define WPAMSG_H + +#include <qdatetime.h> + +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; +}; + +typedef QValueList<WpaMsg> WpaMsgList; + +#endif /* WPAMSG_H */ diff --git a/contrib/wpa_supplicant/wpa_i.h b/contrib/wpa_supplicant/wpa_i.h new file mode 100644 index 0000000..4442832 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_i.h @@ -0,0 +1,197 @@ +/* + * wpa_supplicant - Internal WPA state machine definitions + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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_I_H +#define WPA_I_H + +#define WPA_NONCE_LEN 32 +#define WPA_REPLAY_COUNTER_LEN 8 + + +struct rsn_pmksa_candidate; + +/** + * struct wpa_ptk - WPA Pairwise Transient Key + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + */ +struct wpa_ptk { + u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */ + u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */ + u8 tk1[16]; /* Temporal Key 1 (TK1) */ + union { + u8 tk2[16]; /* Temporal Key 2 (TK2) */ + struct { + u8 tx_mic_key[8]; + u8 rx_mic_key[8]; + } auth; + } u; +} __attribute__ ((packed)); + + +/** + * struct rsn_pmksa_cache - PMKSA cache entry + */ +struct rsn_pmksa_cache { + struct rsn_pmksa_cache *next; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + time_t expiration; + time_t reauth_time; + int akmp; /* WPA_KEY_MGMT_* */ + u8 aa[ETH_ALEN]; + struct wpa_ssid *ssid; + int opportunistic; +}; + + +/** + * struct wpa_sm - Internal WPA state machine data + */ +struct wpa_sm { + u8 pmk[PMK_LEN]; + size_t pmk_len; + struct wpa_ptk ptk, tptk; + int ptk_set, tptk_set; + u8 snonce[WPA_NONCE_LEN]; + u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ + int renew_snonce; + u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int rx_replay_counter_set; + u8 request_counter[WPA_REPLAY_COUNTER_LEN]; + + struct eapol_sm *eapol; /* EAPOL state machine from upper level code */ + + struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ + struct rsn_pmksa_cache *cur_pmksa; /* current PMKSA entry */ + int pmksa_count; /* number of entries in PMKSA cache */ + struct rsn_pmksa_candidate *pmksa_candidates; + + struct l2_packet_data *l2_preauth; + u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or + * 00:00:00:00:00:00 if no pre-auth is + * in progress */ + struct eapol_sm *preauth_eapol; + + struct wpa_sm_ctx *ctx; + + void *scard_ctx; /* context for smartcard callbacks */ + int fast_reauth; /* whether EAP fast re-authentication is enabled */ + + struct wpa_ssid *cur_ssid; + + u8 own_addr[ETH_ALEN]; + const char *ifname; + u8 bssid[ETH_ALEN]; + + unsigned int dot11RSNAConfigPMKLifetime; + unsigned int dot11RSNAConfigPMKReauthThreshold; + unsigned int dot11RSNAConfigSATimeout; + + unsigned int dot11RSNA4WayHandshakeFailures; + + /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ + unsigned int proto; + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int key_mgmt; + + u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ + size_t assoc_wpa_ie_len; + u8 *ap_wpa_ie, *ap_rsn_ie; + size_t ap_wpa_ie_len, ap_rsn_ie_len; +}; + + +static inline void wpa_sm_set_state(struct wpa_sm *sm, wpa_states state) +{ + sm->ctx->set_state(sm->ctx->ctx, state); +} + +static inline wpa_states wpa_sm_get_state(struct wpa_sm *sm) +{ + return sm->ctx->get_state(sm->ctx->ctx); +} + +static inline void wpa_sm_req_scan(struct wpa_sm *sm, int sec, int usec) +{ + sm->ctx->req_scan(sm->ctx->ctx, sec, usec); +} + +static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) +{ + sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); +} + +static inline void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code) +{ + sm->ctx->disassociate(sm->ctx->ctx, reason_code); +} + +static inline int wpa_sm_set_key(struct wpa_sm *sm, 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) +{ + return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); +} + +static inline struct wpa_ssid * wpa_sm_get_ssid(struct wpa_sm *sm) +{ + return sm->ctx->get_ssid(sm->ctx->ctx); +} + +static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid) +{ + return sm->ctx->get_bssid(sm->ctx->ctx, bssid); +} + +static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest, + u16 proto, const u8 *buf, size_t len) +{ + return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len); +} + +static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm) +{ + return sm->ctx->get_beacon_ie(sm->ctx->ctx); +} + +static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm) +{ + sm->ctx->cancel_auth_timeout(sm->ctx->ctx); +} + +static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len, + msg_len, data_pos); +} + +static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid, + const u8 *pmkid) +{ + return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid); +} + +static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid, + const u8 *pmkid) +{ + return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid); +} + +#endif /* WPA_I_H */ diff --git a/contrib/wpa_supplicant/wpa_passphrase.c b/contrib/wpa_supplicant/wpa_passphrase.c index 5a8203b..2a68cb5 100644 --- a/contrib/wpa_supplicant/wpa_passphrase.c +++ b/contrib/wpa_supplicant/wpa_passphrase.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - ASCII passphrase to WPA PSK tool - * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -23,15 +23,36 @@ int main(int argc, char *argv[]) { unsigned char psk[32]; int i; - char *ssid, *passphrase; + char *ssid, *passphrase, buf[64], *pos; - if (argc != 3) { - printf("usage: wpa_passphrase <ssid> <passphrase>\n"); + 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]; - passphrase = argv[2]; + + 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 (strlen(passphrase) < 8 || strlen(passphrase) > 63) { printf("Passphrase must be 8..63 characters\n"); diff --git a/contrib/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa_supplicant/wpa_supplicant.conf index e8be91a..92e9ec1 100644 --- a/contrib/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa_supplicant/wpa_supplicant.conf @@ -1,9 +1,28 @@ ##### 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) # # Interface for separate control program. If this is specified, wpa_supplicant @@ -52,13 +71,15 @@ eapol_version=1 # 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) +# 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 driver to +# BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to # enable operation with hidden SSIDs and optimized roaming; in this mode, -# 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 +# 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 ap_scan=1 # EAP fast re-authentication @@ -67,6 +88,31 @@ ap_scan=1 # 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 + +# 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" + +# 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 + # network block # # Each network (usually AP's sharing the same SSID) is configured as a separate @@ -75,6 +121,11 @@ fast_reauth=1 # # 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) +# # ssid: SSID (mandatory); either as an ASCII string with double quotation or # as hex string; network name # @@ -95,9 +146,9 @@ fast_reauth=1 # 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 is not using this priority to -# select the order for scanning. Instead, it uses the order the networks are in -# the configuration file. +# 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) @@ -155,10 +206,21 @@ fast_reauth=1 # only when the passphrase or SSID has actually changed. # # eapol_flags: IEEE 802.1X/EAPOL options (bit field) -# Dynamic WEP key require for non-WPA mode +# 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. +# +# 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) # # Following fields are only used with internal EAP implementation. # eap: space-separated list of accepted EAP methods @@ -182,16 +244,40 @@ fast_reauth=1 # unencrypted identity with EAP types that support different tunnelled # identity, e.g., EAP-TTLS) # password: Password string for EAP -# ca_cert: File path to CA certificate file. This file can have one or more -# trusted CA certificates. If ca_cert is not included, server certificate -# will not be verified. This is insecure and the CA file should always be -# configured. +# 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. +# 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". +# 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. -# private_key_passwd: Password for private key file +# 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" +# 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 @@ -205,6 +291,13 @@ fast_reauth=1 # 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: Substring 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 this string in +# an alternative subject name extension. +# altSubjectName string is in following format: TYPE:VALUE +# Example: DNS:server.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") @@ -230,25 +323,30 @@ fast_reauth=1 # 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 is not included, server -# certificate will not be verified. This is insecure and the CA file -# should always be configured. +# 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. # # EAP-PSK variables: # eappsk: 16-byte (128-bit, 32 hex digits) pre-shared key in hex format # nai: user NAI -# server_nai: authentication server NAI # # 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. +# 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=1 option enables in-line provisioning of EAP-FAST # credentials (PAC) # @@ -400,7 +498,6 @@ network={ identity="eap_psk_user" eappsk=06b4be19da289f475aa46a33cb793029 nai="eap_psk_user@example.com" - server_nai="as@example.com" } @@ -441,6 +538,17 @@ network={ 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" @@ -503,3 +611,52 @@ network={ 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== +} diff --git a/contrib/wpa_supplicant/wpa_supplicant.h b/contrib/wpa_supplicant/wpa_supplicant.h index e5ad182..e7e6f0d 100644 --- a/contrib/wpa_supplicant/wpa_supplicant.h +++ b/contrib/wpa_supplicant/wpa_supplicant.h @@ -1,3 +1,17 @@ +/* + * wpa_supplicant - Exported functions for wpa_supplicant modules + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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_H #define WPA_SUPPLICANT_H @@ -6,21 +20,164 @@ */ struct wpa_supplicant; -typedef enum { - EVENT_ASSOC, EVENT_DISASSOC, EVENT_MICHAEL_MIC_FAILURE, - EVENT_SCAN_RESULTS, EVENT_ASSOCINFO, EVENT_INTERFACE_STATUS, +/** + * enum wpa_event_type - Event type for wpa_supplicant_event() calls + */ +typedef enum wpa_event_type { + /** + * EVENT_ASSOC - Association completed + * + * This event needs to be delivered when the driver completes IEEE + * 802.11 association or reassociation successfully. + * wpa_driver_ops::get_bssid() is expected to provide the current BSSID + * after this even has been generated. In addition, optional + * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide + * more information about the association. If the driver interface gets + * both of these events at the same time, it can also include the + * assoc_info data in EVENT_ASSOC call. + */ + EVENT_ASSOC, + + /** + * EVENT_DISASSOC - Association lost + * + * This event should be called when association is lost either due to + * receiving deauthenticate or disassociate frame from the AP or when + * sending either of these frames to the current AP. + */ + EVENT_DISASSOC, + + /** + * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected + * + * This event must be delivered when a Michael MIC error is detected by + * the local driver. Additional data is for event processing is + * provided with union wpa_event_data::michael_mic_failure. This + * information is used to request new encyption key and to initiate + * TKIP countermeasures if needed. + */ + EVENT_MICHAEL_MIC_FAILURE, + + /** + * EVENT_SCAN_RESULTS - Scan results available + * + * This event must be called whenever scan results are available to be + * fetched with struct wpa_driver_ops::get_scan_results(). This event + * is expected to be used some time after struct wpa_driver_ops::scan() + * is called. If the driver provides an unsolicited event when the scan + * has been completed, this event can be used to trigger + * EVENT_SCAN_RESULTS call. If such event is not available from the + * driver, the driver wrapper code is expected to use a registered + * timeout to generate EVENT_SCAN_RESULTS call after the time that the + * scan is expected to be completed. + */ + EVENT_SCAN_RESULTS, + + /** + * EVENT_ASSOCINFO - Report optional extra information for association + * + * This event can be used to report extra association information for + * EVENT_ASSOC processing. This extra information includes IEs from + * association frames and Beacon/Probe Response frames in union + * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before + * EVENT_ASSOC. Alternatively, the driver interface can include + * assoc_info data in the EVENT_ASSOC call if it has all the + * information available at the same point. + */ + EVENT_ASSOCINFO, + + /** + * EVENT_INTERFACE_STATUS - Report interface status changes + * + * This optional event can be used to report changes in interface + * status (interface added/removed) using union + * wpa_event_data::interface_status. This can be used to trigger + * wpa_supplicant to stop and re-start processing for the interface, + * e.g., when a cardbus card is ejected/inserted. + */ + EVENT_INTERFACE_STATUS, + + /** + * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication + * + * This event can be used to inform wpa_supplicant about candidates for + * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible + * for scan request (ap_scan=2 mode), this event is required for + * pre-authentication. If wpa_supplicant is performing scan request + * (ap_scan=1), this event is optional since scan results can be used + * to add pre-authentication candidates. union + * wpa_event_data::pmkid_candidate is used to report the BSSID of the + * candidate and priority of the candidate, e.g., based on the signal + * strength, in order to try to pre-authenticate first with candidates + * that are most likely targets for re-association. + * + * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates + * on the candidate list. In addition, it can be called for the current + * AP and APs that have existing PMKSA cache entries. wpa_supplicant + * will automatically skip pre-authentication in cases where a valid + * PMKSA exists. When more than one candidate exists, this event should + * be generated once for each candidate. + * + * Driver will be notified about successful pre-authentication with + * struct wpa_driver_ops::add_pmkid() calls. + */ EVENT_PMKID_CANDIDATE } wpa_event_type; + +/** + * union wpa_event_data - Additional data for wpa_supplicant_event() calls + */ union wpa_event_data { - struct { - /* Optional request information data: IEs included in AssocReq - * and AssocResp. If these are not returned by the driver, - * WPA Supplicant will generate the WPA/RSN IE. */ - u8 *req_ies, *resp_ies; - size_t req_ies_len, resp_ies_len; - - /* Optional Beacon/ProbeResp data: IEs included in Beacon or + /** + * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events + * + * This structure is optional for EVENT_ASSOC calls and required for + * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the + * driver interface does not need to generate separate EVENT_ASSOCINFO + * calls. + */ + struct assoc_info { + /** + * req_ies - (Re)Association Request IEs + * + * If the driver generates WPA/RSN IE, this event data must be + * returned for WPA handshake to have needed information. If + * wpa_supplicant-generated WPA/RSN IE is used, this + * information event is optional. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + u8 *req_ies; + + /** + * req_ies_len - Length of req_ies in bytes + */ + size_t req_ies_len; + + /** + * resp_ies - (Re)Association Response IEs + * + * Optional association data from the driver. This data is not + * required WPA, but may be useful for some protocols and as + * such, should be reported if this is available to the driver + * interface. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + u8 *resp_ies; + + /** + * resp_ies_len - Length of resp_ies in bytes + */ + size_t resp_ies_len; + + /** + * beacon_ies - Beacon or Probe Response IEs + * + * Optional Beacon/ProbeResp data: IEs included in Beacon or * Probe Response frames from the current AP (i.e., the one * that the client just associated with). This information is * used to update WPA/RSN IE for the AP. If this field is not @@ -28,30 +185,52 @@ union wpa_event_data { * data for the new AP is found, scan results will be requested * again (without scan request). At this point, the driver is * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is - * used). */ - u8 *beacon_ies; /* beacon or probe resp IEs */ + * used). + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + u8 *beacon_ies; + + /** + * beacon_ies_len - Length of beacon_ies */ size_t beacon_ies_len; } assoc_info; - struct { + + /** + * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE + */ + struct michael_mic_failure { int unicast; } michael_mic_failure; - struct { + + /** + * struct interface_status - Data for EVENT_INTERFACE_STATUS + */ + struct interface_status { char ifname[20]; enum { EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED } ievent; } interface_status; - struct { + + /** + * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE + */ + struct pmkid_candidate { + /** BSSID of the PMKID candidate */ u8 bssid[ETH_ALEN]; - int index; /* smaller the index, higher the priority */ + /** Smaller the index, higher the priority */ + int index; + /** Whether RSN IE includes pre-authenticate flag */ int preauth; } pmkid_candidate; }; /** - * wpa_supplicant_event - report a driver event for wpa_supplicant - * @wpa_s: pointer to wpa_supplicant data; this is the @ctx variable registered - * with wpa_driver_events_init() + * wpa_supplicant_event - Report a driver event for wpa_supplicant + * @wpa_s: pointer to wpa_supplicant data; this is the ctx variable registered + * with struct wpa_driver_ops::init() * @event: event type (defined above) * @data: possible extra data for the event * @@ -62,7 +241,9 @@ void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, union wpa_event_data *data); /** - * wpa_msg - conditional printf for default target and ctrl_iface monitors + * wpa_msg - Conditional printf for default target and ctrl_iface monitors + * @wpa_s: pointer to wpa_supplicant data; this is the ctx variable registered + * with struct wpa_driver_ops::init() * @level: priority level (MSG_*) of the message * @fmt: printf format string, followed by optional arguments * @@ -78,4 +259,16 @@ __attribute__ ((format (printf, 3, 4))); const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len); +/** + * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant + * @ctx: Context pointer (wpa_s) + * @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. + */ +void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + #endif /* WPA_SUPPLICANT_H */ diff --git a/contrib/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa_supplicant/wpa_supplicant_i.h index 508fe09..a22bc6f 100644 --- a/contrib/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa_supplicant/wpa_supplicant_i.h @@ -1,93 +1,177 @@ +/* + * wpa_supplicant - Internal definitions + * Copyright (c) 2003-2006, Jouni Malinen <jkmaline@cc.hut.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 "driver.h" -#ifdef EAPOL_TEST -#include <netinet/in.h> - -struct hostapd_radius_server { - struct in_addr addr; - int port; - u8 *shared_secret; - size_t shared_secret_len; -}; -#endif /* EAPOL_TEST */ - -#define PMKID_LEN 16 -struct rsn_pmksa_cache { - struct rsn_pmksa_cache *next; - u8 pmkid[PMKID_LEN]; - u8 pmk[PMK_LEN]; - size_t pmk_len; - time_t expiration; - int akmp; /* WPA_KEY_MGMT_* */ - u8 aa[ETH_ALEN]; -}; - -struct rsn_pmksa_candidate { - struct rsn_pmksa_candidate *next; +struct wpa_blacklist { + struct wpa_blacklist *next; u8 bssid[ETH_ALEN]; - int priority; + int count; }; -struct wpa_ptk { - u8 mic_key[16]; /* EAPOL-Key MIC Key (MK) */ - u8 encr_key[16]; /* EAPOL-Key Encryption Key (EK) */ - u8 tk1[16]; /* Temporal Key 1 (TK1) */ - union { - u8 tk2[16]; /* Temporal Key 2 (TK2) */ - struct { - u8 tx_mic_key[8]; - u8 rx_mic_key[8]; - } auth; - } u; -} __attribute__ ((packed)); +struct wpa_scan_result; +struct wpa_sm; +struct wpa_supplicant; +/** + * 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; +}; -struct wpa_blacklist { - struct wpa_blacklist *next; - u8 bssid[ETH_ALEN]; - int count; +/** + * struct wpa_params - Parameters for wpa_supplicant_init() + */ +struct wpa_params { + /** + * daemonize - Run %wpa_supplicant in the background + */ + int daemonize; + + /** + * wait_for_interface - Wait for the network interface to appear + * + * If set, %wpa_supplicant will wait until all the configured network + * interfaces are available before starting processing. Please note + * that in many cases, a better alternative would be to start + * %wpa_supplicant without network interfaces and add the interfaces + * dynamically whenever they become available. + */ + int wait_for_interface; + + /** + * 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; }; +/** + * 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; + int ctrl_sock; +}; +/** + * 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_supplicant *head; + struct wpa_global *global; struct wpa_supplicant *next; struct l2_packet_data *l2; unsigned char own_addr[ETH_ALEN]; char ifname[100]; -#ifdef CONFIG_XSUPPLICANT_IFACE - int dot1x_s; /* socket for connection to Xsupplicant */ - int ext_pmk_received; /* 1 = PMK was received from Xsupplicant */ -#endif /* CONFIG_XSUPPLICANT_IFACE */ - - u8 pmk[PMK_LEN]; - size_t pmk_len; - u8 snonce[WPA_NONCE_LEN]; - u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ - struct wpa_ptk ptk, tptk; - int ptk_set, tptk_set; - int renew_snonce; + char *confname; struct wpa_config *conf; - u8 request_counter[WPA_REPLAY_COUNTER_LEN]; int countermeasures; time_t last_michael_mic_error; - u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; - int rx_replay_counter_set; u8 bssid[ETH_ALEN]; int reassociate; /* reassociation requested */ + int disconnected; /* all connections disabled; i.e., do no reassociate + * before this has been cleared */ struct wpa_ssid *current_ssid; - u8 *ap_wpa_ie, *ap_rsn_ie; - size_t ap_wpa_ie_len, ap_rsn_ie_len; - u8 *assoc_wpa_ie; - size_t assoc_wpa_ie_len; /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ - int proto; int pairwise_cipher; int group_cipher; int key_mgmt; @@ -108,34 +192,20 @@ struct wpa_supplicant { struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been * removed */ + struct wpa_sm *wpa; struct eapol_sm *eapol; int ctrl_sock; /* UNIX domain socket for control interface or -1 if * not used */ struct wpa_ctrl_dst *ctrl_dst; - enum { - WPA_DISCONNECTED, WPA_SCANNING, WPA_ASSOCIATING, - WPA_ASSOCIATED, WPA_4WAY_HANDSHAKE, WPA_GROUP_HANDSHAKE, - WPA_COMPLETED - } wpa_state; - - struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ - int pmksa_count; /* number of entries in PMKSA cache */ - struct rsn_pmksa_cache *cur_pmksa; /* current PMKSA entry */ - struct rsn_pmksa_candidate *pmksa_candidates; - - struct l2_packet_data *l2_preauth; - u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or - * 00:00:00:00:00:00 if no pre-auth is - * in progress */ - struct eapol_sm *preauth_eapol; + wpa_states wpa_state; + int new_connection; + int reassociated_connection; int eapol_received; /* number of EAPOL packets received after the * previous association event */ - u8 *imsi; - size_t imsi_len; struct scard_data *scard; unsigned char last_eapol_src[ETH_ALEN]; @@ -144,136 +214,61 @@ struct wpa_supplicant { struct wpa_blacklist *blacklist; -#ifdef EAPOL_TEST - u8 radius_identifier; - struct radius_msg *last_recv_radius; - struct in_addr own_ip_addr; - struct radius_client_data *radius; - - /* RADIUS Authentication and Accounting servers in priority order */ - struct hostapd_radius_server *auth_servers, *auth_server; - int num_auth_servers; - struct hostapd_radius_server *acct_servers, *acct_server; - int num_acct_servers; - - int radius_retry_primary_interval; - int radius_acct_interim_interval; - - 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; -#endif /* EAPOL_TEST */ + int scan_req; /* manual scan request; this forces a scan even if there + * are no enabled networks in the configuration */ }; /* wpa_supplicant.c */ -void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx); - -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_disassociate(struct wpa_supplicant *wpa_s, - int reason_code); -void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, - int reason_code); +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); +const char * wpa_supplicant_state_txt(int state); +int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s, + int wait_for_interface); +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); +int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid, + u8 *wpa_ie, size_t *wpa_ie_len); +void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *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); +int wpa_supplicant_get_scan_results(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_cancel_auth_timeout(struct wpa_supplicant *wpa_s); - -int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); - -int wpa_supplicant_get_beacon_ie(struct wpa_supplicant *wpa_s); - - -/* wpa.c */ -void wpa_supplicant_key_request(struct wpa_supplicant *wpa_s, - int error, int pairwise); - -struct wpa_ie_data { - int proto; - int pairwise_cipher; - int group_cipher; - int key_mgmt; - int capabilities; - int num_pmkid; - u8 *pmkid; -}; - -int wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie, - size_t wpa_ie_len, struct wpa_ie_data *data); - -int wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie); - -void wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr, - unsigned char *buf, size_t len); - +void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_states state); struct wpa_ssid * wpa_supplicant_get_ssid(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_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec); -void pmksa_cache_free(struct wpa_supplicant *wpa_s); -struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_supplicant *wpa_s, - u8 *aa, u8 *pmkid); -int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len); -void pmksa_candidate_free(struct wpa_supplicant *wpa_s); - -int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen); - -struct wpa_scan_result; -#ifdef IEEE8021X_EAPOL -int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst); -void rsn_preauth_deinit(struct wpa_supplicant *wpa_s); -void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s, - struct wpa_scan_result *results, int count); -void pmksa_candidate_add(struct wpa_supplicant *wpa_s, const u8 *bssid, - int prio); -#else /* IEEE8021X_EAPOL */ -static inline int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst) -{ - return -1; -} - -static inline void rsn_preauth_deinit(struct wpa_supplicant *wpa_s) -{ -} -static inline void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s, - struct wpa_scan_result *results, - int count) -{ -} - -static inline void pmksa_candidate_add(struct wpa_supplicant *wpa_s, - const u8 *bssid, - int prio) -{ -} -#endif /* IEEE8021X_EAPOL */ +void wpa_show_license(void); -void wpa_supplicant_notify_eapol_done(void *ctx); +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); -/** - * wpa_eapol_send - send IEEE 802.1X EAPOL packet to the Authenticator - * @ctx: pointer to wpa_supplicant data - * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*) - * @buf: EAPOL payload (after IEEE 802.1X header) - * @len: EAPOL payload length - * - * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame - * to the current Authenticator or in case of pre-authentication, to the peer - * of the authentication. - */ -int wpa_eapol_send(void *ctx, int type, u8 *buf, size_t len); -int wpa_eapol_send_preauth(void *ctx, int type, u8 *buf, size_t len); +int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); /* driver_ops */ @@ -292,6 +287,14 @@ static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s) 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_drop_unencrypted(struct wpa_supplicant *wpa_s, int enabled) { @@ -381,6 +384,7 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, wpa_alg alg, 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->drv_priv, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); @@ -467,4 +471,14 @@ static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s) 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; +} + #endif /* WPA_SUPPLICANT_I_H */ |